1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use std::fmt::Debug;

use crate::{go, unknown_game, SgfNode, SgfParseError};

/// The game recorded in a [`GameTree`].
///
/// Any [`GameTree`] retured by [`parse`](`crate::parse`) will have a game type which corresponds to
/// the SGF `GM` property of the root node.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GameType {
    Go,
    Unknown,
}

/// An SGF [GameTree](https://www.red-bean.com/sgf/sgf4.html#ebnf-def) value.
///
/// This type allows creating a collection of [`SgfNode`] values for different games. This is
/// used in the return type of the [`parse`](`crate::parse()`) function. Users of the
/// [`serialize`](`crate::serialize()`) function will need to build these.
///
/// For now, all non-Go games will parse as [`GameTree::Unknown`] which should also be used for any
/// serialization of non-Go games.
#[derive(Clone, Debug, PartialEq)]
pub enum GameTree {
    GoGame(SgfNode<go::Prop>),
    Unknown(SgfNode<unknown_game::Prop>),
}

impl GameTree {
    /// Consumes a Go game `GameTree` and returns the contained [`SgfNode`].
    ///
    /// This is a convenience method for go games.
    ///
    /// # Errors
    /// Returns an error if the variant isn't a [`GameTree::GoGame`].
    ///
    /// # Examples
    /// ```
    /// use sgf_parse::parse;
    ///
    /// let gametree = parse("(;B[de]C[A comment])").unwrap().into_iter().next().unwrap();
    /// let sgf_node = gametree.into_go_node().unwrap();
    /// ```
    pub fn into_go_node(self) -> Result<SgfNode<go::Prop>, SgfParseError> {
        match self {
            Self::GoGame(sgf_node) => Ok(sgf_node),
            _ => Err(SgfParseError::UnexpectedGameType),
        }
    }

    /// Returns the [`GameType`] for this [`GameTree`].
    ///
    /// # Examples
    /// ```
    /// use sgf_parse::{parse, GameType};
    ///
    /// let gametree = parse("(;GM[1]B[de]C[A comment])").unwrap().into_iter().next().unwrap();
    /// assert_eq!(gametree.gametype(), GameType::Go);
    /// ```
    pub fn gametype(&self) -> GameType {
        match self {
            Self::GoGame(_) => GameType::Go,
            Self::Unknown(_) => GameType::Unknown,
        }
    }
}

impl std::fmt::Display for GameTree {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let node_text = match self {
            Self::GoGame(sgf_node) => sgf_node.serialize(),
            Self::Unknown(sgf_node) => sgf_node.serialize(),
        };
        std::fmt::Display::fmt(&node_text, f)
    }
}

impl std::convert::From<SgfNode<go::Prop>> for GameTree {
    fn from(sgf_node: SgfNode<go::Prop>) -> Self {
        Self::GoGame(sgf_node)
    }
}

impl std::convert::From<SgfNode<unknown_game::Prop>> for GameTree {
    fn from(sgf_node: SgfNode<unknown_game::Prop>) -> Self {
        Self::Unknown(sgf_node)
    }
}