use std::{ fmt::{Display, Formatter}, marker::PhantomData, ops::{ Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }, }; use num_traits::{Num, One, Zero}; use typenum::{int::Z0, op, Integer}; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub struct SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, { value: T, _s: PhantomData, _m: PhantomData, _kg: PhantomData, _a: PhantomData, _k: PhantomData, _mol: PhantomData, _cd: PhantomData, } macro_rules! display_unit_defmt { ($formatter: ident, $param: ident, $symbol: literal, $e1: ident, $e2: ident, $e3: ident, $e4: ident, $e5: ident, $e6: ident) => { if $param::I64 != 0 { if $e1::I64 == 0 && $e2::I64 == 0 && $e3::I64 == 0 && $e4::I64 == 0 && $e5::I64 == 0 && $e6::I64 == 0 { defmt::write!($formatter, $symbol); } else { defmt::write!($formatter, "*{}", $symbol); } if $param::I64 != 1 { defmt::write!($formatter, "^{}", $param::I64); } } }; } macro_rules! display_special_unit_defmt { ($formatter: ident, $self: ident, $(($symbol: literal, $other: ty)),* $(,)?) => { $( if ::core::any::TypeId::of::<$self>() == ::core::any::TypeId::of::<$other>() { defmt::write!($formatter, $symbol); return; } )* }; } #[cfg(feature = "defmt")] impl defmt::Format for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: defmt::Format + 'static, { fn format(&self, f: defmt::Formatter<'_>) { defmt::write!(f, "{}", self.value); // derived units with special symbols display_special_unit_defmt!(f, Self, ("Hz", Herz), ("N", Newton), ("Pa", Pascal), ("J", Joule), ("W", Watt), ("C", Coulomb), ("V", Volt), ("F", Farad), ("Ohm", Ohm), ("S", Siemens), ("Wb", Weber), ("T", Tesla), ("H", Henry), ("lx", Lux), ("Gy|Sv", Gray), ("kat", Katal), ); // derived units display_special_unit_defmt!(f, Self, ("m/s", MeterPerSecond), ("m/s²", MeterPerSquareSecond), ("kg/m³", KilogramPerCubicMeter), ("kg/m²", KilogramPerSquareMeter), ("m³/kg", KilogramPerSquareMeter), ("A/m²", AmperePerSquareMeter), ("A/m", AmperePerMeter), ("mol/m³", MolePerCubicMeter), ("cd/m²", CandelaPerSquareMeter), ); // derived units including special names display_special_unit_defmt!(f, Self, ("Pa*s", PascalSecond), ("N*m", NewtonMeter), ("N/m", NewtonPerMeter), ("W/m²", WattPerSquareMeter), ("J/K", JoulePerKelvin), ("J/(kg*K)", JoulePerKilogramKelvin), ("J/kg", JoulePerKilogram), ("W/(m*K)", WattPerMeterKelvin), ("J/m³", JoulePerCubicMeter), ("V/m", VoltPerMeter), ("C/m³", CoulombPerCubicMeter), ("C/m²", CoulombPerSquareMeter), ("F/m", FaradPerMeter), ("H/m", HenryPerMeter), ("J/mol", JoulePerMole), ("J/(mol*K)", JoulePerMoleKelvin), ("C/kg", CoulombPerKilogram), ("Gy/s", GrayPerSecond), ("kat/m³", KatalPerCubicMeter), ); // base units display_unit_defmt!(f, Second, "s", Meter, Kilogram, Ampere, Kelvin, Mole, Candela); display_unit_defmt!(f, Meter, "m", Second, Kilogram, Ampere, Kelvin, Mole, Candela); display_unit_defmt!(f, Kilogram, "kg", Second, Meter, Ampere, Kelvin, Mole, Candela); display_unit_defmt!(f, Ampere, "A", Second, Meter, Kilogram, Kelvin, Mole, Candela); display_unit_defmt!(f, Kelvin, "K", Second, Meter, Kilogram, Ampere, Mole, Candela); display_unit_defmt!(f, Mole, "mol", Second, Meter, Kilogram, Ampere, Kelvin, Candela); display_unit_defmt!(f, Candela, "cd", Second, Meter, Kilogram, Ampere, Kelvin, Mole); } } macro_rules! display_unit { ($formatter: ident, $param: ident, $symbol: literal, $e1: ident, $e2: ident, $e3: ident, $e4: ident, $e5: ident, $e6: ident) => { if $param::I64 != 0 { if $e1::I64 == 0 && $e2::I64 == 0 && $e3::I64 == 0 && $e4::I64 == 0 && $e5::I64 == 0 && $e6::I64 == 0 { write!($formatter, $symbol)?; } else { write!($formatter, "*{}", $symbol)?; } if $param::I64 != 1 { write!($formatter, "^{}", $param::I64)?; } } }; } macro_rules! display_special_unit { ($formatter: ident, $self: ident, $(($symbol: literal, $other: ty)),* $(,)?) => { $( if ::core::any::TypeId::of::<$self>() == ::core::any::TypeId::of::<$other>() { return write!($formatter, $symbol); } )* }; } impl Display for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Display + 'static, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.value)?; // derived units with special symbols display_special_unit!(f, Self, ("Hz", Herz), ("N", Newton), ("Pa", Pascal), ("J", Joule), ("W", Watt), ("C", Coulomb), ("V", Volt), ("F", Farad), ("Ohm", Ohm), ("S", Siemens), ("Wb", Weber), ("T", Tesla), ("H", Henry), ("lx", Lux), ("Gy|Sv", Gray), ("kat", Katal), ); // derived units display_special_unit!(f, Self, ("m/s", MeterPerSecond), ("m/s²", MeterPerSquareSecond), ("kg/m³", KilogramPerCubicMeter), ("kg/m²", KilogramPerSquareMeter), ("m³/kg", KilogramPerSquareMeter), ("A/m²", AmperePerSquareMeter), ("A/m", AmperePerMeter), ("mol/m³", MolePerCubicMeter), ("cd/m²", CandelaPerSquareMeter), ); // derived units including special names display_special_unit!(f, Self, ("Pa*s", PascalSecond), ("N*m", NewtonMeter), ("N/m", NewtonPerMeter), ("W/m²", WattPerSquareMeter), ("J/K", JoulePerKelvin), ("J/(kg*K)", JoulePerKilogramKelvin), ("J/kg", JoulePerKilogram), ("W/(m*K)", WattPerMeterKelvin), ("J/m³", JoulePerCubicMeter), ("V/m", VoltPerMeter), ("C/m³", CoulombPerCubicMeter), ("C/m²", CoulombPerSquareMeter), ("F/m", FaradPerMeter), ("H/m", HenryPerMeter), ("J/mol", JoulePerMole), ("J/(mol*K)", JoulePerMoleKelvin), ("C/kg", CoulombPerKilogram), ("Gy/s", GrayPerSecond), ("kat/m³", KatalPerCubicMeter), ); // base units display_unit!(f, Second, "s", Meter, Kilogram, Ampere, Kelvin, Mole, Candela); display_unit!(f, Meter, "m", Second, Kilogram, Ampere, Kelvin, Mole, Candela); display_unit!(f, Kilogram, "kg", Second, Meter, Ampere, Kelvin, Mole, Candela); display_unit!(f, Ampere, "A", Second, Meter, Kilogram, Kelvin, Mole, Candela); display_unit!(f, Kelvin, "K", Second, Meter, Kilogram, Ampere, Mole, Candela); display_unit!(f, Mole, "mol", Second, Meter, Kilogram, Ampere, Kelvin, Candela); display_unit!(f, Candela, "cd", Second, Meter, Kilogram, Ampere, Kelvin, Mole); Ok(()) } } impl Deref for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, { type Target = T; fn deref(&self) -> &Self::Target { &self.value } } impl DerefMut for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } } impl Neg for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Neg, { type Output = SiUnit; fn neg(self) -> Self::Output { Self::Output::new(-self.value) } } impl Zero for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Zero, { fn zero() -> Self { Self::new(T::zero()) } fn is_zero(&self) -> bool { self.value.is_zero() } } impl One for SiUnit where Second: Integer + Add, Meter: Integer + Add, Kilogram: Integer + Add, Ampere: Integer + Add, Kelvin: Integer + Add, Mole: Integer + Add, Candela: Integer + Add, T: One, ::Output: Integer, ::Output: Integer, ::Output: Integer, ::Output: Integer, ::Output: Integer, ::Output: Integer, ::Output: Integer, { fn one() -> Self { Self::new(T::one()) } } impl Num for SiUnit where Second: Integer + Add + Sub + PartialEq, Meter: Integer + Add + Sub + PartialEq, Kilogram: Integer + Add + Sub + PartialEq, Ampere: Integer + Add + Sub + PartialEq, Kelvin: Integer + Add + Sub + PartialEq, Mole: Integer + Add + Sub + PartialEq, Candela: Integer + Add + Sub + PartialEq, T: Num, { type FromStrRadixErr = T::FromStrRadixErr; fn from_str_radix(str: &str, radix: u32) -> Result { T::from_str_radix(str, radix).map(Self::new) } } impl Add for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Add, { type Output = SiUnit; fn add(self, rhs: Self) -> Self::Output { Self::Output::new(self.value + rhs.value) } } impl AddAssign for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: AddAssign, { fn add_assign(&mut self, rhs: Self) { self.value += rhs.value; } } impl Sub for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Sub, { type Output = SiUnit; fn sub(self, rhs: Self) -> Self::Output { Self::Output::new(self.value - rhs.value) } } impl SubAssign for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: SubAssign, { fn sub_assign(&mut self, rhs: Self) { self.value -= rhs.value; } } impl Mul for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: Mul, { type Output = >>::Output; fn mul(self, rhs: T) -> Self::Output { self * Unit::new(rhs) } } impl MulAssign for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: MulAssign, { fn mul_assign(&mut self, rhs: T) { self.value *= rhs; } } impl Div for SiUnit where Second: Integer + Sub, Meter: Integer + Sub, Kilogram: Integer + Sub, Ampere: Integer + Sub, Kelvin: Integer + Sub, Mole: Integer + Sub, Candela: Integer + Sub, T: Div, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, { type Output = >>::Output; fn div(self, rhs: T) -> Self::Output { self / Unit::new(rhs) } } impl DivAssign for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: DivAssign, { fn div_assign(&mut self, rhs: T) { self.value /= rhs; } } impl Rem for SiUnit where Second: Integer + Sub, Meter: Integer + Sub, Kilogram: Integer + Sub, Ampere: Integer + Sub, Kelvin: Integer + Sub, Mole: Integer + Sub, Candela: Integer + Sub, T: Rem, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, >::Output: Integer, { type Output = >>::Output; fn rem(self, rhs: T) -> Self::Output { self % Unit::new(rhs) } } impl RemAssign for SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, T: RemAssign, { fn rem_assign(&mut self, rhs: T) { self.value %= rhs; } } impl SiUnit where Second: Integer, Meter: Integer, Kilogram: Integer, Ampere: Integer, Kelvin: Integer, Mole: Integer, Candela: Integer, { pub const fn new(value: T) -> Self { Self { value, _s: PhantomData, _m: PhantomData, _kg: PhantomData, _a: PhantomData, _k: PhantomData, _mol: PhantomData, _cd: PhantomData, } } } impl< T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2, > Mul> for SiUnit where Mole2: Integer, Candela2: Integer, Candela1: Integer + Add, Mole1: Integer + Add, Kelvin1: Integer + Add, Ampere1: Integer + Add, Kilogram1: Integer + Add, Meter1: Integer + Add, Second1: Integer + Add, Kelvin2: Integer, Ampere2: Integer, Kilogram2: Integer, Meter2: Integer, Second2: Integer, T: Mul, Second1::Output: Integer, Meter1::Output: Integer, Kilogram1::Output: Integer, Ampere1::Output: Integer, Kelvin1::Output: Integer, Mole1::Output: Integer, Candela1::Output: Integer, { type Output = SiUnit< T::Output, op!(Second1 + Second2), op!(Meter1 + Meter2), op!(Kilogram1 + Kilogram2), op!(Ampere1 + Ampere2), op!(Kelvin1 + Kelvin2), op!(Mole1 + Mole2), op!(Candela1 + Candela2), >; fn mul( self, rhs: SiUnit, ) -> Self::Output { Self::Output::new(self.value * rhs.value) } } impl< T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2, > Div> for SiUnit where Second1: Integer, Meter1: Integer, Kilogram1: Integer, Ampere1: Integer, Kelvin1: Integer, Mole1: Integer, Candela1: Integer, Second2: Integer + Sub, Meter2: Integer + Sub, Kilogram2: Integer + Sub, Ampere2: Integer + Sub, Kelvin2: Integer + Sub, Mole2: Integer + Sub, Candela2: Integer + Sub, T: Div, Second2::Output: Integer, Meter2::Output: Integer, Kilogram2::Output: Integer, Ampere2::Output: Integer, Kelvin2::Output: Integer, Mole2::Output: Integer, Candela2::Output: Integer, { type Output = SiUnit< T::Output, op!(Second2 - Second1), op!(Meter2 - Meter1), op!(Kilogram2 - Kilogram1), op!(Ampere2 - Ampere1), op!(Kelvin2 - Kelvin1), op!(Mole2 - Mole1), op!(Candela2 - Candela1), >; fn div( self, rhs: SiUnit, ) -> Self::Output { Self::Output::new(self.value / rhs.value) } } impl< T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2, > Rem> for SiUnit where Second1: Integer, Meter1: Integer, Kilogram1: Integer, Ampere1: Integer, Kelvin1: Integer, Mole1: Integer, Candela1: Integer, Second2: Integer + Sub, Meter2: Integer + Sub, Kilogram2: Integer + Sub, Ampere2: Integer + Sub, Kelvin2: Integer + Sub, Mole2: Integer + Sub, Candela2: Integer + Sub, T: Rem, Second2::Output: Integer, Meter2::Output: Integer, Kilogram2::Output: Integer, Ampere2::Output: Integer, Kelvin2::Output: Integer, Mole2::Output: Integer, Candela2::Output: Integer, { type Output = SiUnit< T::Output, op!(Second2 - Second1), op!(Meter2 - Meter1), op!(Kilogram2 - Kilogram1), op!(Ampere2 - Ampere1), op!(Kelvin2 - Kelvin1), op!(Mole2 - Mole1), op!(Candela2 - Candela1), >; fn rem( self, rhs: SiUnit, ) -> Self::Output { Self::Output::new(self.value % rhs.value) } } #[rustfmt::skip] mod aliases { use super::SiUnit; use typenum::consts::{N1, N2, N3, P1, P2, P3, P4, Z0}; // Base units pub type Unit = SiUnit; /// time pub type Second = SiUnit; /// length pub type Meter = SiUnit; /// mass pub type Kilogram = SiUnit; /// electric current pub type Ampere = SiUnit; /// thermodynamic temperature pub type Kelvin = SiUnit; /// amount of substance pub type Mole = SiUnit; /// luminous intensity pub type Candela = SiUnit; // Derived units with special names /// plane angle pub type Radian = Unit; /// solid angle pub type Steradian = Unit; /// frequency pub type Herz = SiUnit; /// force, weight pub type Newton = SiUnit; /// pressure, stress pub type Pascal = SiUnit; /// energy, work, heat pub type Joule = SiUnit; /// power, radiant flux pub type Watt = SiUnit; /// electric charge pub type Coulomb = SiUnit; /// electric potential, voltage, emf pub type Volt = SiUnit; /// capacitance pub type Farad = SiUnit; /// resistance impedance, reactance pub type Ohm = SiUnit; /// electrical conductance pub type Siemens = SiUnit; /// magnetic flux pub type Weber = SiUnit; /// magnetic flux density pub type Tesla = SiUnit; /// inductance pub type Henry = SiUnit; /// luminous flux pub type Lumen = Candela; /// iluminance pub type Lux = SiUnit; /// activity referred to a radionuclide pub type Becquerel = Herz; /// absorbed dose pub type Gray = SiUnit; /// equivalent dose pub type Sievert = Gray; /// catalytic activity pub type Katal = SiUnit; // Derived units without special names /// area pub type SquareMeter = SiUnit; /// volume pub type CubicMeter = SiUnit; /// speed, velocity pub type MeterPerSecond = SiUnit; /// acceleration pub type MeterPerSquareSecond = SiUnit; /// wavenumber, vergence pub type ReciprocalMeter = SiUnit; /// density, mass concentration pub type KilogramPerCubicMeter = SiUnit; /// surface density pub type KilogramPerSquareMeter = SiUnit; /// specific density pub type CubicMeterPerKilogram = SiUnit; /// current density pub type AmperePerSquareMeter = SiUnit; /// magnetic field strength pub type AmperePerMeter = SiUnit; /// concentration pub type MolePerCubicMeter = SiUnit; /// luminance pub type CandelaPerSquareMeter = SiUnit; // Derived units including special names /// dynamic viscosity pub type PascalSecond = SiUnit; /// moment of force pub type NewtonMeter = SiUnit; /// surface tension pub type NewtonPerMeter = SiUnit; /// angular velocity, angular frequency pub type RadianPerSecond = SiUnit; /// angular acceleration pub type RadianPerSquareSecond = SiUnit; /// heat flux density, irradiance pub type WattPerSquareMeter = SiUnit; /// entropy, heat capacity pub type JoulePerKelvin = SiUnit; /// specific heat capacity, specific entropy pub type JoulePerKilogramKelvin = SiUnit; /// specific energy pub type JoulePerKilogram = SiUnit; /// themal conductivity pub type WattPerMeterKelvin = SiUnit; /// energy density pub type JoulePerCubicMeter = SiUnit; /// electric field strength pub type VoltPerMeter = SiUnit; /// electric charge density pub type CoulombPerCubicMeter = SiUnit; /// surface charge density, electirc flusx density, electric displacement pub type CoulombPerSquareMeter = SiUnit; /// permittivity pub type FaradPerMeter = SiUnit; /// permeability pub type HenryPerMeter = SiUnit; /// molar energy pub type JoulePerMole = SiUnit; /// molar entropy, molar heat capacity pub type JoulePerMoleKelvin = SiUnit; /// exposure (x- and gamma-rays) pub type CoulombPerKilogram = SiUnit; /// absorbed dose rate pub type GrayPerSecond = SiUnit; /// radiant intensity pub type WattPerSteradian = SiUnit; /// radiance pub type WattPerSquareMeterSteradian = SiUnit; /// catalytic activity concentration pub type KatalPerCubicMeter = SiUnit; } pub use aliases::*; #[cfg(test)] mod test { use super::*; #[test] fn debug() { let m = Meter::new(2); assert_eq!(format!("{m:?}"), "SiUnit { value: 2, _s: PhantomData, _m: PhantomData>>, _kg: PhantomData, _a: PhantomData, _k: PhantomData, _mol: PhantomData, _cd: PhantomData }".to_owned()); } #[test] fn display() { let unit = Unit::new(2); let second = Second::new(2); let meter = Meter::new(2); let kilogram = Kilogram::new(2); let ampere = Ampere::new(2); let kelvin = Kelvin::new(2); let mole = Mole::new(2); let candela = Candela::new(2); assert_eq!(unit.to_string(), "2"); assert_eq!(second.to_string(), "2s"); assert_eq!(meter.to_string(), "2m"); assert_eq!(kilogram.to_string(), "2kg"); assert_eq!(ampere.to_string(), "2A"); assert_eq!(kelvin.to_string(), "2K"); assert_eq!(mole.to_string(), "2mol"); assert_eq!(candela.to_string(), "2cd"); let square_second: SiUnit = second * second; let square_meter: SiUnit = meter * meter; let square_kilogram: SiUnit = kilogram * kilogram; let square_ampere: SiUnit = ampere * ampere; let square_kelvin: SiUnit = kelvin * kelvin; let square_mole: SiUnit = mole * mole; let square_candela: SiUnit = candela * candela; assert_eq!(square_second.to_string(), "4s^2"); assert_eq!(square_meter.to_string(), "4m^2"); assert_eq!(square_kilogram.to_string(), "4kg^2"); assert_eq!(square_ampere.to_string(), "4A^2"); assert_eq!(square_kelvin.to_string(), "4K^2"); assert_eq!(square_mole.to_string(), "4mol^2"); assert_eq!(square_candela.to_string(), "4cd^2"); let w = Watt::new(2); assert_eq!(w.to_string(), "2W"); } #[test] fn clone() { let m = Meter::new(2); assert_eq!(m.clone(), m); } #[test] fn ord() { let a = Meter::new(2); let b = Meter::new(3); assert!(a < b); assert!(b > a); assert!(a == a); assert!(a != b); assert_eq!(a.max(b), b); assert_eq!(a.min(b), a); } #[test] fn deref() { let m = Meter::new(2); assert_eq!(*m, 2); } #[test] fn deref_mut() { let mut m = Meter::new(2); *m = 3; assert_eq!(*m, 3); } #[test] fn neg() { let m = Meter::new(2); assert_eq!(-m, Meter::new(-2)); } #[test] fn zero() { let z = Meter::zero(); assert_eq!(z, Meter::new(0)); assert!(z.is_zero()); } #[test] fn one() { let o = Unit::one(); assert_eq!(o, Unit::new(1)); } #[test] fn from_str_radix() { assert_eq!(Unit::from_str_radix("24", 10), Ok(Unit::new(24))); } #[test] fn add() { let m = Meter::new(2); assert_eq!(m + m, Meter::new(4)); assert_eq!(m + m + m, Meter::new(6)); } #[test] fn add_assign() { let mut m = Meter::new(2); m += m; assert_eq!(m, Meter::new(4)); } #[test] fn sub() { let m = Meter::new(2); assert_eq!(m - m, Meter::new(0)); assert_eq!(m - m - m, Meter::new(-2)); } #[test] fn sub_assign() { let mut m = Meter::new(2); m -= m; assert_eq!(m, Meter::new(0)); } #[test] fn mul() { let m = Meter::new(2); assert_eq!(m * m, SquareMeter::new(4)); assert_eq!(m * m * m, CubicMeter::new(8)); let s = Second::new(1); let a = Ampere::new(2); assert_eq!(s * a, Coulomb::new(2)); let v = Volt::new(2); assert_eq!(v * a, Watt::new(4)); assert_eq!(m * 2, Meter::new(4)); } #[test] fn mul_assign() { let mut m = Meter::new(2); m *= 2; assert_eq!(m, Meter::new(4)); } #[test] fn div() { let m = Meter::new(2); assert_eq!(m / m, Unit::new(1)); assert_eq!(m / m / m, ReciprocalMeter::new(0)); let c = Coulomb::new(4); let a = Ampere::new(2); assert_eq!(c / a, Second::new(2)); let w = Watt::new(2); assert_eq!(w / a, Volt::new(1)); assert_eq!(m / 2, Meter::new(1)); } #[test] fn div_assign() { let mut m = Meter::new(2); m /= 2; assert_eq!(m, Meter::new(1)); } #[test] fn rem() { let m = Meter::new(2); assert_eq!(m % m, Unit::new(0)); assert_eq!(m / m % m, ReciprocalMeter::new(1)); let c = Coulomb::new(4); let a = Ampere::new(2); assert_eq!(c % a, Second::new(0)); let w = Watt::new(2); assert_eq!(w % a, Volt::new(0)); assert_eq!(m % 2, Meter::new(0)); } #[test] fn rem_assign() { let mut m = Meter::new(2); m %= 2; assert_eq!(m, Meter::new(0)); } }