si_scale/
prefix.rs

1//! The prefix is added to the unit string representation, such as `µ` in `µs`,
2//! and requires the mantissa to be scaled accordingly.
3
4use std::convert::TryFrom;
5use std::fmt;
6use std::str::FromStr;
7
8/// Concise definition of constraints on the prefix.
9///
10/// This allows forcing the mantissa to not be scaled, or to be scaled only
11/// towards higher scales such as `k` or `M`, or towards lower scales such as
12/// `µ`.
13///
14/// You can also define your custom constraint.
15pub mod constraint {
16    use super::Prefix;
17
18    /// Specifies a constraint on the SI [`Prefix`].
19    #[derive(Debug, Clone, PartialEq, Eq)]
20    pub enum Constraint {
21        /// Allows all prefixes from `Yocto` to `Yotta`.
22        ///
23        /// Note: having a `None` variant proves ergonomic
24        /// in the context of this library.
25        None,
26        /// Only allows the `Unit` prefix, effectively preventing scaling.
27        UnitOnly,
28        /// Only allows prefixes from `Unit` to `Yotta`.
29        UnitAndAbove,
30        /// Only allows prefixes from `Yocto` to `Unit`.
31        UnitAndBelow,
32        /// Only allows the specified prefixes (should be sorted in ascending
33        /// exponent order).
34        Custom(Vec<Prefix>),
35    }
36
37    impl AsRef<Constraint> for Constraint {
38        fn as_ref(&self) -> &Self {
39            self
40        }
41    }
42}
43
44pub use constraint::Constraint;
45
46use crate::{Result, SIUnitsError};
47
48/// Represents units' [SI prefixes](https://www.bipm.org/en/measurement-units/si-prefixes).
49///
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[repr(i32)]
52pub enum Prefix {
53    /// "yocto" prefix, 1e-24
54    Yocto = -24,
55    /// "zepto" prefix, 1e-21
56    Zepto = -21,
57    /// "atto" prefix, 1e-18
58    Atto = -18,
59    /// "femto" prefix, 1e-15
60    Femto = -15,
61    /// "pico" prefix, 1e-12
62    Pico = -12,
63    /// "nano" prefix, 1e-9
64    Nano = -9,
65    /// "micro" prefix, 1e-6
66    Micro = -6,
67    /// "milli" prefix, 1e-3
68    Milli = -3,
69    /// unit prefix (empty), 1
70    Unit = 0,
71    /// "kilo" prefix, 1e3
72    Kilo = 3,
73    /// "mega" prefix, 1e6
74    Mega = 6,
75    /// "giga" prefix, 1e9
76    Giga = 9,
77    /// "tera" prefix, 1e12
78    Tera = 12,
79    /// "peta" prefix, 1e15
80    Peta = 15,
81    /// "exa" prefix, 1e18
82    Exa = 18,
83    /// "zetta" prefix, 1e21
84    Zetta = 21,
85    /// "yotta" prefix, 1e24
86    Yotta = 24,
87}
88
89impl Prefix {
90    /// Returns the exponent `e` for `base.pow(e)` to return the total
91    /// scaling factor. See [`Base::pow()`][`crate::base::Base::pow()`].
92    ///
93    /// For instance,
94    ///
95    /// - if self is `-12` ("pico"), then `exponent()` returns `-12` so that
96    ///   `Base::B1000.pow(-12)` returns the scaling factor `1e-12`.
97    ///
98    /// - if self is `3` ("kilo"), then `exponent()` returns `3` so that
99    ///   `Base::B1024.pow(3)` returns the scaling factor `1024`.
100    pub fn exponent(&self) -> i32 {
101        *self as i32
102    }
103}
104
105impl FromStr for Prefix {
106    type Err = SIUnitsError;
107
108    /// Converts a `&str` into a `Prefix` if conversion is successful,
109    /// otherwise return an `Err`.
110    ///
111    /// # Example
112    ///
113    /// ```
114    /// use std::str::FromStr;
115    /// use si_scale::prelude::Prefix;
116    /// use si_scale::Result;
117    ///
118    /// let actual= Prefix::from_str("y");
119    /// let expected = Ok(Prefix::Yocto);
120    /// assert_eq!(actual, expected);
121    /// ```
122    fn from_str(s: &str) -> Result<Self> {
123        match s {
124            "Yocto" | "yocto" | "y" => Ok(Self::Yocto),
125            "Zepto" | "zepto" | "z" => Ok(Self::Zepto),
126            "Atto" | "atto" | "a" => Ok(Self::Atto),
127            "Femto" | "femto" | "f" => Ok(Self::Femto),
128            "Pico" | "pico" | "p" => Ok(Self::Pico),
129            "Nano" | "nano" | "n" => Ok(Self::Nano),
130            "Micro" | "micro" | "µ" => Ok(Self::Micro),
131            "Milli" | "milli" | "m" => Ok(Self::Milli),
132            "Kilo" | "kilo" | "k" => Ok(Self::Kilo),
133            "Mega" | "mega" | "M" => Ok(Self::Mega),
134            "Giga" | "giga" | "G" => Ok(Self::Giga),
135            "Tera" | "tera" | "T" => Ok(Self::Tera),
136            "Peta" | "peta" | "P" => Ok(Self::Peta),
137            "Exa" | "exa" | "E" => Ok(Self::Exa),
138            "Zetta" | "zetta" | "Z" => Ok(Self::Zetta),
139            "Yotta" | "yotta" | "Y" => Ok(Self::Yotta),
140            _ => Err(SIUnitsError::ExponentParsing(s.to_string())),
141        }
142    }
143}
144
145impl From<&Prefix> for &str {
146    /// Converts a prefix into its displayable form (`&'static str`).
147    ///
148    /// # Example
149    ///
150    /// ```
151    /// use si_scale::prelude::Prefix;
152    ///
153    /// let pfx = Prefix::Tera;
154    /// let a_string = format!("value: {} {}B", 1.5, pfx);
155    ///
156    /// assert_eq!(a_string, "value: 1.5 TB");
157    /// ```
158    ///
159    fn from(prefix: &Prefix) -> &'static str {
160        match prefix {
161            Prefix::Yocto => "y",
162            Prefix::Zepto => "z",
163            Prefix::Atto => "a",
164            Prefix::Femto => "f",
165            Prefix::Pico => "p",
166            Prefix::Nano => "n",
167            Prefix::Micro => "µ",
168            Prefix::Milli => "m",
169            Prefix::Unit => "",
170            Prefix::Kilo => "k",
171            Prefix::Mega => "M",
172            Prefix::Giga => "G",
173            Prefix::Tera => "T",
174            Prefix::Peta => "P",
175            Prefix::Exa => "E",
176            Prefix::Zetta => "Z",
177            Prefix::Yotta => "Y",
178        }
179    }
180}
181
182impl fmt::Display for Prefix {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        let s: &'static str = self.into();
185        write!(f, "{}", s)
186    }
187}
188
189impl TryFrom<i32> for Prefix {
190    type Error = SIUnitsError;
191
192    /// Builds a `Prefix` from a `i32` if successful, otherwise returns a
193    /// `SIUnitsError::ExponentParsing()` error.
194    fn try_from(value: i32) -> Result<Self> {
195        match value {
196            -24 => Ok(Self::Yocto),
197            -21 => Ok(Self::Zepto),
198            -18 => Ok(Self::Atto),
199            -15 => Ok(Self::Femto),
200            -12 => Ok(Self::Pico),
201            -9 => Ok(Self::Nano),
202            -6 => Ok(Self::Micro),
203            -3 => Ok(Self::Milli),
204            0 => Ok(Self::Unit),
205            3 => Ok(Self::Kilo),
206            6 => Ok(Self::Mega),
207            9 => Ok(Self::Giga),
208            12 => Ok(Self::Tera),
209            15 => Ok(Self::Peta),
210            18 => Ok(Self::Exa),
211            21 => Ok(Self::Zetta),
212            24 => Ok(Self::Yotta),
213            _ => Err(SIUnitsError::ExponentParsing(format!(
214                "Provided value should be a multiple of 3, between -24 and 24, got `{}` instead",
215                value
216            ))),
217        }
218    }
219}
220
221// Currently commented out because not really useful.
222//
223// macro_rules! impl_try_from_num_for_siprefix {
224//     ($t:ty) => {
225//         impl TryFrom<$t> for Prefix {
226//             type Error = SIUnitsError;
227
228//             fn try_from(value: $t) -> Result<Self> {
229//                 Prefix::try_from(value as i32)
230//             }
231//         }
232//     };
233// }
234
235// impl_try_from_num_for_siprefix!(u8);
236// impl_try_from_num_for_siprefix!(i8);
237// impl_try_from_num_for_siprefix!(u16);
238// impl_try_from_num_for_siprefix!(i16);
239// impl_try_from_num_for_siprefix!(u32);
240// // impl_try_from_num_for_siprefix!(i32);
241// impl_try_from_num_for_siprefix!(u64);
242// impl_try_from_num_for_siprefix!(i64);
243// impl_try_from_num_for_siprefix!(usize);
244// impl_try_from_num_for_siprefix!(isize);
245// impl_try_from_num_for_siprefix!(f32);
246// impl_try_from_num_for_siprefix!(f64);