use crate::units::{Metric, MetricQuantity}; pub fn format(quantity: MetricQuantity) -> String { let SiUnit(multiplier, unit) = si_unit(quantity); let amount = quantity.amount / multiplier; format!("{amount} {unit}") } #[derive(Debug, PartialEq)] struct SiUnit(f64, &'static str); fn si_unit(quantity: MetricQuantity) -> SiUnit { let absolute = quantity.amount.abs(); match quantity.unit { Metric::Metre => { if absolute >= 1000.0 { return SiUnit(1000.0, "km"); } else if absolute >= 1.0 { return SiUnit(1.0, "m"); } else if absolute >= 0.01 { return SiUnit(0.01, "cm"); } else { return SiUnit(0.001, "mm"); } } Metric::Gram => { if absolute >= 1000.0 { return SiUnit(1000.0, "kg"); } else { return SiUnit(1.0, "g"); } } } } #[cfg(test)] mod test { use super::*; #[test] fn quantities() { assert_eq!("0.1 mm", &format(MetricQuantity { amount: 0.000_1, unit: Metric::Metre, })); assert_eq!("-1 m", &format(MetricQuantity { amount: -1.0, unit: Metric::Metre, })); assert_eq!("1.5 m", &format(MetricQuantity { amount: 1.5, unit: Metric::Metre, })); assert_eq!("1000 km", &format(MetricQuantity { amount: 1_000_000.0, unit: Metric::Metre, })); assert_eq!("-5 g", &format(MetricQuantity { amount: -5.0, unit: Metric::Gram, })); assert_eq!("3.2 kg", &format(MetricQuantity { amount: 3_200.0, unit: Metric::Gram, })); } #[test] fn units() { assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { amount: 0.0001, unit: Metric::Metre, })); assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { amount: 0.001, unit: Metric::Metre, })); assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { amount: 0.01, unit: Metric::Metre, })); assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { amount: 0.1, unit: Metric::Metre, })); assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { amount: 1.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { amount: 10.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { amount: 100.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { amount: 1000.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { amount: 10_000.0, unit: Metric::Metre, })); assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity { amount: -0.001, unit: Metric::Metre, })); assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity { amount: -0.01, unit: Metric::Metre, })); assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity { amount: -1.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity { amount: -1000.0, unit: Metric::Metre, })); assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { amount: 0.1, unit: Metric::Gram, })); assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { amount: 1.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { amount: 10.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { amount: 100.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { amount: 1000.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { amount: 10_1000.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity { amount: -1.0, unit: Metric::Gram, })); assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity { amount: -1000.0, unit: Metric::Gram, })); } }