From da620a04aa6246eb27ec27070b0c8af48626d252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Sun, 14 May 2023 03:40:21 +0300 Subject: [PATCH] Implement most of application logic --- src/lib.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 9 +++--- src/units.rs | 2 +- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3eb2031..d9738d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,86 @@ mod conversions; mod parse; mod units; -pub use conversions::convert; -pub use parse::{parse, ParseError}; +use conversions::convert; +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())); + } +} diff --git a/src/main.rs b/src/main.rs index 513f0d8..fa2fcf6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ -use metrify::parse; +use metrify::run; fn main() { - let parsed = parse("12 stones 1 pound"); - match parsed { - Ok(parsed) => println!("{parsed:?}"), - Err(err) => eprintln!("{err:?}"), + match run("5′7″") { + Ok(str) => println!("{str}"), + Err(err) => eprintln!("{err}"), } } diff --git a/src/units.rs b/src/units.rs index 6e13eff..6e0b0c6 100644 --- a/src/units.rs +++ b/src/units.rs @@ -23,7 +23,7 @@ pub struct MetricQuantity { pub unit: Metric, } -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct NonMetricQuantity { pub amount: f64, pub unit: NonMetric,