1use float_cmp::{ApproxEq, F64Margin};
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::iter::Sum;
6use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
7use std::str::FromStr;
8
9pub trait UnitType:
11 fmt::Debug
12 + Copy
13 + FromStr
14 + PartialEq
15 + PartialOrd
16 + Serialize
17 + Add
18 + Sub
19 + Div
20 + Neg
21 + Mul<Dimensionless, Output = Self>
22 + AddAssign
23 + SubAssign
24 + Sum
25 + ApproxEq<Margin = F64Margin>
26 + fmt::Display
27{
28 fn new(value: f64) -> Self;
30 fn value(&self) -> f64;
32 fn is_normal(&self) -> bool;
34 fn is_finite(&self) -> bool;
36 fn abs(&self) -> Self;
38 fn max(&self, other: Self) -> Self;
40 fn min(&self, other: Self) -> Self;
42 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
44}
45
46macro_rules! base_unit_struct {
47 ($name:ident $(, $extra_derive:path)* $(,)?) => {
48 #[derive(
50 Debug,
51 Clone,
52 Copy,
53 PartialEq,
54 PartialOrd,
55 Serialize,
56 derive_more::Add,
57 derive_more::Sub,
58 derive_more::AddAssign,
59 derive_more::SubAssign,
60 derive_more::Neg,
61 derive_more::Sum,
62 derive_more::Display,
63 derive_more::FromStr,
64 $($extra_derive,)*
65 )]
66 pub struct $name(pub f64);
67
68 impl std::ops::Div<$name> for $name {
69 type Output = Dimensionless;
70 fn div(self, rhs: $name) -> Dimensionless {
71 Dimensionless(self.0 / rhs.0)
72 }
73 }
74 impl std::ops::Mul<Dimensionless> for $name {
75 type Output = $name;
76 fn mul(self, rhs: Dimensionless) -> $name {
77 $name(self.0 * rhs.0)
78 }
79 }
80 impl std::ops::MulAssign<Dimensionless> for $name {
81 fn mul_assign(&mut self, rhs: Dimensionless) {
82 self.0 *= rhs.0;
83 }
84 }
85 impl std::ops::DivAssign<Dimensionless> for $name {
86 fn div_assign(&mut self, rhs: Dimensionless) {
87 self.0 /= rhs.0;
88 }
89 }
90 impl float_cmp::ApproxEq for $name {
91 type Margin = float_cmp::F64Margin;
92 fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
93 self.0.approx_eq(other.0, margin)
94 }
95 }
96 impl $name {
97 pub const EPSILON: $name = $name(f64::EPSILON);
99
100 pub fn new(value: f64) -> Self {
102 $name(value)
103 }
104 pub fn value(&self) -> f64 {
106 self.0
107 }
108 pub fn is_normal(&self) -> bool {
110 self.0.is_normal()
111 }
112 pub fn is_finite(&self) -> bool {
114 self.0.is_finite()
115 }
116 pub fn abs(&self) -> Self {
118 $name(self.0.abs())
119 }
120 pub fn max(&self, other: Self) -> Self {
122 Self(self.0.max(other.0))
123 }
124 pub fn min(&self, other: Self) -> Self {
126 Self(self.0.min(other.0))
127 }
128 pub fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
130 self.0.total_cmp(&other.0)
131 }
132 }
133 impl<'de> Deserialize<'de> for $name {
134 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135 where
136 D: serde::Deserializer<'de>,
137 {
138 let value = f64::deserialize(deserializer)?;
139 if !value.is_finite() {
140 Err(serde::de::Error::custom("Value cannot be NaN or infinite"))?;
141 }
142
143 Ok($name(value))
144 }
145 }
146 impl UnitType for $name {
147 fn new(value: f64) -> Self {
149 Self::new(value)
150 }
151 fn value(&self) -> f64 {
153 Self::value(&self)
154 }
155 fn is_normal(&self) -> bool {
157 Self::is_normal(&self)
158 }
159 fn is_finite(&self) -> bool {
161 Self::is_finite(&self)
162 }
163 fn abs(&self) -> Self {
165 Self::abs(&self)
166 }
167 fn max(&self, other: Self) -> Self {
169 Self::max(&self, other)
170 }
171 fn min(&self, other: Self) -> Self {
173 Self::min(&self, other)
174 }
175 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
177 Self::total_cmp(&self, other)
178 }
179 }
180 };
181}
182
183base_unit_struct!(Dimensionless, derive_more::From, derive_more::Into);
185
186impl Dimensionless {
187 pub fn powi(self, rhs: i32) -> Self {
189 Dimensionless(self.0.powi(rhs))
190 }
191}
192
193macro_rules! unit_struct {
195 ($name:ident) => {
196 base_unit_struct!($name);
197
198 impl std::ops::Mul<$name> for Dimensionless {
199 type Output = $name;
200 fn mul(self, rhs: $name) -> $name {
201 $name(self.0 * rhs.0)
202 }
203 }
204 impl std::ops::Div<Dimensionless> for $name {
205 type Output = $name;
206 fn div(self, rhs: Dimensionless) -> $name {
207 $name(self.0 / rhs.0)
208 }
209 }
210 };
211}
212
213unit_struct!(Money);
215unit_struct!(Flow);
216unit_struct!(Activity);
217unit_struct!(Capacity);
218unit_struct!(Year);
219
220unit_struct!(MoneyPerYear);
222unit_struct!(MoneyPerFlow);
223unit_struct!(MoneyPerCapacity);
224unit_struct!(MoneyPerCapacityPerYear);
225unit_struct!(MoneyPerActivity);
226unit_struct!(ActivityPerCapacity);
227unit_struct!(FlowPerActivity);
228unit_struct!(FlowPerCapacity);
229unit_struct!(CapacityPerYear);
230
231macro_rules! impl_div {
232 ($Lhs:ident, $Rhs:ident, $Out:ident) => {
233 impl std::ops::Div<$Rhs> for $Lhs {
234 type Output = $Out;
235 fn div(self, rhs: $Rhs) -> $Out {
236 $Out(self.0 / rhs.0)
237 }
238 }
239 impl std::ops::Div<$Out> for $Lhs {
240 type Output = $Rhs;
241 fn div(self, rhs: $Out) -> $Rhs {
242 $Rhs(self.0 / rhs.0)
243 }
244 }
245 impl std::ops::Mul<$Rhs> for $Out {
246 type Output = $Lhs;
247 fn mul(self, by: $Rhs) -> $Lhs {
248 $Lhs(self.0 * by.0)
249 }
250 }
251 impl std::ops::Mul<$Lhs> for $Out {
252 type Output = $Rhs;
253 fn mul(self, by: $Lhs) -> $Rhs {
254 $Rhs(self.0 * by.0)
255 }
256 }
257 impl std::ops::Mul<$Out> for $Rhs {
258 type Output = $Lhs;
259 fn mul(self, by: $Out) -> $Lhs {
260 $Lhs(self.0 * by.0)
261 }
262 }
263 impl std::ops::Mul<$Out> for $Lhs {
264 type Output = $Rhs;
265 fn mul(self, by: $Out) -> $Rhs {
266 $Rhs(self.0 * by.0)
267 }
268 }
269 };
270}
271
272impl_div!(Flow, Activity, FlowPerActivity);
274impl_div!(Flow, Capacity, FlowPerCapacity);
275impl_div!(Money, Year, MoneyPerYear);
276impl_div!(Money, Flow, MoneyPerFlow);
277impl_div!(Money, Capacity, MoneyPerCapacity);
278impl_div!(Money, Activity, MoneyPerActivity);
279impl_div!(Activity, Capacity, ActivityPerCapacity);
280impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
281impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
282impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
283impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);
284impl_div!(Capacity, Year, CapacityPerYear);