Skip to main content

quantities/
si_prefixes.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2023 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/si_prefixes.rs $
8// $Revision: 2026-02-01T17:50:55+01:00 $
9
10use ::qty_macros::EnumIter;
11
12/// Enum of unit prefixes defined for the System of Units (SI).
13///
14/// These prefixes can be added to unit names to name multiples and submultiples
15/// of the original unit.
16#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumIter)]
17pub enum SIPrefix {
18    /// 10⁻³⁰
19    QUECTO = -30,
20    /// 10⁻²⁷
21    RONTO = -27,
22    /// 10⁻²⁴
23    YOCTO = -24,
24    /// 10⁻²¹
25    ZEPTO = -21,
26    /// 10⁻¹⁸
27    ATTO = -18,
28    /// 10⁻¹⁵
29    FEMTO = -15,
30    /// 10⁻¹²
31    PICO = -12,
32    /// 10⁻⁹
33    NANO = -9,
34    /// 10⁻⁶
35    MICRO = -6,
36    /// 10⁻³
37    MILLI = -3,
38    /// 10⁻²
39    CENTI = -2,
40    /// 10⁻¹
41    DECI = -1,
42    /// 10⁰
43    NONE = 0,
44    /// 10¹
45    DECA = 1,
46    /// 10²
47    HECTO = 2,
48    /// 10³
49    KILO = 3,
50    /// 10⁶
51    MEGA = 6,
52    /// 10⁹
53    GIGA = 9,
54    /// 10¹²
55    TERA = 12,
56    /// 10¹⁵
57    PETA = 15,
58    /// 10¹⁸
59    EXA = 18,
60    /// 10²¹
61    ZETTA = 21,
62    /// 10²⁴
63    YOTTA = 24,
64    /// 10²⁷
65    RONNA = 27,
66    /// 10³⁰
67    QUETTA = 30,
68}
69
70impl SIPrefix {
71    /// Returns the name of `self`.
72    #[must_use]
73    pub const fn name(&self) -> &'static str {
74        match self {
75            Self::QUECTO => "Quecto",
76            Self::RONTO => "Ronto",
77            Self::YOCTO => "Yocto",
78            Self::ZEPTO => "Zepto",
79            Self::ATTO => "Atto",
80            Self::FEMTO => "Femto",
81            Self::PICO => "Pico",
82            Self::NANO => "Nano",
83            Self::MICRO => "Micro",
84            Self::MILLI => "Milli",
85            Self::CENTI => "Centi",
86            Self::DECI => "Deci",
87            Self::NONE => "",
88            Self::DECA => "Deca",
89            Self::HECTO => "Hecto",
90            Self::KILO => "Kilo",
91            Self::MEGA => "Mega",
92            Self::GIGA => "Giga",
93            Self::TERA => "Tera",
94            Self::PETA => "Peta",
95            Self::EXA => "Exa",
96            Self::ZETTA => "Zetta",
97            Self::YOTTA => "Yotta",
98            Self::RONNA => "Ronna",
99            Self::QUETTA => "Quetta",
100        }
101    }
102
103    /// Returns the abbreviation used to represent `self`.
104    #[must_use]
105    pub const fn abbr(&self) -> &'static str {
106        match self {
107            Self::QUECTO => "q",
108            Self::RONTO => "r",
109            Self::YOCTO => "y",
110            Self::ZEPTO => "z",
111            Self::ATTO => "a",
112            Self::FEMTO => "f",
113            Self::PICO => "p",
114            Self::NANO => "n",
115            Self::MICRO => "µ",
116            Self::MILLI => "m",
117            Self::CENTI => "c",
118            Self::DECI => "d",
119            Self::NONE => "",
120            Self::DECA => "da",
121            Self::HECTO => "h",
122            Self::KILO => "k",
123            Self::MEGA => "M",
124            Self::GIGA => "G",
125            Self::TERA => "T",
126            Self::PETA => "P",
127            Self::EXA => "E",
128            Self::ZETTA => "Z",
129            Self::YOTTA => "Y",
130            Self::RONNA => "R",
131            Self::QUETTA => "Q",
132        }
133    }
134
135    /// Returns the exponent of base 10 represented by `self`.
136    #[inline(always)]
137    #[must_use]
138    pub const fn exp(&self) -> i8 {
139        *self as i8
140    }
141
142    /// Returns the SI prefix with the abbreviation `abbr`, or `None` if there
143    /// is no such SI prefix.
144    #[must_use]
145    pub fn from_abbr(abbr: &str) -> Option<Self> {
146        match abbr {
147            "q" => Some(Self::QUECTO),
148            "r" => Some(Self::RONTO),
149            "y" => Some(Self::YOCTO),
150            "z" => Some(Self::ZEPTO),
151            "a" => Some(Self::ATTO),
152            "f" => Some(Self::FEMTO),
153            "p" => Some(Self::PICO),
154            "n" => Some(Self::NANO),
155            "µ" => Some(Self::MICRO),
156            "m" => Some(Self::MILLI),
157            "c" => Some(Self::CENTI),
158            "d" => Some(Self::DECI),
159            "" => Some(Self::NONE),
160            "da" => Some(Self::DECA),
161            "h" => Some(Self::HECTO),
162            "k" => Some(Self::KILO),
163            "M" => Some(Self::MEGA),
164            "G" => Some(Self::GIGA),
165            "T" => Some(Self::TERA),
166            "P" => Some(Self::PETA),
167            "E" => Some(Self::EXA),
168            "Z" => Some(Self::ZETTA),
169            "Y" => Some(Self::YOTTA),
170            "R" => Some(Self::RONNA),
171            "Q" => Some(Self::QUETTA),
172            _ => None,
173        }
174    }
175
176    /// Returns the SI prefix with the exponent `exp`, or `None` if there is no
177    /// such SI prefix.
178    #[must_use]
179    pub const fn from_exp(exp: i8) -> Option<Self> {
180        match exp {
181            -30 => Some(Self::QUECTO),
182            -27 => Some(Self::RONTO),
183            -24 => Some(Self::YOCTO),
184            -21 => Some(Self::ZEPTO),
185            -18 => Some(Self::ATTO),
186            -15 => Some(Self::FEMTO),
187            -12 => Some(Self::PICO),
188            -9 => Some(Self::NANO),
189            -6 => Some(Self::MICRO),
190            -3 => Some(Self::MILLI),
191            -2 => Some(Self::CENTI),
192            -1 => Some(Self::DECI),
193            0 => Some(Self::NONE),
194            1 => Some(Self::DECA),
195            2 => Some(Self::HECTO),
196            3 => Some(Self::KILO),
197            6 => Some(Self::MEGA),
198            9 => Some(Self::GIGA),
199            12 => Some(Self::TERA),
200            15 => Some(Self::PETA),
201            18 => Some(Self::EXA),
202            21 => Some(Self::ZETTA),
203            24 => Some(Self::YOTTA),
204            27 => Some(Self::RONNA),
205            30 => Some(Self::QUETTA),
206            _ => None,
207        }
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[allow(clippy::cognitive_complexity)]
216    #[test]
217    fn test_iter() {
218        let mut it = SIPrefix::iter();
219        assert_eq!(it.next(), Some(&SIPrefix::QUECTO));
220        assert_eq!(it.next(), Some(&SIPrefix::RONTO));
221        assert_eq!(it.next(), Some(&SIPrefix::YOCTO));
222        assert_eq!(it.next(), Some(&SIPrefix::ZEPTO));
223        assert_eq!(it.next(), Some(&SIPrefix::ATTO));
224        assert_eq!(it.next(), Some(&SIPrefix::FEMTO));
225        assert_eq!(it.next(), Some(&SIPrefix::PICO));
226        assert_eq!(it.next(), Some(&SIPrefix::NANO));
227        assert_eq!(it.next(), Some(&SIPrefix::MICRO));
228        assert_eq!(it.next(), Some(&SIPrefix::MILLI));
229        assert_eq!(it.next(), Some(&SIPrefix::CENTI));
230        assert_eq!(it.next(), Some(&SIPrefix::DECI));
231        assert_eq!(it.next(), Some(&SIPrefix::NONE));
232        assert_eq!(it.next(), Some(&SIPrefix::DECA));
233        assert_eq!(it.next(), Some(&SIPrefix::HECTO));
234        assert_eq!(it.next(), Some(&SIPrefix::KILO));
235        assert_eq!(it.next(), Some(&SIPrefix::MEGA));
236        assert_eq!(it.next(), Some(&SIPrefix::GIGA));
237        assert_eq!(it.next(), Some(&SIPrefix::TERA));
238        assert_eq!(it.next(), Some(&SIPrefix::PETA));
239        assert_eq!(it.next(), Some(&SIPrefix::EXA));
240        assert_eq!(it.next(), Some(&SIPrefix::ZETTA));
241        assert_eq!(it.next(), Some(&SIPrefix::YOTTA));
242        assert_eq!(it.next(), Some(&SIPrefix::RONNA));
243        assert_eq!(it.next(), Some(&SIPrefix::QUETTA));
244        assert_eq!(it.next(), None);
245    }
246
247    #[test]
248    fn test_si_prefix_attrs() {
249        let m = SIPrefix::MILLI;
250        assert_eq!(m.name(), "Milli");
251        assert_eq!(m.abbr(), "m");
252        assert_eq!(m.exp(), -3);
253    }
254
255    #[test]
256    fn test_from_abbr() {
257        assert_eq!(SIPrefix::from_abbr("M").unwrap(), SIPrefix::MEGA);
258        assert_eq!(SIPrefix::from_abbr("µ").unwrap(), SIPrefix::MICRO);
259        assert_eq!(SIPrefix::from_abbr("Z").unwrap(), SIPrefix::ZETTA);
260        assert!(SIPrefix::from_abbr("x").is_none());
261    }
262
263    #[test]
264    fn test_from_exp() {
265        assert_eq!(SIPrefix::from_exp(-18).unwrap(), SIPrefix::ATTO);
266        assert_eq!(SIPrefix::from_exp(0).unwrap(), SIPrefix::NONE);
267        assert_eq!(SIPrefix::from_exp(9).unwrap(), SIPrefix::GIGA);
268        assert!(SIPrefix::from_exp(7).is_none());
269    }
270}