cas_unit_convert/
lib.rs

1#![doc = include_str!("../README.md")]
2//!
3//! [`assert_float_eq`]: https://crates.io/crates/assert_float_eq
4//! [`approx`]: https://crates.io/crates/approx
5
6pub mod convert;
7pub mod unit;
8
9use std::ops::Mul;
10pub use unit::{Area, ConversionError, CompoundUnit, Length, Mass, Base, Time, Unit, Volume};
11
12/// A value and the unit it represents.
13pub struct Measurement<T> {
14    value: T,
15    unit: CompoundUnit,
16}
17
18impl<T> Measurement<T> {
19    /// Create a new measurement.
20    pub fn new(value: T, unit: impl Into<CompoundUnit>) -> Self {
21        Self { value, unit: unit.into() }
22    }
23
24    /// Get the value of this measurement.
25    pub fn value(&self) -> &T {
26        &self.value
27    }
28
29    /// Get the unit of this measurement.
30    pub fn unit(&self) -> &CompoundUnit {
31        &self.unit
32    }
33
34    /// Convert this measurement to another unit. Returns [`Err`] if the conversion is not
35    /// possible.
36    pub fn convert(&self, target: impl Into<CompoundUnit>) -> Result<Self, ConversionError>
37        where T: Copy + Mul<f64, Output = T>,
38    {
39        let target = target.into();
40        Ok(Self {
41            value: self.value * self.unit.conversion_factor(&target)?,
42            unit: target,
43        })
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use assert_float_eq::{
50        afe_abs,
51        afe_relative_error_msg,
52        afe_is_relative_eq,
53        assert_float_relative_eq,
54    };
55    use super::*;
56    use unit::{Area, Length, Mass, Time, Volume};
57
58    #[test]
59    fn parse_unit() {
60        let unit: Unit = "µm^3".parse().unwrap();
61        assert_eq!(unit, Length::Micrometer.cubed());
62    }
63
64    #[test]
65    fn complex() {
66        let ml: Unit = "mL".parse().unwrap();
67        let cm_cubed: Unit = "cm^3".parse().unwrap();
68        let m: Measurement<f64> = Measurement::new(500.0, ml);
69        let m2 = m.convert(cm_cubed).unwrap();
70        assert_float_relative_eq!(*m2.value(), 500.0);
71    }
72
73    #[test]
74    fn identity_length() {
75        let m = Measurement::new(2.0, Length::Yard);
76        let m2 = m.convert(Length::Yard).unwrap();
77        assert_float_relative_eq!(*m2.value(), 2.0);
78    }
79
80    #[test]
81    fn convert_length() {
82        let m = Measurement::new(2.0, Length::Mile);
83        let m2 = m.convert(Length::Decimeter).unwrap();
84        assert_float_relative_eq!(*m2.value(), 32186.88);
85    }
86
87    #[test]
88    fn identity_mass() {
89        let m = Measurement::new(1690.0, Mass::Kilogram);
90        let m2 = m.convert(Mass::Kilogram).unwrap();
91        assert_float_relative_eq!(*m2.value(), 1690.0);
92    }
93
94    #[test]
95    fn convert_mass() {
96        let m = Measurement::new(37.0, Mass::Kilogram);
97        let m2 = m.convert(Mass::Pound).unwrap();
98        assert_float_relative_eq!(*m2.value(), 81.571);
99    }
100
101    #[test]
102    fn convert_area_as_length() {
103        let m = Measurement::new(2.0, Length::Meter.squared());
104        let m2 = m.convert(Length::Yard.squared()).unwrap();
105        assert_float_relative_eq!(*m2.value(), 2.39198);
106    }
107
108    #[test]
109    fn identity_area() {
110        let m = Measurement::new(11.45, Area::Acre);
111        let m2 = m.convert(Area::Acre).unwrap();
112        assert_float_relative_eq!(*m2.value(), 11.45);
113    }
114
115    #[test]
116    fn convert_len_sq_to_area() {
117        let m = Measurement::new(11.45, Length::Meter.squared());
118        let m2 = m.convert(Area::Hectare).unwrap();
119        assert_float_relative_eq!(*m2.value(), 1.145e-3);
120    }
121
122    #[test]
123    fn convert_area_to_len_sq() {
124        let m = Measurement::new(11.45, Area::Acre);
125        let m2 = m.convert(Length::NauticalMile.squared()).unwrap();
126        assert_float_relative_eq!(*m2.value(), 1.3509563543609384e-2);
127    }
128
129    #[test]
130    fn identity_volume() {
131        let m = Measurement::new(25.25, Volume::Bushel);
132        let m2 = m.convert(Volume::Bushel).unwrap();
133        assert_float_relative_eq!(*m2.value(), 25.25);
134    }
135
136    #[test]
137    fn convert_volume() {
138        let m = Measurement::new(25.25, Volume::Bushel);
139        let m2 = m.convert(Volume::Liter).unwrap();
140        assert_float_relative_eq!(*m2.value(), 889.7865276);
141    }
142
143    #[test]
144    fn convert_len_cube_to_volume() {
145        let m = Measurement::new(56.0, Length::Inch.cubed());
146        let m2 = m.convert(Volume::Milliliter).unwrap();
147        assert_float_relative_eq!(*m2.value(), 917.676);
148    }
149
150    #[test]
151    fn convert_volume_to_len_cube() {
152        let m = Measurement::new(505.0, Volume::Kiloliter);
153        let m2 = m.convert(Length::Foot.cubed()).unwrap();
154        assert_float_relative_eq!(*m2.value(), 17833.9);
155    }
156
157    #[test]
158    fn identity_time() {
159        let m = Measurement::new(38.66, Time::Decade);
160        let m2 = m.convert(Time::Decade).unwrap();
161        assert_float_relative_eq!(*m2.value(), 38.66);
162    }
163
164    #[test]
165    fn convert_time() {
166        let m = Measurement::new(38.66, Time::Decade);
167        let m2 = m.convert(Time::Decisecond).unwrap();
168        assert_float_relative_eq!(*m2.value(), 1.220016816e11);
169    }
170}