use std::fmt::{self, Display, Formatter}; #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub enum Unit { #[default] CubicMetre, Litre, CubicCentimetre, } impl Unit { pub const ALL: [Self; 3] = [Self::CubicMetre, Self::Litre, Self::CubicCentimetre]; } impl Display for Unit { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, "{}", match self { Self::CubicMetre => "m³", Self::Litre => "L", Self::CubicCentimetre => "cc", } ) } } #[derive(Debug, Default, PartialEq)] pub struct CubicMetre(pub f64); impl CubicMetre { pub fn from_unit>(unit: Unit, value: F) -> Self { match unit { Unit::CubicMetre => Self(value.into()), Unit::Litre => Self::from(Litre(value.into())), Unit::CubicCentimetre => Self::from(CubicCentimetre(value.into())), } } } impl From for CubicMetre { fn from(value: Litre) -> Self { Self(value.0 * 10_f64.powi(-3)) } } impl From for CubicMetre { fn from(value: CubicCentimetre) -> Self { Self(value.0 * 10_f64.powi(-6)) } } #[derive(Debug, PartialEq)] struct Litre(f64); impl From for Litre { fn from(value: CubicMetre) -> Self { Self(value.0 * 10_f64.powi(3)) } } impl From for Litre { fn from(value: CubicCentimetre) -> Self { Self(value.0 * 10_f64.powi(-3)) } } #[derive(Debug, PartialEq)] pub struct CubicCentimetre(f64); impl From for CubicCentimetre { fn from(value: CubicMetre) -> Self { Self(value.0 * 10_f64.powi(6)) } } impl From for CubicCentimetre { fn from(value: Litre) -> Self { Self(value.0 * 10_f64.powi(3)) } } pub fn convert>(val: F, from: Unit, to: Unit) -> f64 { match from { Unit::CubicMetre => match to { Unit::CubicMetre => val.into(), Unit::Litre => Litre::from(CubicMetre(val.into())).0, Unit::CubicCentimetre => CubicCentimetre::from(CubicMetre(val.into())).0, }, Unit::Litre => match to { Unit::Litre => val.into(), Unit::CubicMetre => CubicMetre::from(Litre(val.into())).0, Unit::CubicCentimetre => CubicCentimetre::from(Litre(val.into())).0, }, Unit::CubicCentimetre => match to { Unit::CubicCentimetre => val.into(), Unit::CubicMetre => CubicMetre::from(CubicCentimetre(val.into())).0, Unit::Litre => Litre::from(CubicCentimetre(val.into())).0, }, } } #[cfg(test)] mod tests { use super::{CubicCentimetre, CubicMetre, Litre}; #[test] fn cubic_metre_to_litre() { assert_eq!(Litre::from(CubicMetre(1.)), Litre(1000.)); } #[test] fn cubic_metre_to_cubic_centimetre() { assert_eq!( CubicCentimetre::from(CubicMetre(1.)), CubicCentimetre(1_000_000.) ); } #[test] fn litre_to_cubic_metre() { assert_eq!(CubicMetre::from(Litre(1000.)), CubicMetre(1.)); } #[test] fn litre_to_cubic_centimetre() { assert_eq!(CubicCentimetre::from(Litre(1.)), CubicCentimetre(1000.)); } #[test] fn cubic_centimetre_to_cubic_metre() { assert_eq!( CubicMetre::from(CubicCentimetre(1_000_000.)), CubicMetre(1.) ); } #[test] fn cubic_centimetre_to_litre() { assert_eq!(Litre::from(CubicCentimetre(1000.)), Litre(1.)); } }