1use float_cmp::{ApproxEq, F64Margin};
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::iter::Sum;
7use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
8
9pub trait UnitType:
11 fmt::Debug
12 + Copy
13 + PartialEq
14 + PartialOrd
15 + Serialize
16 + Add
17 + Sub
18 + Div
19 + Mul<Dimensionless, Output = Self>
20 + AddAssign
21 + SubAssign
22 + Sum
23 + ApproxEq<Margin = F64Margin>
24 + fmt::Display
25 + Default
26{
27 fn new(value: f64) -> Self;
29 fn value(&self) -> f64;
31 fn is_normal(&self) -> bool;
33 fn is_finite(&self) -> bool;
35 fn abs(&self) -> Self;
37 fn max(&self, other: Self) -> Self;
39 fn min(&self, other: Self) -> Self;
41 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
43}
44
45macro_rules! base_unit_struct {
46 ($name:ident) => {
47 #[derive(
49 Debug,
50 Clone,
51 Copy,
52 PartialEq,
53 PartialOrd,
54 Default,
55 Serialize,
56 derive_more::Add,
57 derive_more::Sub,
58 )]
59 pub struct $name(pub f64);
60
61 impl std::ops::Div<$name> for $name {
62 type Output = Dimensionless;
63 fn div(self, rhs: $name) -> Dimensionless {
64 Dimensionless(self.0 / rhs.0)
65 }
66 }
67 impl std::iter::Sum for $name {
68 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
69 iter.fold($name(0.0), |a, b| $name(a.0 + b.0))
70 }
71 }
72 impl AddAssign for $name {
73 fn add_assign(&mut self, other: Self) {
74 self.0 += other.0;
75 }
76 }
77 impl SubAssign for $name {
78 fn sub_assign(&mut self, other: Self) {
79 self.0 -= other.0;
80 }
81 }
82 impl fmt::Display for $name {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}", self.0)
85 }
86 }
87 impl float_cmp::ApproxEq for $name {
88 type Margin = float_cmp::F64Margin;
89 fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
90 self.0.approx_eq(other.0, margin)
91 }
92 }
93 impl std::ops::Neg for $name {
94 type Output = $name;
95 fn neg(self) -> $name {
96 $name(-self.0)
97 }
98 }
99 impl $name {
100 pub const EPSILON: $name = $name(f64::EPSILON);
102
103 pub fn new(value: f64) -> Self {
105 $name(value)
106 }
107 pub fn value(&self) -> f64 {
109 self.0
110 }
111 pub fn is_normal(&self) -> bool {
113 self.0.is_normal()
114 }
115 pub fn is_finite(&self) -> bool {
117 self.0.is_finite()
118 }
119 pub fn abs(&self) -> Self {
121 $name(self.0.abs())
122 }
123 pub fn max(&self, other: Self) -> Self {
125 Self(self.0.max(other.0))
126 }
127 pub fn min(&self, other: Self) -> Self {
129 Self(self.0.min(other.0))
130 }
131 pub fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
133 self.0.total_cmp(&other.0)
134 }
135 }
136 impl<'de> Deserialize<'de> for $name {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 let value = f64::deserialize(deserializer)?;
142 if !value.is_finite() {
143 Err(serde::de::Error::custom("Value cannot be NaN or infinite"))?;
144 }
145
146 Ok($name(value))
147 }
148 }
149 impl UnitType for $name {
150 fn new(value: f64) -> Self {
152 Self::new(value)
153 }
154 fn value(&self) -> f64 {
156 Self::value(&self)
157 }
158 fn is_normal(&self) -> bool {
160 Self::is_normal(&self)
161 }
162 fn is_finite(&self) -> bool {
164 Self::is_finite(&self)
165 }
166 fn abs(&self) -> Self {
168 Self::abs(&self)
169 }
170 fn max(&self, other: Self) -> Self {
172 Self::max(&self, other)
173 }
174 fn min(&self, other: Self) -> Self {
176 Self::min(&self, other)
177 }
178 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
180 Self::total_cmp(&self, other)
181 }
182 }
183 };
184}
185
186base_unit_struct!(Dimensionless);
188
189impl From<f64> for Dimensionless {
191 fn from(val: f64) -> Self {
192 Self(val)
193 }
194}
195impl From<Dimensionless> for f64 {
196 fn from(val: Dimensionless) -> Self {
197 val.0
198 }
199}
200
201impl Mul for Dimensionless {
202 type Output = Dimensionless;
203
204 fn mul(self, rhs: Self) -> Self::Output {
205 Dimensionless(self.0 * rhs.0)
206 }
207}
208
209impl Dimensionless {
210 pub fn powi(self, rhs: i32) -> Self {
212 Dimensionless(self.0.powi(rhs))
213 }
214}
215
216macro_rules! unit_struct {
218 ($name:ident) => {
219 base_unit_struct!($name);
220
221 impl std::ops::Mul<Dimensionless> for $name {
222 type Output = $name;
223 fn mul(self, rhs: Dimensionless) -> $name {
224 $name(self.0 * rhs.0)
225 }
226 }
227 impl std::ops::Mul<$name> for Dimensionless {
228 type Output = $name;
229 fn mul(self, rhs: $name) -> $name {
230 $name(self.0 * rhs.0)
231 }
232 }
233 impl std::ops::Div<Dimensionless> for $name {
234 type Output = $name;
235 fn div(self, rhs: Dimensionless) -> $name {
236 $name(self.0 / rhs.0)
237 }
238 }
239 };
240}
241
242unit_struct!(Money);
244unit_struct!(Flow);
245unit_struct!(Activity);
246unit_struct!(Capacity);
247unit_struct!(Year);
248
249unit_struct!(MoneyPerYear);
251unit_struct!(MoneyPerFlow);
252unit_struct!(MoneyPerCapacity);
253unit_struct!(MoneyPerCapacityPerYear);
254unit_struct!(MoneyPerActivity);
255unit_struct!(ActivityPerCapacity);
256unit_struct!(FlowPerActivity);
257unit_struct!(FlowPerCapacity);
258
259macro_rules! impl_div {
260 ($Lhs:ident, $Rhs:ident, $Out:ident) => {
261 impl std::ops::Div<$Rhs> for $Lhs {
262 type Output = $Out;
263 fn div(self, rhs: $Rhs) -> $Out {
264 $Out(self.0 / rhs.0)
265 }
266 }
267 impl std::ops::Div<$Out> for $Lhs {
268 type Output = $Rhs;
269 fn div(self, rhs: $Out) -> $Rhs {
270 $Rhs(self.0 / rhs.0)
271 }
272 }
273 impl std::ops::Mul<$Rhs> for $Out {
274 type Output = $Lhs;
275 fn mul(self, by: $Rhs) -> $Lhs {
276 $Lhs(self.0 * by.0)
277 }
278 }
279 impl std::ops::Mul<$Lhs> for $Out {
280 type Output = $Rhs;
281 fn mul(self, by: $Lhs) -> $Rhs {
282 $Rhs(self.0 * by.0)
283 }
284 }
285 impl std::ops::Mul<$Out> for $Rhs {
286 type Output = $Lhs;
287 fn mul(self, by: $Out) -> $Lhs {
288 $Lhs(self.0 * by.0)
289 }
290 }
291 impl std::ops::Mul<$Out> for $Lhs {
292 type Output = $Rhs;
293 fn mul(self, by: $Out) -> $Rhs {
294 $Rhs(self.0 * by.0)
295 }
296 }
297 };
298}
299
300impl_div!(Flow, Activity, FlowPerActivity);
302impl_div!(Flow, Capacity, FlowPerCapacity);
303impl_div!(Money, Year, MoneyPerYear);
304impl_div!(Money, Flow, MoneyPerFlow);
305impl_div!(Money, Capacity, MoneyPerCapacity);
306impl_div!(Money, Activity, MoneyPerActivity);
307impl_div!(Activity, Capacity, ActivityPerCapacity);
308impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
309impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
310impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
311impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);