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);