physical_units/
format.rs

1use core::fmt;
2
3use crate::{base, derived};
4
5impl fmt::Debug for base::BaseUnit {
6    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7        if *self == base::UNITLESS {
8            return write!(f, "BaseUnit::UNITLESS");
9        }
10
11        let components = [
12            (self.kilogram, "kilogram"),
13            (self.meter, "meter"),
14            (self.second, "second"),
15            (self.mole, "mole"),
16            (self.ampere, "ampere"),
17            (self.kelvin, "kelvin"),
18            (self.candela, "candela"),
19        ];
20
21        write!(f, "BaseUnit(")?;
22
23        let mut is_first = true;
24        for (n, symbol) in components {
25            if n != 0 {
26                if !is_first {
27                    write!(f, "⋅")?;
28                }
29                write!(f, "{symbol}{}", SignedSuperscript { n })?;
30                is_first = false;
31            }
32        }
33
34        write!(f, ")")
35    }
36}
37
38impl fmt::Display for base::BaseUnit {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        if *self == base::UNITLESS {
41            return write!(f, "Unitless");
42        }
43
44        let components = [
45            (self.kilogram, "kg"),
46            (self.meter, "m"),
47            (self.second, "s"),
48            (self.mole, "mol"),
49            (self.ampere, "A"),
50            (self.kelvin, "K"),
51            (self.candela, "cd"),
52        ];
53
54        let mut is_first = true;
55        for (n, symbol) in components.iter() {
56            let n = *n;
57            if n > 0 {
58                if !is_first {
59                    write!(f, "⋅")?;
60                }
61                write!(f, "{symbol}{}", SignedSuperscript { n })?;
62                is_first = false;
63            }
64        }
65
66        let negatives = components.iter().filter(|(n, _)| *n < 0).count();
67        if negatives != 0 {
68            write!(f, "/")?;
69
70            if negatives > 1 {
71                write!(f, "(")?;
72            }
73
74            let mut is_first = true;
75            for (n, symbol) in components.iter() {
76                let n = -*n;
77                if n > 0 {
78                    if !is_first {
79                        write!(f, "⋅")?;
80                    }
81                    write!(f, "{symbol}{}", SignedSuperscript { n })?;
82                    is_first = false;
83                }
84            }
85
86            if negatives > 1 {
87                write!(f, ")")?;
88            }
89        }
90        Ok(())
91    }
92}
93
94impl<Number> fmt::Display for base::BaseValue<Number>
95where
96    Number: fmt::Display,
97{
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        if self.unit == base::UNITLESS {
100            write!(f, "{}", self.number)
101        } else {
102            write!(f, "{} {}", self.number, self.unit)
103        }
104    }
105}
106
107impl fmt::Debug for derived::DerivedUnit {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        if *self == derived::UNITLESS {
110            return write!(f, "DerivedUnit::UNITLESS");
111        }
112
113        let components = [
114            (self.base.kilogram, "kilogram"),
115            (self.base.meter, "meter"),
116            (self.base.second, "second"),
117            (self.base.mole, "mole"),
118            (self.base.ampere, "ampere"),
119            (self.base.kelvin, "kelvin"),
120            (self.base.candela, "candela"),
121            (self.hertz, "hertz"),
122            (self.newton, "newton"),
123            (self.pascal, "pascal"),
124            (self.joule, "joule"),
125            (self.watt, "watt"),
126            (self.coulomb, "coulomb"),
127            (self.volt, "volt"),
128            (self.farad, "farad"),
129            (self.ohm, "ohm"),
130            (self.siemens, "siemens"),
131            (self.weber, "weber"),
132            (self.tesla, "tesla"),
133            (self.henry, "henry"),
134            (self.lux, "lux"),
135            (self.becquerel, "becquerel"),
136            (self.gray, "gray"),
137            (self.sievert, "sievert"),
138            (self.katal, "katal"),
139        ];
140
141        write!(f, "DerivedUnit(")?;
142
143        let mut is_first = true;
144        for (n, symbol) in components {
145            if n != 0 {
146                if !is_first {
147                    write!(f, "⋅")?;
148                }
149                write!(f, "{symbol}{}", SignedSuperscript { n })?;
150                is_first = false;
151            }
152        }
153
154        write!(f, ")")
155    }
156}
157
158impl fmt::Display for derived::DerivedUnit {
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        if *self == derived::UNITLESS {
161            return write!(f, "Unitless");
162        }
163
164        let components = [
165            (self.base.kilogram, "kg"),
166            (self.base.meter, "m"),
167            (self.base.second, "s"),
168            (self.base.mole, "mol"),
169            (self.base.ampere, "A"),
170            (self.base.kelvin, "K"),
171            (self.base.candela, "cd"),
172            (self.hertz, "Hz"),
173            (self.newton, "N"),
174            (self.pascal, "Pa"),
175            (self.joule, "J"),
176            (self.watt, "W"),
177            (self.coulomb, "C"),
178            (self.volt, "V"),
179            (self.farad, "F"),
180            (self.ohm, "Ω"),
181            (self.siemens, "S"),
182            (self.weber, "Wb"),
183            (self.tesla, "T"),
184            (self.henry, "H"),
185            (self.lux, "lx"),
186            (self.becquerel, "Bq"),
187            (self.gray, "Gy"),
188            (self.sievert, "Sv"),
189            (self.katal, "kat"),
190        ];
191
192        let mut is_first = true;
193        for (n, symbol) in components.iter() {
194            let n = *n;
195            if n > 0 {
196                if !is_first {
197                    write!(f, "⋅")?;
198                }
199                write!(f, "{symbol}{}", SignedSuperscript { n })?;
200                is_first = false;
201            }
202        }
203
204        let negatives = components.iter().filter(|(n, _)| *n < 0).count();
205        if negatives != 0 {
206            write!(f, "/")?;
207
208            if negatives > 1 {
209                write!(f, "(")?;
210            }
211
212            let mut is_first = true;
213            for (n, symbol) in components.iter() {
214                let n = -*n;
215                if n > 0 {
216                    if !is_first {
217                        write!(f, "⋅")?;
218                    }
219                    write!(f, "{symbol}{}", SignedSuperscript { n })?;
220                    is_first = false;
221                }
222            }
223
224            if negatives > 1 {
225                write!(f, ")")?;
226            }
227        }
228        Ok(())
229    }
230}
231
232impl<Number> fmt::Display for derived::DerivedValue<Number>
233where
234    Number: fmt::Display,
235{
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        if self.unit == derived::UNITLESS {
238            write!(f, "{}", self.number)
239        } else {
240            write!(f, "{} {}", self.number, self.unit)
241        }
242    }
243}
244
245struct SignedSuperscript {
246    n: i8,
247}
248
249impl fmt::Display for SignedSuperscript {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        if self.n == 0 {
252            return write!(f, "⁰");
253        }
254        if self.n == 1 {
255            return Ok(());
256        }
257
258        // Handle negatives
259        let n = if self.n < 0 {
260            write!(f, "⁻")?;
261            -self.n as u8
262        } else {
263            self.n as u8
264        };
265
266        // Numbers are in the range of 1-128
267        // so we can use a simple version of decimal conversion
268        let hundreds = n / 100; // Will always be zero or 1
269        let n = n - 100 * hundreds;
270        let tens = n / 10; // Will be 0-9
271        let n = n - 10 * tens;
272        let ones = n; // Will be 0-9
273
274        if hundreds != 0 {
275            SuperscriptDigit { digit: hundreds }.fmt(f)?;
276        }
277        if hundreds != 0 || tens != 0 {
278            SuperscriptDigit { digit: tens }.fmt(f)?;
279        }
280        SuperscriptDigit { digit: ones }.fmt(f)?;
281        Ok(())
282    }
283}
284
285struct SuperscriptDigit {
286    digit: u8,
287}
288
289impl fmt::Display for SuperscriptDigit {
290    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291        match self.digit {
292            0 => write!(f, "⁰"),
293            1 => write!(f, "¹"),
294            2 => write!(f, "²"),
295            3 => write!(f, "³"),
296            4 => write!(f, "⁴"),
297            5 => write!(f, "⁵"),
298            6 => write!(f, "⁶"),
299            7 => write!(f, "⁷"),
300            8 => write!(f, "⁸"),
301            9 => write!(f, "⁹"),
302            _ => unreachable!(),
303        }
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    #[test]
312    fn test_base_unit_display() {
313        let unit = base::BaseUnit {
314            meter: 1,
315            second: -2,
316            mole: 0,
317            ampere: 0,
318            kelvin: 0,
319            candela: 0,
320            kilogram: 1,
321        };
322        assert_eq!(String::from("kg⋅m/s²"), format!("{}", unit));
323    }
324
325    #[test]
326    fn test_base_value_display() {
327        use crate::base::SECOND;
328        // Ints
329        let one_second = base::BaseValue {
330            unit: SECOND,
331            number: 1u32,
332        };
333        assert_eq!(String::from("1 s"), format!("{}", one_second));
334        let two_seconds = base::BaseValue {
335            unit: SECOND,
336            number: 2u32,
337        };
338        assert_eq!(String::from("2 s"), format!("{}", two_seconds));
339        // Floats
340        let one_second = base::BaseValue {
341            unit: SECOND,
342            number: 1f32,
343        };
344        assert_eq!(String::from("1 s"), format!("{}", one_second));
345        let two_seconds = base::BaseValue {
346            unit: SECOND,
347            number: 2f32,
348        };
349        assert_eq!(String::from("2 s"), format!("{}", two_seconds));
350    }
351
352    #[test]
353    fn test_derived_unit_display() {
354        let unit = derived::DerivedUnit {
355            farad: 1,
356            sievert: -2,
357            ..derived::UNITLESS
358        };
359        assert_eq!(String::from("F/Sv²"), format!("{}", unit));
360    }
361
362    #[test]
363    fn test_derived_unit_debug() {
364        let unit = derived::DerivedUnit {
365            farad: 1,
366            sievert: -2,
367            ..derived::UNITLESS
368        };
369        assert_eq!(String::from("DerivedUnit(farad⋅sievert⁻²)"), format!("{:?}", unit));
370    }
371
372    #[test]
373    fn test_derived_value_display() {
374        use crate::derived::BECQUEREL;
375        // Ints
376        let one_second = derived::DerivedValue {
377            unit: BECQUEREL,
378            number: 1u32,
379        };
380        assert_eq!(String::from("1 Bq"), format!("{}", one_second));
381        let two_seconds = derived::DerivedValue {
382            unit: BECQUEREL,
383            number: 2u32,
384        };
385        assert_eq!(String::from("2 Bq"), format!("{}", two_seconds));
386        // Floats
387        let one_second = derived::DerivedValue {
388            unit: BECQUEREL,
389            number: 1f32,
390        };
391        assert_eq!(String::from("1 Bq"), format!("{}", one_second));
392        let two_seconds = derived::DerivedValue {
393            unit: BECQUEREL,
394            number: 2f32,
395        };
396        assert_eq!(String::from("2 Bq"), format!("{}", two_seconds));
397    }
398}