use std::{ fmt::{self, Display}, fs::read_to_string, }; pub fn run(arg: &str) -> String { let content = read_to_string(arg).unwrap(); let result = interpret(&content.to_string()); result.to_string() } #[derive(Debug, PartialEq)] pub enum Value { Num(i32), Bool(bool), Sym(String), } impl Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Value::Num(i) => write!(f, "{}", i), Value::Bool(b) => write!(f, "{}", b), Value::Sym(s) => write!(f, "{}", s), } } } fn interpret(arg: &str) -> Value { let token = arg.trim(); token .parse::() .map(Value::Num) .or(token.parse::().map(Value::Bool)) .unwrap_or(Value::Sym(token.to_string())) } #[cfg(test)] mod tests { use super::interpret; use super::Value::*; use proptest::prelude::*; proptest! { #[test] fn interpret_integer_as_number(i in -1000i32..1000) { let result = interpret(&i.to_string()); assert_eq!(Num(i), result); } } #[test] fn interpret_truth_values_as_booleans() { assert_eq!(Bool(true), interpret("true")); assert_eq!(Bool(false), interpret("false")); } #[test] fn interpret_identifiers_values_as_symbols() { assert_eq!(Sym("foo".to_string()), interpret("foo")); } #[test] fn ignores_whitespace() { assert_eq!(Sym("foo".to_string()), interpret(" foo \n\r")); assert_eq!(Num(-42), interpret("\n-42")); } }