|
|
|
@ -10,7 +10,7 @@ use nom::{
|
|
|
|
|
IResult, Parser,
|
|
|
|
|
};
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use std::{fmt::Write, num::ParseIntError, time::Duration};
|
|
|
|
|
use std::{fmt::Display, num::ParseIntError, time::Duration};
|
|
|
|
|
|
|
|
|
|
impl From<ParseSizeError> for Error {
|
|
|
|
|
fn from(_: ParseSizeError) -> Self {
|
|
|
|
@ -142,9 +142,9 @@ impl From<&GameType> for String {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToString for GameType {
|
|
|
|
|
fn to_string(&self) -> String {
|
|
|
|
|
String::from(self)
|
|
|
|
|
impl Display for GameType {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
write!(f, "{}", String::from(self))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -189,12 +189,15 @@ pub struct Tree {
|
|
|
|
|
pub root: Node,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToString for Tree {
|
|
|
|
|
fn to_string(&self) -> String {
|
|
|
|
|
format!("({})", self.root.to_string())
|
|
|
|
|
impl Display for Tree {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
write!(f, "({})", self.root)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Properties<'a>(&'a Vec<Property>);
|
|
|
|
|
struct Nodes<'a>(&'a Vec<Node>);
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct Node {
|
|
|
|
|
pub properties: Vec<Property>,
|
|
|
|
@ -264,8 +267,12 @@ impl Node {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToString for Node {
|
|
|
|
|
fn to_string(&self) -> String {
|
|
|
|
|
impl Display for Node {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
write!(f, ";{}", Properties(&self.properties))?;
|
|
|
|
|
write!(f, "{}", Nodes(&self.next))?;
|
|
|
|
|
Ok(())
|
|
|
|
|
/*
|
|
|
|
|
let props = self
|
|
|
|
|
.properties
|
|
|
|
|
.iter()
|
|
|
|
@ -281,11 +288,36 @@ impl ToString for Node {
|
|
|
|
|
} else {
|
|
|
|
|
self.next
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|node| format!("({})", node.to_string()))
|
|
|
|
|
.map(|node| write!(f, "({})", node.to_string()))
|
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
|
.join("")
|
|
|
|
|
};
|
|
|
|
|
format!(";{}{}", props, next)
|
|
|
|
|
write!(f, ";{}{}", props, next)
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Properties<'_> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
for property in self.0.iter() {
|
|
|
|
|
write!(f, "{}", property)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Nodes<'_> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
if self.0.len() == 1 {
|
|
|
|
|
for node in self.0.iter() {
|
|
|
|
|
write!(f, "{}", node)?;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for node in self.0.iter() {
|
|
|
|
|
write!(f, "({})", node)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -460,95 +492,96 @@ pub struct UnknownProperty {
|
|
|
|
|
pub value: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToString for Property {
|
|
|
|
|
fn to_string(&self) -> String {
|
|
|
|
|
impl Display for Property {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Property::Move((color, Move::Move(mv))) => {
|
|
|
|
|
format!("{}[{}]", color.abbreviation(), mv)
|
|
|
|
|
write!(f, "{}[{}]", color.abbreviation(), mv)
|
|
|
|
|
}
|
|
|
|
|
Property::Move((color, Move::Pass)) => {
|
|
|
|
|
format!("{}[]", color.abbreviation())
|
|
|
|
|
write!(f, "{}[]", color.abbreviation())
|
|
|
|
|
}
|
|
|
|
|
Property::TimeLeft((color, time)) => {
|
|
|
|
|
format!("{}[{}]", color.abbreviation(), time.as_secs())
|
|
|
|
|
write!(f, "{}[{}]", color.abbreviation(), time.as_secs())
|
|
|
|
|
}
|
|
|
|
|
Property::Comment(value) => format!("C[{}]", value),
|
|
|
|
|
Property::Annotation(Annotation::BadMove) => "BM[]".to_owned(),
|
|
|
|
|
Property::Annotation(Annotation::DoubtfulMove) => "DO[]".to_owned(),
|
|
|
|
|
Property::Annotation(Annotation::InterestingMove) => "IT[]".to_owned(),
|
|
|
|
|
Property::Annotation(Annotation::Tesuji) => "TE[]".to_owned(),
|
|
|
|
|
Property::Application(app) => format!("AP[{}]", app),
|
|
|
|
|
Property::Charset(set) => format!("CA[{}]", set),
|
|
|
|
|
Property::FileFormat(ff) => format!("FF[{}]", ff),
|
|
|
|
|
Property::GameType(gt) => format!("GM[{}]", gt.to_string()),
|
|
|
|
|
Property::Comment(value) => write!(f, "C[{}]", value),
|
|
|
|
|
Property::Annotation(Annotation::BadMove) => write!(f, "BM[]"),
|
|
|
|
|
Property::Annotation(Annotation::DoubtfulMove) => write!(f, "DO[]"),
|
|
|
|
|
Property::Annotation(Annotation::InterestingMove) => write!(f, "IT[]"),
|
|
|
|
|
Property::Annotation(Annotation::Tesuji) => write!(f, "TE[]"),
|
|
|
|
|
Property::Application(app) => write!(f, "AP[{}]", app),
|
|
|
|
|
Property::Charset(set) => write!(f, "CA[{}]", set),
|
|
|
|
|
Property::FileFormat(ff) => write!(f, "FF[{}]", ff),
|
|
|
|
|
Property::GameType(gt) => write!(f, "GM[{}]", gt),
|
|
|
|
|
Property::VariationDisplay => unimplemented!(),
|
|
|
|
|
Property::BoardSize(Size { width, height }) => {
|
|
|
|
|
if width == height {
|
|
|
|
|
format!("SZ[{}]", width)
|
|
|
|
|
write!(f, "SZ[{}]", width)
|
|
|
|
|
} else {
|
|
|
|
|
format!("SZ[{}:{}]", width, height)
|
|
|
|
|
write!(f, "SZ[{}:{}]", width, height)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Property::SetupBlackStones(positions) => {
|
|
|
|
|
format!("AB[{}]", positions.compressed_list(),)
|
|
|
|
|
write!(f, "AB[{}]", positions.compressed_list(),)
|
|
|
|
|
}
|
|
|
|
|
Property::ClearStones(positions) => {
|
|
|
|
|
format!("AE[{}]", positions.compressed_list(),)
|
|
|
|
|
write!(f, "AE[{}]", positions.compressed_list(),)
|
|
|
|
|
}
|
|
|
|
|
Property::SetupWhiteStones(positions) => {
|
|
|
|
|
format!("AW[{}]", positions.compressed_list(),)
|
|
|
|
|
write!(f, "AW[{}]", positions.compressed_list(),)
|
|
|
|
|
}
|
|
|
|
|
Property::NextPlayer(color) => format!("PL[{}]", color.abbreviation()),
|
|
|
|
|
Property::Evaluation(Evaluation::Even) => "DM[]".to_owned(),
|
|
|
|
|
Property::Evaluation(Evaluation::GoodForBlack) => "GB[]".to_owned(),
|
|
|
|
|
Property::Evaluation(Evaluation::GoodForWhite) => "GW[]".to_owned(),
|
|
|
|
|
Property::Evaluation(Evaluation::Unclear) => "UC[]".to_owned(),
|
|
|
|
|
Property::Hotspot => "HO[]".to_owned(),
|
|
|
|
|
Property::Value(value) => format!("V[{}]", value),
|
|
|
|
|
Property::Annotator(value) => format!("AN[{}]", value),
|
|
|
|
|
Property::BlackRank(value) => format!("BR[{}]", value),
|
|
|
|
|
Property::BlackTeam(value) => format!("BT[{}]", value),
|
|
|
|
|
Property::Copyright(value) => format!("CP[{}]", value),
|
|
|
|
|
Property::NextPlayer(color) => write!(f, "PL[{}]", color.abbreviation()),
|
|
|
|
|
Property::Evaluation(Evaluation::Even) => write!(f, "DM[]"),
|
|
|
|
|
Property::Evaluation(Evaluation::GoodForBlack) => write!(f, "GB[]"),
|
|
|
|
|
Property::Evaluation(Evaluation::GoodForWhite) => write!(f, "GW[]"),
|
|
|
|
|
Property::Evaluation(Evaluation::Unclear) => write!(f, "UC[]"),
|
|
|
|
|
Property::Hotspot => write!(f, "HO[]"),
|
|
|
|
|
Property::Value(value) => write!(f, "V[{}]", value),
|
|
|
|
|
Property::Annotator(value) => write!(f, "AN[{}]", value),
|
|
|
|
|
Property::BlackRank(value) => write!(f, "BR[{}]", value),
|
|
|
|
|
Property::BlackTeam(value) => write!(f, "BT[{}]", value),
|
|
|
|
|
Property::Copyright(value) => write!(f, "CP[{}]", value),
|
|
|
|
|
Property::EventDates(_) => unimplemented!(),
|
|
|
|
|
Property::EventName(value) => format!("EV[{}]", value),
|
|
|
|
|
Property::GameName(value) => format!("GN[{}]", value),
|
|
|
|
|
Property::ExtraGameInformation(value) => format!("GC[{}]", value),
|
|
|
|
|
Property::GameOpening(value) => format!("ON[{}]", value),
|
|
|
|
|
Property::Overtime(value) => format!("OT[{}]", value),
|
|
|
|
|
Property::BlackPlayer(value) => format!("PB[{}]", value),
|
|
|
|
|
Property::GameLocation(value) => format!("PC[{}]", value),
|
|
|
|
|
Property::WhitePlayer(value) => format!("PW[{}]", value),
|
|
|
|
|
Property::EventName(value) => write!(f, "EV[{}]", value),
|
|
|
|
|
Property::GameName(value) => write!(f, "GN[{}]", value),
|
|
|
|
|
Property::ExtraGameInformation(value) => write!(f, "GC[{}]", value),
|
|
|
|
|
Property::GameOpening(value) => write!(f, "ON[{}]", value),
|
|
|
|
|
Property::Overtime(value) => write!(f, "OT[{}]", value),
|
|
|
|
|
Property::BlackPlayer(value) => write!(f, "PB[{}]", value),
|
|
|
|
|
Property::GameLocation(value) => write!(f, "PC[{}]", value),
|
|
|
|
|
Property::WhitePlayer(value) => write!(f, "PW[{}]", value),
|
|
|
|
|
Property::Result(_) => unimplemented!(),
|
|
|
|
|
Property::Round(value) => format!("RO[{}]", value),
|
|
|
|
|
Property::Ruleset(value) => format!("RU[{}]", value),
|
|
|
|
|
Property::Source(value) => format!("SO[{}]", value),
|
|
|
|
|
Property::TimeLimit(value) => format!("TM[{}]", value.as_secs()),
|
|
|
|
|
Property::User(value) => format!("US[{}]", value),
|
|
|
|
|
Property::WhiteRank(value) => format!("WR[{}]", value),
|
|
|
|
|
Property::WhiteTeam(value) => format!("WT[{}]", value),
|
|
|
|
|
Property::Round(value) => write!(f, "RO[{}]", value),
|
|
|
|
|
Property::Ruleset(value) => write!(f, "RU[{}]", value),
|
|
|
|
|
Property::Source(value) => write!(f, "SO[{}]", value),
|
|
|
|
|
Property::TimeLimit(value) => write!(f, "TM[{}]", value.as_secs()),
|
|
|
|
|
Property::User(value) => write!(f, "US[{}]", value),
|
|
|
|
|
Property::WhiteRank(value) => write!(f, "WR[{}]", value),
|
|
|
|
|
Property::WhiteTeam(value) => write!(f, "WT[{}]", value),
|
|
|
|
|
Property::Territory(Color::White, positions) => {
|
|
|
|
|
positions
|
|
|
|
|
.iter()
|
|
|
|
|
.fold("TW".to_owned(), |mut output, Position(p)| {
|
|
|
|
|
let _ = write!(output, "{}", p);
|
|
|
|
|
output
|
|
|
|
|
})
|
|
|
|
|
write!(f, "TW[{}]", Positions(positions))
|
|
|
|
|
}
|
|
|
|
|
Property::Territory(Color::Black, positions) => {
|
|
|
|
|
positions
|
|
|
|
|
.iter()
|
|
|
|
|
.fold("TB".to_owned(), |mut output, Position(p)| {
|
|
|
|
|
let _ = write!(output, "{}", p);
|
|
|
|
|
output
|
|
|
|
|
})
|
|
|
|
|
write!(f, "TB[{}]", Positions(positions))
|
|
|
|
|
}
|
|
|
|
|
Property::Unknown(UnknownProperty { ident, value }) => {
|
|
|
|
|
format!("{}[{}]", ident, value)
|
|
|
|
|
write!(f, "{}[{}]", ident, value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Positions<'a>(&'a Vec<Position>);
|
|
|
|
|
|
|
|
|
|
impl Display for Positions<'_> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
for Position(p) in self.0.iter() {
|
|
|
|
|
write!(f, "{}", p)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn parse_collection<'a, E: nom::error::ParseError<&'a str>>(
|
|
|
|
|
input: &'a str,
|
|
|
|
|
) -> IResult<&'a str, Vec<Tree>, E> {
|
|
|
|
|