use crate::units::{Metric, MetricQuantity, NonMetric, NonMetricQuantity}; pub fn convert(from: NonMetricQuantity) -> MetricQuantity { let conversion = get_conversion(from.unit); 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, } fn get_conversion(unit: NonMetric) -> Conversion { const INCH_FROM: f64 = 10_000.0; const INCH_TO: f64 = 254.0; const POUND_FROM: f64 = 100_000.0; const POUND_TO: f64 = 45359237.0; const IMPERIAL_GALLON_FROM: f64 = 100_000.0; const IMPERIAL_GALLON_TO: f64 = 454609.0; const US_GALLON_FROM: f64 = INCH_FROM * INCH_FROM * INCH_FROM; const US_GALLON_TO: f64 = 231.0 * INCH_TO * INCH_TO * INCH_TO * 1000.0; 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 }, }, // Mass 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 }, }, NonMetric::ShortTon => Conversion { offset: 0.0, from: POUND_FROM, to: MetricQuantity { amount: 2000.0 * POUND_TO, unit: Metric::Gram }, }, NonMetric::LongTon => Conversion { offset: 0.0, from: POUND_FROM, to: MetricQuantity { amount: 2240.0 * POUND_TO, unit: Metric::Gram }, }, // Temperature NonMetric::Fahrenheit => Conversion { offset: 32.0, from: 9.0, to: MetricQuantity { amount: 5.0, unit: Metric::Celsius }, }, // Area NonMetric::SquareInch => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM, to: MetricQuantity { amount: INCH_TO * INCH_TO, unit: Metric::SquareMetre }, }, NonMetric::SquareFoot => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::SquareMetre }, }, NonMetric::SquareYard => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO, unit: Metric::SquareMetre }, }, NonMetric::Acre => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 43_560.0 * 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::SquareMetre }, }, NonMetric::SquareMile => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * INCH_TO * 1760.0 * 3.0 * 12.0 * INCH_TO, unit: Metric::SquareMetre }, }, // Volume NonMetric::CubicInch => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM * INCH_FROM, to: MetricQuantity { amount: INCH_TO * INCH_TO * INCH_TO, unit: Metric::CubicMetre }, }, NonMetric::CubicFoot => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 12.0 * INCH_TO * 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::CubicMetre }, }, NonMetric::CubicYard => Conversion { offset: 0.0, from: INCH_FROM * INCH_FROM * INCH_FROM, to: MetricQuantity { amount: 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO, unit: Metric::CubicMetre }, }, // Fluid volume NonMetric::ImperialFluidOunce => Conversion { offset: 0.0, from: 20.0 * 2.0 * 4.0 * IMPERIAL_GALLON_FROM, to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre }, }, NonMetric::ImperialPint => Conversion { offset: 0.0, from: 2.0 * 4.0 * IMPERIAL_GALLON_FROM, to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre }, }, NonMetric::ImperialQuart => Conversion { offset: 0.0, from: 4.0 * IMPERIAL_GALLON_FROM, to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre }, }, NonMetric::ImperialGallon => Conversion { offset: 0.0, from: IMPERIAL_GALLON_FROM, to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USTeaspoon => Conversion { offset: 0.0, from: 6.0 * 16.0 * 2.0 * 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USTablespoon => Conversion { offset: 0.0, from: 2.0 * 16.0 * 2.0 * 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USFluidOunce => Conversion { offset: 0.0, from: 16.0 * 2.0 * 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USCup => Conversion { offset: 0.0, from: 2.0 * 2.0 * 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USLiquidPint => Conversion { offset: 0.0, from: 2.0 * 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USLiquidQuart => Conversion { offset: 0.0, from: 4.0 * US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, NonMetric::USGallon => Conversion { offset: 0.0, from: US_GALLON_FROM, to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre }, }, } } #[cfg(test)] mod test { use super::*; struct Test(NonMetric, f64); #[test] fn length() { run_tests(Metric::Metre, &[ Test(NonMetric::Inch, 0.0254), Test(NonMetric::Foot, 0.3048), Test(NonMetric::Yard, 0.9144), Test(NonMetric::Mile, 1609.344), ]); } #[test] fn mass() { run_tests(Metric::Gram, &[ Test(NonMetric::Ounce, 28.349523125), Test(NonMetric::Pound, 453.59237), Test(NonMetric::Stone, 6350.29318), Test(NonMetric::ShortTon, 907184.74), Test(NonMetric::LongTon, 1016046.9088), ]); } #[test] fn temperature() { assert_eq!(convert(NonMetricQuantity { amount: -40.0, unit: NonMetric::Fahrenheit, }), MetricQuantity { amount: -40.0, unit: Metric::Celsius, }); assert_eq!(convert(NonMetricQuantity { amount: 32.0, unit: NonMetric::Fahrenheit, }), MetricQuantity { amount: 0.0, unit: Metric::Celsius, }); } #[test] fn area() { run_tests(Metric::SquareMetre, &[ Test(NonMetric::SquareInch, 0.00064516), Test(NonMetric::SquareFoot, 0.09290304), Test(NonMetric::SquareYard, 0.83612736), Test(NonMetric::Acre, 4046.8564224), Test(NonMetric::SquareMile, 2589988.110336), ]); } #[test] fn volume() { run_tests(Metric::CubicMetre, &[ Test(NonMetric::CubicInch, 1.6387064e-5), Test(NonMetric::CubicFoot, 0.028316846592), Test(NonMetric::CubicYard, 0.764554857984), ]); } #[test] fn fluid_volume() { run_tests(Metric::Litre, &[ Test(NonMetric::ImperialFluidOunce, 0.0284130625), Test(NonMetric::ImperialPint, 0.56826125), Test(NonMetric::ImperialQuart, 1.1365225), Test(NonMetric::ImperialGallon, 4.54609), Test(NonMetric::USTeaspoon, 0.00492892159375), Test(NonMetric::USTablespoon, 0.01478676478125), Test(NonMetric::USFluidOunce, 0.0295735295625), Test(NonMetric::USCup, 0.2365882365), Test(NonMetric::USLiquidPint, 0.473176473), Test(NonMetric::USLiquidQuart, 0.946352946), Test(NonMetric::USGallon, 3.785411784), ]); } fn run_tests(unit: Metric, tests: &[Test]) { for test in tests { let from = NonMetricQuantity { amount: 1.0, unit: test.0, }; let to = MetricQuantity { amount: test.1, unit: unit, }; assert_eq!(convert(from), to); } } }