#[derive(Debug, Clone)]
pub struct InvalidCharError(pub char);
impl std::fmt::Display for InvalidCharError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} is not a valid Tic-Tac-Toe team", self.0)
}
}
impl std::error::Error for InvalidCharError {}
#[derive(Debug, Clone)]
pub enum InvalidStrError {
InvalidLength(usize),
InvalidChar(InvalidCharError),
}
impl From<InvalidCharError> for InvalidStrError {
fn from(e: InvalidCharError) -> Self {
Self::InvalidChar(e)
}
}
impl std::fmt::Display for InvalidStrError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidLength(len) => write!(
f,
"a Tic-Tac-Toe team cannot be made from inputs of length {}",
len
),
Self::InvalidChar(e) => e.fmt(f),
}
}
}
impl std::error::Error for InvalidStrError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Self::InvalidChar(e) = self {
Some(e)
} else {
None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Team {
X,
O,
}
impl Team {
#[must_use]
pub fn inverse(self) -> Self {
match self {
Self::X => Self::O,
Self::O => Self::X,
}
}
pub fn from_char(c: char) -> Result<Self, InvalidCharError> {
match c {
'x' | 'X' => Ok(Self::X),
'o' | 'O' => Ok(Self::O),
c => Err(InvalidCharError(c)),
}
}
}
impl std::str::FromStr for Team {
type Err = InvalidStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 1 {
return Err(InvalidStrError::InvalidLength(s.len()));
}
Ok(Self::from_char(s.chars().next().expect("missing char"))?)
}
}