use enum_map::Enum; use std::fmt; /// A single digit that can be wrapped in parentheses. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct Digit { pub value: i8, pub has_left_parenthesis: bool, pub has_right_parenthesis: bool, } impl Digit { pub fn new(value: i8) -> Self { Self { value, has_left_parenthesis: false, has_right_parenthesis: false, } } } impl fmt::Display for Digit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.has_left_parenthesis { write!(f, "(")?; } write!(f, "{}", self.value)?; if self.has_right_parenthesis { write!(f, ")")?; } Ok(()) } } impl TryFrom<&str> for Digit { type Error = (); fn try_from(value: &str) -> Result { let mut res = Digit { value: 0, has_left_parenthesis: false, has_right_parenthesis: false, }; let mut it = value.chars(); let c = it.next().ok_or(())?; if c == '(' { res.has_left_parenthesis = true; res.value = it.next().ok_or(())?.to_digit(10).ok_or(())? as i8; } else { res.value = c.to_digit(10).ok_or(())? as i8; } if let Some(c) = it.next() { if c != ')' { return Err(()); } res.has_right_parenthesis = true; } Ok(res) } } /// An operator that can be applied between two terms. #[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)] pub enum Operator { Add, Subtract, Multiply, Divide, } impl Operator { pub fn precedence(&self) -> u8 { match self { Operator::Add | Operator::Subtract => 1, Operator::Multiply | Operator::Divide => 2, } } } impl fmt::Display for Operator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Operator::Add => write!(f, "+"), Operator::Subtract => write!(f, "-"), Operator::Multiply => write!(f, "*"), Operator::Divide => write!(f, "/"), } } } impl TryFrom for Operator { type Error = (); fn try_from(value: char) -> Result { match value { '+' => Ok(Operator::Add), '-' => Ok(Operator::Subtract), '*' => Ok(Operator::Multiply), '/' => Ok(Operator::Divide), _ => Err(()), } } } /// A single piece of a mathematical expression. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Tile { Digit(Digit), Operator(Operator), Equals, } impl TryFrom<&str> for Tile { type Error = (); fn try_from(value: &str) -> Result { if let Ok(digit) = Digit::try_from(value) { return Ok(Tile::Digit(digit)); } match value { "=" => Ok(Tile::Equals), _ => Ok(Tile::Operator(value.chars().next().ok_or(())?.try_into()?)), } } } impl fmt::Display for Tile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Tile::Digit(digit) => write!(f, "{digit}"), Tile::Operator(operator) => write!(f, "{operator}"), Tile::Equals => write!(f, "="), } } } #[cfg(test)] mod tests { use super::*; #[test] fn digit_from_str() { assert_eq!(Digit::try_from("1"), Ok(Digit::new(1))); assert_eq!( Digit::try_from("(5"), Ok(Digit { value: 5, has_left_parenthesis: true, has_right_parenthesis: false, }) ); assert_eq!( Digit::try_from("8)"), Ok(Digit { value: 8, has_left_parenthesis: false, has_right_parenthesis: true, }) ); assert_eq!(Digit::try_from("+"), Err(())); assert_eq!(Digit::try_from("1("), Err(())); assert_eq!(Digit::try_from(""), Err(())); } #[test] fn operator_from_str() { assert_eq!(Operator::try_from('+'), Ok(Operator::Add)); assert_eq!(Operator::try_from('-'), Ok(Operator::Subtract)); assert_eq!(Operator::try_from('²'), Err(())); } #[test] fn piece_from_str() { assert_eq!(Tile::try_from("+"), Ok(Tile::Operator(Operator::Add))); assert_eq!( Tile::try_from("(7)"), Ok(Tile::Digit(Digit { value: 7, has_left_parenthesis: true, has_right_parenthesis: true, })) ); assert_eq!(Tile::try_from("="), Ok(Tile::Equals)); assert_eq!(Tile::try_from(""), Err(())); } }