mod conversions; mod format; mod parse; mod units; use conversions::convert; use format::format; use parse::{parse, ParseError}; use units::{MetricQuantity, NonMetric}; pub fn run(input: &str) -> Result { let non_metric = match parse(input) { Ok(non_metric) => non_metric, Err(ParseError::NotValidNumber(string)) => { return Err(format!("Not a valid number: {string}")); } Err(ParseError::UnexpectedUnit(unit)) => { return Err(format!("Unexpected unit: {unit}")); } Err(ParseError::UnknownUnit(unit)) => { return Err(format!("Unknown unit: {unit}")); } Err(ParseError::ExpectedUnit) => { return Err("Expected a unit".to_string()); } }; if non_metric.len() == 0 { return Err("Expected quantity or quantities to convert".to_string()); } let metric: Vec = non_metric.clone().into_iter().map(convert).collect(); // Make sure the results of the conversions can be summed together // This is the case if the units after conversion are the same let mut metric_unit = None; for index in 0..metric.len() { match metric_unit { Some(metric_unit) => { if metric[index].unit != metric_unit { let first_unit_name = unit_to_name(non_metric[0].unit); let unit_name = unit_to_name(non_metric[index].unit); return Err(format!("Incompatible units: {first_unit_name}, {unit_name}")); } } None => { metric_unit = Some(metric[index].unit); } } } let amount = metric.into_iter().map(|quantity| { quantity.amount }).sum(); let quantity = MetricQuantity { amount: amount, unit: metric_unit.expect("we must have at least one quantity by this point"), }; Ok(format(quantity)) } fn unit_to_name(unit: NonMetric) -> &'static str { match unit { // Length NonMetric::Inch => "inches", NonMetric::Foot => "feet", NonMetric::Yard => "yards", NonMetric::Mile => "miles", // Weight NonMetric::Ounce => "ounces", NonMetric::Pound => "pounds", NonMetric::Stone => "stones", } } #[cfg(test)] mod test { use super::*; #[test] fn errors() { assert_eq!(run("1.0."), Err("Not a valid number: 1.0.".to_string())); assert_eq!(run("ft"), Err("Unexpected unit: ft".to_string())); assert_eq!(run("1 tf"), Err("Unknown unit: tf".to_string())); assert_eq!(run("1"), Err("Expected a unit".to_string())); assert_eq!(run(""), Err("Expected quantity or quantities to convert".to_string())); assert_eq!(run("6 ft 1 lbs"), Err("Incompatible units: feet, pounds".to_string())); } }