1pub trait Quantity:
8 std::ops::Add<Output = Self>
9 + std::ops::Sub<Output = Self>
10 + std::ops::Mul<Percentage, Output = Self>
11 + std::ops::Mul<f32, Output = Self>
12 + std::ops::Div<f32, Output = Self>
13 + std::ops::Div<Self, Output = f32>
14 + std::cmp::PartialOrd
15 + std::fmt::Display
16 + Copy
17 + Clone
18 + std::fmt::Debug
19 + Default
20 + Sized
21 + Send
22 + Sync
23{
24 const MIN: Self;
25 const MAX: Self;
26
27 fn zero() -> Self {
28 Self::default()
29 }
30
31 fn abs(self) -> Self;
32 fn floor(self) -> Self;
33 fn ceil(self) -> Self;
34 fn round(self) -> Self;
35 fn trunc(self) -> Self;
36 fn fract(self) -> Self;
37 fn is_nan(self) -> bool;
38 fn is_infinite(self) -> bool;
39 fn min(self, other: Self) -> Self;
40 fn max(self, other: Self) -> Self;
41}
42
43impl std::ops::Mul<Percentage> for f32 {
44 type Output = f32;
45
46 fn mul(self, other: Percentage) -> Self::Output {
47 self * other.as_fraction()
48 }
49}
50
51impl Quantity for f32 {
52 const MIN: Self = f32::MIN;
53 const MAX: Self = f32::MAX;
54
55 fn abs(self) -> Self {
56 self.abs()
57 }
58
59 fn floor(self) -> Self {
60 self.floor()
61 }
62
63 fn ceil(self) -> Self {
64 self.ceil()
65 }
66
67 fn round(self) -> Self {
68 self.round()
69 }
70
71 fn trunc(self) -> Self {
72 self.trunc()
73 }
74
75 fn fract(self) -> Self {
76 self.fract()
77 }
78
79 fn is_nan(self) -> bool {
80 self.is_nan()
81 }
82
83 fn is_infinite(self) -> bool {
84 self.is_infinite()
85 }
86
87 fn min(self, other: Self) -> Self {
88 self.min(other)
89 }
90
91 fn max(self, other: Self) -> Self {
92 self.max(other)
93 }
94}
95
96fn format_float(value: f32, precision: usize) -> String {
98 let mut s = format!("{:.1$}", value, precision);
99 if s.contains('.') {
100 s = s.trim_end_matches('0').to_string();
101 }
102 if s.ends_with('.') {
103 s.pop();
104 }
105 s
106}
107
108macro_rules! qty_format {
109 (@impl $self:ident, $f:ident, $prec:ident,
110 ($ctor:ident, $getter:ident, $unit:literal, $exp:literal),
111 ) => {
112 write!($f, "{} {}", format_float( $self.$getter(), $prec), $unit)
113 };
114
115 (@impl $self:ident, $f:ident, $prec:ident,
116 ($ctor1:ident, $getter1:ident, $unit1:literal, $exp1:literal),
117 ($ctor2:ident, $getter2:ident, $unit2:literal, $exp2:literal), $($rest:tt)*
118 ) => {{
119 const {assert!($exp1 < $exp2, "Units must be in increasing order of magnitude.")};
120
121 if $exp1 <= $self.value.abs() && $self.value.abs() < $exp2 {
122 write!($f, "{} {}", format_float( $self.$getter1(), $prec), $unit1)
123 } else {
124 qty_format!(@impl $self, $f, $prec, ($ctor2, $getter2, $unit2, $exp2), $($rest)*)
125 }}
126 };
127
128 (@impl $self:ident, $f:ident, $prec:ident,
129 ($ctor1:ident, $getter1:ident, $unit1:literal, $exp1:literal),
130 ($ctor2:ident, $getter2:ident, None, $exp2:literal),
131 ) => {
132 write!($f, "{} {}", format_float( $self.$getter1(), $prec), $unit1)
133 };
134
135 (@start $self:ident, $f:ident, $prec:ident,
136 ($ctor:ident, $getter:ident, $unit:literal, $exp:literal), $($rest:tt)*
137 ) => {
138 if $self.value.abs() <= $exp {
139 write!($f, "{} {}", format_float( $self.$getter(), $prec), $unit)
140 } else {
141 qty_format!(@impl $self, $f, $prec, ($ctor, $getter, $unit, $exp), $($rest)*)
142 }
143 };
144
145 ($typename:ident => {$($rest:tt)*}) => {
146 use super::format_float;
147 impl std::fmt::Display for $typename {
148 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
149 let prec = if let Some(prec) = f.precision() {
150 prec
151 } else {
152 3
153 };
154 qty_format!(@start self, f, prec, $($rest)*)
155 }
156
157 }
158 };
159}
160
161macro_rules! qty_ctor {
162 (@impl ($ctor:ident, $getter:ident, $unit:tt, $exp:literal) $(,)?) => {
163 pub const fn $ctor(value: f32) -> Self {
164 Self { value: value * $exp }
165 }
166 pub const fn $getter(&self) -> f32 {
167 self.value / $exp
168 }
169 };
170 (@impl ($ctor:ident, $getter:ident, $unit:tt, $exp:literal), $($rest:tt)*) => {
171 qty_ctor!(@impl ($ctor, $getter, $unit, $exp));
172 qty_ctor!(@impl $($rest)*);
173 };
174 (@impl_arith_ops $typename:ident) => {
175 impl std::ops::Add for $typename {
176 type Output = Self;
177
178 fn add(self, rhs: Self) -> Self::Output {
179 Self {
180 value: self.value + rhs.value,
181 }
182 }
183 }
184
185 impl std::ops::Sub for $typename {
186 type Output = Self;
187
188 fn sub(self, rhs: Self) -> Self::Output {
189 Self {
190 value: self.value - rhs.value,
191 }
192 }
193 }
194
195 impl std::ops::Mul<super::Percentage> for $typename {
196 type Output = Self;
197
198 fn mul(self, other: super::Percentage) -> Self::Output {
199 Self {
200 value: self.value * other.as_fraction(),
201 }
202 }
203 }
204
205 impl std::ops::Mul<f32> for $typename {
206 type Output = Self;
207
208 fn mul(self, other: f32) -> Self::Output {
209 Self {
210 value: self.value * other,
211 }
212 }
213 }
214
215 impl std::ops::Div<f32> for $typename {
216 type Output = Self;
217
218 fn div(self, other: f32) -> Self::Output {
219 Self {
220 value: self.value / other,
221 }
222 }
223 }
224
225 impl std::ops::Div<$typename> for $typename {
226 type Output = f32;
227
228 fn div(self, other: Self) -> Self::Output {
229 self.value / other.value
230 }
231 }
232
233 impl std::cmp::PartialOrd for $typename {
234 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
235 self.value.partial_cmp(&other.value)
236 }
237 }
238
239 };
240 (#[$meta:meta] $typename:ident => {$($rest:tt)*}) => {
241 #[$meta]
242 #[derive(Copy, Clone, Debug, Default, PartialEq)]
243 pub struct $typename {
244 value: f32,
245 }
246
247 impl $typename {
248 qty_ctor!(@impl $($rest)*);
249
250 pub const fn abs(self) -> Self {
251 Self {
252 value: self.value.abs(),
253 }
254 }
255
256 pub const fn floor(self) -> Self {
257 Self {
258 value: self.value.floor(),
259 }
260 }
261
262 pub const fn ceil(self) -> Self {
263 Self {
264 value: self.value.ceil(),
265 }
266 }
267
268 pub const fn round(self) -> Self {
269 Self {
270 value: self.value.round(),
271 }
272 }
273
274 pub const fn trunc(self) -> Self {
275 Self {
276 value: self.value.trunc(),
277 }
278 }
279
280 pub const fn fract(self) -> Self {
281 Self {
282 value: self.value.fract(),
283 }
284 }
285
286 pub const fn is_nan(self) -> bool {
287 self.value.is_nan()
288 }
289
290 pub const fn is_infinite(self) -> bool {
291 self.value.is_infinite()
292 }
293
294 pub const fn min(self, other: Self) -> Self {
295 Self {
296 value: self.value.min(other.value),
297 }
298 }
299
300 pub const fn max(self, other: Self) -> Self {
301 Self {
302 value: self.value.max(other.value),
303 }
304 }
305 }
306
307 qty_ctor!{@impl_arith_ops $typename}
308 qty_format!{$typename => {$($rest)*}}
309
310 impl super::Quantity for $typename {
311 const MIN: Self = Self { value: f32::MIN };
312 const MAX: Self = Self { value: f32::MAX };
313
314 fn abs(self) -> Self {
315 self.abs()
316 }
317
318 fn floor(self) -> Self {
319 self.floor()
320 }
321
322 fn ceil(self) -> Self {
323 self.ceil()
324 }
325
326 fn round(self) -> Self {
327 self.round()
328 }
329
330 fn trunc(self) -> Self {
331 self.trunc()
332 }
333
334 fn fract(self) -> Self {
335 self.fract()
336 }
337
338 fn is_nan(self) -> bool {
339 self.is_nan()
340 }
341
342 fn is_infinite(self) -> bool {
343 self.is_infinite()
344 }
345
346 fn min(self, other: Self) -> Self {
347 self.min(other)
348 }
349
350 fn max(self, other: Self) -> Self {
351 self.max(other)
352 }
353 }
354 };
355}
356
357mod current;
358mod energy;
359mod frequency;
360mod percentage;
361mod power;
362mod reactive_power;
363mod voltage;
364
365pub use current::Current;
366pub use energy::Energy;
367pub use frequency::Frequency;
368pub use percentage::Percentage;
369pub use power::Power;
370pub use reactive_power::ReactivePower;
371pub use voltage::Voltage;
372
373#[cfg(test)]
374mod test_utils {
375 #[track_caller]
377 pub(crate) fn assert_f32_eq(a: f32, b: f32) {
378 let epsilon: f32 = 10.0_f32.powf(a.log10().min(b.log10())) * 1e-6;
379 if (a - b).abs() > epsilon {
380 panic!(
381 "assertion failed: `(left ~= right)` (epsilon: {})\n left: `{}`,\n right: `{}`",
382 epsilon, a, b
383 );
384 }
385 }
386}