diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 1891a5f..02881d9 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -5,27 +5,35 @@ use crate::{ use std::{collections::HashSet, time::Duration}; use uuid::Uuid; +#[derive(Clone, Debug, PartialEq)] +pub enum GameError { + InvalidGame, + RequiredPropertiesMissing, + InvalidGameNode(GameNodeError), +} + #[derive(Clone, Debug, PartialEq)] pub enum MoveNodeError { - IncompatibleProperty, + IncompatibleProperty(parser::Property), ConflictingProperty, + NotAMoveNode, + ChildError(Box), } #[derive(Clone, Debug, PartialEq)] pub enum SetupNodeError { - IncompatibleProperty, + IncompatibleProperty(parser::Property), + ConflictingProperty, ConflictingPosition, -} - -#[derive(Clone, Debug, PartialEq)] -pub enum GameError { - InvalidGame, - RequiredPropertiesMissing, + NotASetupNode, + ChildError(Box), } #[derive(Clone, Debug, PartialEq)] pub enum GameNodeError { - UnsupportedGameNode, + UnsupportedGameNode(MoveNodeError, SetupNodeError), + ConflictingProperty, + ConflictingPosition, } #[derive(Clone, Debug, PartialEq, Default)] @@ -188,6 +196,14 @@ impl TryFrom<&parser::Tree> for Game { } } + s.children = tree + .root + .next + .iter() + .map(|node| GameNode::try_from(node)) + .collect::, GameNodeError>>() + .map_err(GameError::InvalidGameNode)?; + Ok(s) } } @@ -252,10 +268,16 @@ impl Node for GameNode { impl TryFrom<&parser::Node> for GameNode { type Error = GameNodeError; fn try_from(n: &parser::Node) -> Result { - MoveNode::try_from(n) - .map(GameNode::MoveNode) - .or_else(|_| SetupNode::try_from(n).map(GameNode::SetupNode)) - .map_err(|_| Self::Error::UnsupportedGameNode) + let move_node = MoveNode::try_from(n); + let setup_node = SetupNode::try_from(n); + + match (move_node, setup_node) { + (Ok(node), _) => Ok(Self::MoveNode(node)), + (Err(_), Ok(node)) => Ok(Self::SetupNode(node)), + (Err(move_err), Err(setup_err)) => { + Err(Self::Error::UnsupportedGameNode(move_err, setup_err)) + } + } } } @@ -311,7 +333,7 @@ impl TryFrom<&parser::Node> for MoveNode { type Error = MoveNodeError; fn try_from(n: &parser::Node) -> Result { - match n.mv() { + let mut s = match n.mv() { Some((color, mv)) => { let mut s = Self::new(color, mv); @@ -352,14 +374,24 @@ impl TryFrom<&parser::Node> for MoveNode { parser::Property::Unknown(UnknownProperty { ident, value }) => { s.unknown_props.push((ident.clone(), value.clone())); } - _ => return Err(Self::Error::IncompatibleProperty), + _ => return Err(Self::Error::IncompatibleProperty(prop.clone())), } } Ok(s) } - None => Err(MoveNodeError::IncompatibleProperty), - } + None => Err(Self::Error::NotAMoveNode), + }?; + + s.children = n + .next + .iter() + .map(|node| { + GameNode::try_from(node).map_err(|err| Self::Error::ChildError(Box::new(err))) + }) + .collect::, MoveNodeError>>()?; + + Ok(s) } } @@ -409,7 +441,7 @@ impl TryFrom<&parser::Node> for SetupNode { fn try_from(n: &parser::Node) -> Result { match n.setup() { Some(elements) => Self::new(elements), - None => Err(Self::Error::IncompatibleProperty), + None => Err(Self::Error::NotASetupNode), } } } @@ -548,9 +580,9 @@ mod move_node_tests { ], next: vec![], }; - assert_eq!( + assert_matches!( MoveNode::try_from(&n), - Err(MoveNodeError::IncompatibleProperty) + Err(MoveNodeError::IncompatibleProperty(_)) ); } } @@ -603,9 +635,9 @@ mod path_test { #[cfg(test)] mod file_test { - use crate::Win; - use super::*; + use crate::Win; + use cool_asserts::assert_matches; use parser::parse_collection; use std::{fs::File, io::Read}; @@ -693,27 +725,26 @@ mod file_test { for i in 0..16 { assert_eq!(node.properties[i], expected_properties[i]); } + */ - let node = node.next().unwrap(); - let expected_properties = vec![ - Property { - ident: "B".to_owned(), - values: vec!["pp".to_owned()], - }, - Property { - ident: "BL".to_owned(), - values: vec!["1795.449".to_owned()], - }, - Property { - ident: "C".to_owned(), - values: vec!["Geckoz [?]: Good game\nsavanni [23k?]: There we go! This UI is... tough.\nsavanni [23k?]: Have fun! Talk to you at the end.\nGeckoz [?]: Yeah, OGS is much better; I'm a UX professional\n".to_owned()], - } - ]; - - for i in 0..3 { - assert_eq!(node.properties[i], expected_properties[i]); - } + let children = game.children(); + let node = children.first().unwrap(); + assert_matches!(node, GameNode::MoveNode(node) => { + assert_eq!(node.color, Color::Black); + assert_eq!(node.mv, Move::Move("pp".to_owned())); + assert_eq!(node.time_left, Some(Duration::from_secs(1795))); + assert_eq!(node.comments, Some("Geckoz [?]: Good game\nsavanni [23k?]: There we go! This UI is... tough.\nsavanni [23k?]: Have fun! Talk to you at the end.\nGeckoz [?]: Yeah, OGS is much better; I'm a UX professional\n".to_owned()) + )}); + let children = node.children(); + let node = children.first().unwrap(); + assert_matches!(node, GameNode::MoveNode(node) => { + assert_eq!(node.color, Color::White); + assert_eq!(node.mv, Move::Move("dp".to_owned())); + assert_eq!(node.time_left, Some(Duration::from_secs(1765))); + assert_eq!(node.comments, None); + }); + /* let node = node.next().unwrap(); let expected_properties = vec![ Property { diff --git a/sgf/src/parser.rs b/sgf/src/parser.rs index 1005517..eeaca47 100644 --- a/sgf/src/parser.rs +++ b/sgf/src/parser.rs @@ -300,7 +300,7 @@ impl Node { .collect::>(), ); } - _ => unimplemented!(), + _ => return None, } } if setup.len() > 0 {