From 81082fd11d6b7e503839f187633c193cc86571ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Sun, 28 May 2023 20:32:31 +0300 Subject: [PATCH] Add support for degrees Fahrenheit --- src/conversions.rs | 34 +++++++++++++++++++- src/format.rs | 79 ++++++++++++++++++++++++++++------------------ src/lib.rs | 6 ++++ src/parse.rs | 8 +++++ src/units.rs | 3 ++ 5 files changed, 99 insertions(+), 31 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 0e89b4b..c6c1006 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -2,12 +2,13 @@ use crate::units::{Metric, MetricQuantity, NonMetric, NonMetricQuantity}; pub fn convert(from: NonMetricQuantity) -> MetricQuantity { let conversion = get_conversion(from.unit); - let amount = from.amount * conversion.to.amount / conversion.from; + let amount = (from.amount - conversion.offset) * conversion.to.amount / conversion.from; let unit = conversion.to.unit; MetricQuantity { amount, unit } } struct Conversion { + offset: f64, from: f64, to: MetricQuantity, } @@ -22,34 +23,47 @@ fn get_conversion(unit: NonMetric) -> Conversion { match unit { // Length NonMetric::Inch => Conversion { + offset: 0.0, from: inch_from, to: MetricQuantity { amount: inch_to, unit: Metric::Metre }, }, NonMetric::Foot => Conversion { + offset: 0.0, from: inch_from, to: MetricQuantity { amount: 12.0 * inch_to, unit: Metric::Metre }, }, NonMetric::Yard => Conversion { + offset: 0.0, from: inch_from, to: MetricQuantity { amount: 3.0 * 12.0 * inch_to, unit: Metric::Metre }, }, NonMetric::Mile => Conversion { + offset: 0.0, from: inch_from, to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * inch_to, unit: Metric::Metre }, }, // Weight NonMetric::Ounce => Conversion { + offset: 0.0, from: 16.0 * pound_from, to: MetricQuantity { amount: pound_to, unit: Metric::Gram }, }, NonMetric::Pound => Conversion { + offset: 0.0, from: pound_from, to: MetricQuantity { amount: pound_to, unit: Metric::Gram }, }, NonMetric::Stone => Conversion { + offset: 0.0, from: pound_from, to: MetricQuantity { amount: 14.0 * pound_to, unit: Metric::Gram }, }, + // Temperature + NonMetric::Fahrenheit => Conversion { + offset: 32.0, + from: 9.0, + to: MetricQuantity { amount: 5.0, unit: Metric::Celcius }, + }, } } @@ -80,6 +94,24 @@ mod test { run_tests(&tests, Metric::Gram); } + #[test] + fn temperature() { + assert_eq!(convert(NonMetricQuantity { + amount: -40.0, + unit: NonMetric::Fahrenheit, + }), MetricQuantity { + amount: -40.0, + unit: Metric::Celcius, + }); + assert_eq!(convert(NonMetricQuantity { + amount: 32.0, + unit: NonMetric::Fahrenheit, + }), MetricQuantity { + amount: 0.0, + unit: Metric::Celcius, + }); + } + fn run_tests(tests: &[Test], unit: Metric) { for test in tests { let from = NonMetricQuantity { diff --git a/src/format.rs b/src/format.rs index e571f77..25e3def 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,7 +1,7 @@ use crate::units::{Metric, MetricQuantity}; pub fn format(quantity: MetricQuantity) -> String { - let SiUnit(multiplier, unit) = si_unit(quantity); + let PrefixedUnit(multiplier, unit) = prefixed_unit(quantity); let amount = quantity.amount / multiplier; let amount = format_number(amount); format!("{amount} {unit}") @@ -59,30 +59,31 @@ fn format_number(number: f64) -> String { } #[derive(Debug, PartialEq)] -struct SiUnit(f64, &'static str); +struct PrefixedUnit(f64, &'static str); -fn si_unit(quantity: MetricQuantity) -> SiUnit { +fn prefixed_unit(quantity: MetricQuantity) -> PrefixedUnit { let absolute = quantity.amount.abs(); match quantity.unit { Metric::Metre => { if absolute >= 1000.0 { - return SiUnit(1000.0, "km"); + return PrefixedUnit(1000.0, "km"); } else if absolute >= 1.0 { - return SiUnit(1.0, "m"); + return PrefixedUnit(1.0, "m"); } else if absolute >= 0.01 { - return SiUnit(0.01, "cm"); + return PrefixedUnit(0.01, "cm"); } else { - return SiUnit(0.001, "mm"); + return PrefixedUnit(0.001, "mm"); } } Metric::Gram => { if absolute >= 1000.0 { - return SiUnit(1000.0, "kg"); + return PrefixedUnit(1000.0, "kg"); } else { - return SiUnit(1.0, "g"); + return PrefixedUnit(1.0, "g"); } } + Metric::Celcius => PrefixedUnit(1.0, "°C"), } } @@ -117,6 +118,11 @@ mod test { amount: 3_200.0, unit: Metric::Gram, })); + + assert_eq!("1 000 °C", &format(MetricQuantity { + amount: 1_000.0, + unit: Metric::Celcius, + })); } #[test] @@ -149,92 +155,105 @@ mod test { #[test] fn units() { - assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity { amount: 0.0001, unit: Metric::Metre, })); - assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity { amount: 0.001, unit: Metric::Metre, })); - assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity { amount: 0.01, unit: Metric::Metre, })); - assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity { amount: 0.1, unit: Metric::Metre, })); - assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity { amount: 1.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity { amount: 10.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity { amount: 100.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity { amount: 1000.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity { amount: 10_000.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity { amount: -0.001, unit: Metric::Metre, })); - assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity { amount: -0.01, unit: Metric::Metre, })); - assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity { amount: -1.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity { amount: -1000.0, unit: Metric::Metre, })); - assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity { amount: 0.1, unit: Metric::Gram, })); - assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity { amount: 1.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity { amount: 10.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity { amount: 100.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity { amount: 1000.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity { amount: 10_1000.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity { amount: -1.0, unit: Metric::Gram, })); - assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { + assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity { amount: -1000.0, unit: Metric::Gram, })); + + assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity { + amount: 0.0001, + unit: Metric::Celcius, + })); + assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity { + amount: -1.0, + unit: Metric::Celcius, + })); + assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity { + amount: 1_000.0, + unit: Metric::Celcius, + })); } } diff --git a/src/lib.rs b/src/lib.rs index 6e2d58c..672c4cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,8 @@ fn unit_to_name(unit: NonMetric) -> &'static str { NonMetric::Ounce => "ounces", NonMetric::Pound => "pounds", NonMetric::Stone => "stones", + // Temperature + NonMetric::Fahrenheit => "degrees Fahrenheit", } } @@ -98,5 +100,9 @@ mod test { assert_eq!(run("1 oz"), Ok("28.35 g".to_string())); assert_eq!(run("1 lb"), Ok("453.6 g".to_string())); assert_eq!(run("1 st"), Ok("6.35 kg".to_string())); + // Temperature + assert_eq!(run("-40 °F"), Ok("-40 °C".to_string())); + assert_eq!(run("0 °F"), Ok("-17.78 °C".to_string())); + assert_eq!(run("32 °F"), Ok("0 °C".to_string())); } } diff --git a/src/parse.rs b/src/parse.rs index 88bda3b..f7e4301 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -97,6 +97,10 @@ fn parse_unit(input: String) -> Result { "stones" => Ok(NonMetric::Stone), "st" => Ok(NonMetric::Stone), + // Temperature + "°F" => Ok(NonMetric::Fahrenheit), + "F" => Ok(NonMetric::Fahrenheit), + _ => Err(ParseError::UnknownUnit(input)), } } @@ -242,6 +246,10 @@ mod test { assert_eq!(parse_unit("stones".to_string()), Ok(NonMetric::Stone)); assert_eq!(parse_unit("st".to_string()), Ok(NonMetric::Stone)); + // Temperature + assert_eq!(parse_unit("°F".to_string()), Ok(NonMetric::Fahrenheit)); + assert_eq!(parse_unit("F".to_string()), Ok(NonMetric::Fahrenheit)); + // Unknown unit assert_eq!(parse_unit("hutenosa".to_string()), Err(ParseError::UnknownUnit("hutenosa".to_string()))); } diff --git a/src/units.rs b/src/units.rs index 38a2921..40a42af 100644 --- a/src/units.rs +++ b/src/units.rs @@ -2,6 +2,7 @@ pub enum Metric { Metre, Gram, + Celcius, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -15,6 +16,8 @@ pub enum NonMetric { Ounce, Pound, Stone, + // Temperature + Fahrenheit, } #[derive(Clone, Copy, Debug, PartialEq)]