1#![doc = include_str!("../README.md")]
2pub mod convert;
7pub mod unit;
8
9use std::ops::Mul;
10pub use unit::{Area, ConversionError, CompoundUnit, Length, Mass, Base, Time, Unit, Volume};
11
12pub struct Measurement<T> {
14 value: T,
15 unit: CompoundUnit,
16}
17
18impl<T> Measurement<T> {
19 pub fn new(value: T, unit: impl Into<CompoundUnit>) -> Self {
21 Self { value, unit: unit.into() }
22 }
23
24 pub fn value(&self) -> &T {
26 &self.value
27 }
28
29 pub fn unit(&self) -> &CompoundUnit {
31 &self.unit
32 }
33
34 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}