1#[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#[derive(Debug, Clone)]
15pub enum InvalidStrError {
16 InvalidLength(usize),
21
22 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#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
57pub enum Team {
58 X,
59 O,
60}
61
62impl Team {
63 #[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 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 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}