use std::{ fmt::{self, Display, Formatter}, ops::Mul, }; pub trait Volume { /// Returns the volume in SI units (cubic metres). fn si(self) -> CubicMetre; fn set(&mut self, val: f64); fn unit(&self) -> Unit; fn convert(self, unit: Unit) -> Box; } pub enum Unit { CubicMetre, Litre, } impl Display for Unit { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, "{}", match self { Self::CubicMetre => "m³", Self::Litre => "L", } ) } } #[derive(Debug, Default, PartialEq)] pub struct CubicMetre(f64); impl Volume for CubicMetre { fn si(self) -> CubicMetre { self } fn set(&mut self, val: f64) { self.0 = val; } fn unit(&self) -> Unit { Unit::CubicMetre } fn convert(self, unit: Unit) -> Box { match unit { Unit::CubicMetre => Box::new(self), Unit::Litre => Box::new(Litre::from(self)), } } } impl From for CubicMetre { fn from(value: Litre) -> Self { value.si() } } #[derive(Debug, PartialEq)] struct Litre(f64); impl Volume for Litre { fn si(self) -> CubicMetre { CubicMetre(self.0 * 10_f64.powf(-3.)) } fn set(&mut self, val: f64) { self.0 = val } fn unit(&self) -> Unit { Unit::Litre } fn convert(self, unit: Unit) -> Box { match unit { Unit::CubicMetre => Box::new(CubicMetre::from(self)), Unit::Litre => Box::new(self), } } } impl From for Litre { fn from(value: i32) -> Self { Self(value as f64) } } impl From for Litre { fn from(value: CubicMetre) -> Self { Self(value.0 * 10_f64.powf(3.)) } } impl Mul for Litre { type Output = Self; fn mul(self, rhs: f64) -> Self::Output { Self(self.0 * rhs) } } #[cfg(test)] mod tests { use crate::volume::{CubicMetre, Litre, Volume}; #[test] fn litre_to_cubic_metre() { assert_eq!(Litre(1000.).si(), CubicMetre(1.)) } #[test] fn cubic_metre_to_litre() { assert_eq!(Litre::from(CubicMetre(1.)), Litre(1000.)) } }