tic_tac_toe/
team.rs

1/// Failed to parse a [`Team`] from a [`char`].
2#[derive(Debug, Clone)]
3pub struct InvalidCharError(pub char);
4
5impl std::fmt::Display for InvalidCharError {
6    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7        write!(f, "{} is not a valid Tic-Tac-Toe team", self.0)
8    }
9}
10
11impl std::error::Error for InvalidCharError {}
12
13/// Failed to parse a [`Team`] from a [`str`].
14#[derive(Debug, Clone)]
15pub enum InvalidStrError {
16    /// The string is the wrong length. It must contain exactly one ascii char.
17    ///
18    /// The length is in bytes.
19    /// For another metric, just calculate it yourself on failure.
20    InvalidLength(usize),
21
22    /// The char is not valid.
23    InvalidChar(InvalidCharError),
24}
25
26impl From<InvalidCharError> for InvalidStrError {
27    fn from(e: InvalidCharError) -> Self {
28        Self::InvalidChar(e)
29    }
30}
31
32impl std::fmt::Display for InvalidStrError {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            Self::InvalidLength(len) => write!(
36                f,
37                "a Tic-Tac-Toe team cannot be made from inputs of length {}",
38                len
39            ),
40            Self::InvalidChar(e) => e.fmt(f),
41        }
42    }
43}
44
45impl std::error::Error for InvalidStrError {
46    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
47        if let Self::InvalidChar(e) = self {
48            Some(e)
49        } else {
50            None
51        }
52    }
53}
54
55/// A Tic Tac Toe Team
56#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
57pub enum Team {
58    X,
59    O,
60}
61
62impl Team {
63    /// Invert the teams
64    #[must_use]
65    pub fn inverse(self) -> Self {
66        match self {
67            Self::X => Self::O,
68            Self::O => Self::X,
69        }
70    }
71
72    /// Try to parse a [`Team`] from a [`char`].
73    pub fn from_char(c: char) -> Result<Self, InvalidCharError> {
74        match c {
75            'x' | 'X' => Ok(Self::X),
76            'o' | 'O' => Ok(Self::O),
77            c => Err(InvalidCharError(c)),
78        }
79    }
80}
81
82impl std::str::FromStr for Team {
83    type Err = InvalidStrError;
84
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        // This may be in bytes but this only works if the first char is ascii.
87        // Therefore, this is fine.
88        if s.len() != 1 {
89            return Err(InvalidStrError::InvalidLength(s.len()));
90        }
91
92        Ok(Self::from_char(s.chars().next().expect("missing char"))?)
93    }
94}