1use crate::Buffer;
2use crate::Error;
3use pastey::paste;
4
5#[cfg(feature = "serde")]
6pub use serde;
7
8macro_rules! unicode {
9 ($utf8: literal, $ascii: literal) => {
10 if cfg!(feature = "unicode") {
11 $utf8
12 } else {
13 $ascii
14 }
15 };
16}
17
18const MICRO: &str = if true { "μ" } else { "u" }unicode!("μ", "u");
19
20#[doc(hidden)]
21pub const SI_PREFIXES: [&str; 21] = [
22 "q", "r", "y", "z", "a", "f", "p", "n", MICRO, "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y",
23 "R", "Q",
24];
25
26#[doc(hidden)]
27pub const IEC_PREFIXES: [&str; 11] = [
28 "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi", "Ri", "Qi",
29];
30
31#[inline]
32fn pre_parse_str<'a>(string: &'a str, symbol: &str) -> Result<(&'a str, &'a str), Error> {
33 let string = string.trim();
34 let i = string.rfind(char::is_numeric).ok_or(Error)?;
35 let value = &string[..=i];
36 let unit = string[i + 1..].trim_start();
37 if !unit.ends_with(symbol) {
38 return Err(Error);
39 }
40 let prefix = &unit[..unit.len() - symbol.len()];
41 Ok((value, prefix))
42}
43
44pub struct FormattedUnit<'symbol, T, const N: usize> {
46 prefix: &'static str,
47 symbol: &'symbol str,
48 integer: T,
49 fraction: u8,
50}
51
52impl<'symbol, T, const N: usize> FormattedUnit<'symbol, T, N> {
53 #[doc(hidden)]
55 pub const fn new(prefix: &'static str, symbol: &'symbol str, integer: T, fraction: u8) -> Self {
56 Self {
57 prefix,
58 symbol,
59 integer,
60 fraction,
61 }
62 }
63
64 pub const fn prefix(&self) -> &'static str {
66 self.prefix
67 }
68
69 pub const fn symbol(&self) -> &'symbol str {
71 self.symbol
72 }
73
74 pub const fn integer(&self) -> T
76 where
77 T: Copy,
78 {
79 self.integer
80 }
81
82 pub const fn fraction(&self) -> u8 {
84 self.fraction
85 }
86}
87
88macro_rules! parameterize {
89 ($($uint: ident)+) => {
90 paste! {
91 $(
92 #[inline]
93 #[doc(hidden)]
94 pub fn [<unitify_ $uint _1000>]<const MIN: usize, const MAX: usize>(
95 mut value: $uint,
96 ) -> ($uint, usize) {
97 if value == 0 {
98 return (0, MIN);
99 }
100 for power_of_1000 in MIN..MAX {
101 if value % 1000 != 0 {
102 return (value, power_of_1000);
103 }
104 value /= 1000;
105 }
106 (value, MAX)
107 }
108
109 #[inline]
110 #[doc(hidden)]
111 pub fn [<$uint _unit_from_str>]<const SCALE: $uint>(
112 string: &str,
113 symbol: &str,
114 prefixes: &[&str],
115 ) -> Result<$uint, Error> {
116 let (value_str, prefix_str) = pre_parse_str(string, symbol)?;
117 let power = prefixes
118 .iter()
119 .position(|prefix| *prefix == prefix_str)
120 .ok_or(Error)?;
121 let value: $uint = value_str.parse().map_err(|_| Error)?;
122 let factor = SCALE.pow(power as u32);
123 value.checked_mul(factor).ok_or(Error)
124 }
125
126 #[inline]
127 #[doc(hidden)]
128 pub fn [<unitify_ $uint _1024>]<const MIN: usize, const MAX: usize>(
129 value: $uint,
130 ) -> ($uint, usize) {
131 if value == 0 {
132 return (0, MIN);
133 }
134 for p in (0..=MAX - MIN).rev() {
135 let scale = (1024 as $uint).pow(p as u32);
136 if value % scale == 0 {
137 return (value >> (10 * p), p + MIN);
138 }
139 }
140 (value, MIN)
141 }
142
143 impl<const N: usize> core::fmt::Display for FormattedUnit<'_, $uint, N> {
144 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
145 let mut buf = Buffer::<{ N }>::new();
146 buf.[<write_ $uint>](self.integer);
147 if self.fraction != 0 {
148 buf.write_byte(b'.');
149 buf.write_byte(b'0' + self.fraction);
150 }
151 buf.write_byte(b' ');
152 buf.write_str_infallible(self.prefix);
153 buf.write_str_infallible(self.symbol);
154 f.write_str(unsafe { buf.as_str() })
155 }
156 }
157 )+
158 }
159 };
160}
161
162#[inline]
#[doc(hidden)]
pub fn unitify_u16_1000<const MIN : usize, const MAX : usize>(mut value: u16)
-> (u16, usize) {
if value == 0 { return (0, MIN); }
for power_of_1000 in MIN..MAX {
if value % 1000 != 0 { return (value, power_of_1000); }
value /= 1000;
}
(value, MAX)
}
#[inline]
#[doc(hidden)]
pub fn u16_unit_from_str<const SCALE :
u16>(string: &str, symbol: &str, prefixes: &[&str])
-> Result<u16, Error> {
let (value_str, prefix_str) = pre_parse_str(string, symbol)?;
let power =
prefixes.iter().position(|prefix|
*prefix == prefix_str).ok_or(Error)?;
let value: u16 = value_str.parse().map_err(|_| Error)?;
let factor = SCALE.pow(power as u32);
value.checked_mul(factor).ok_or(Error)
}
#[inline]
#[doc(hidden)]
pub fn unitify_u16_1024<const MIN : usize, const MAX : usize>(value: u16)
-> (u16, usize) {
if value == 0 { return (0, MIN); }
for p in (0..=MAX - MIN).rev() {
let scale = (1024 as u16).pow(p as u32);
if value % scale == 0 { return (value >> (10 * p), p + MIN); }
}
(value, MIN)
}
impl<const N : usize> core::fmt::Display for FormattedUnit<'_, u16, N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let mut buf = Buffer::<{ N }>::new();
buf.write_u16(self.integer);
if self.fraction != 0 {
buf.write_byte(b'.');
buf.write_byte(b'0' + self.fraction);
}
buf.write_byte(b' ');
buf.write_str_infallible(self.prefix);
buf.write_str_infallible(self.symbol);
f.write_str(unsafe { buf.as_str() })
}
}parameterize! { u128 u64 u32 u16 }