Implement most of application logic

This commit is contained in:
Juhani Krekelä 2023-05-14 03:40:21 +03:00
parent 7d82e46fd7
commit da620a04aa
3 changed files with 88 additions and 8 deletions

View File

@ -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<String, String> {
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<MetricQuantity> = 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()));
}
}

View File

@ -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("57″") {
Ok(str) => println!("{str}"),
Err(err) => eprintln!("{err}"),
}
}

View File

@ -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,