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 { let inch_from = 10_000.0; let inch_to = 254.0; let pound_from = 100_000.0; let pound_to = 45359237.0; let imperial_gallon_from = 100_000.0; let imperial_gallon_to = 454609.0; let us_gallon_from = inch_from * inch_from * inch_from; let us_gallon_to = 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() { let tests = [ Test(NonMetric::Inch, 0.0254), Test(NonMetric::Foot, 0.3048), Test(NonMetric::Yard, 0.9144), Test(NonMetric::Mile, 1609.344), ]; run_tests(&tests, Metric::Metre); } #[test] fn mass() { let tests = [ 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), ]; run_tests(&tests, Metric::Gram); } #[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() { let tests = [ 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), ]; run_tests(&tests, Metric::SquareMetre); } #[test] fn volume() { let tests = [ Test(NonMetric::CubicInch, 1.6387064e-5), Test(NonMetric::CubicFoot, 0.028316846592), Test(NonMetric::CubicYard, 0.764554857984), ]; run_tests(&tests, Metric::CubicMetre); } #[test] fn fluid_volume() { let tests = [ 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), ]; run_tests(&tests, Metric::Litre); } fn run_tests(tests: &[Test], unit: Metric) { 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); } } }