]>
Commit | Line | Data |
---|---|---|
49aad941 FG |
1 | use super::ByteSize; |
2 | ||
3 | impl std::str::FromStr for ByteSize { | |
4 | type Err = String; | |
5 | ||
6 | fn from_str(value: &str) -> Result<Self, Self::Err> { | |
7 | if let Ok(v) = value.parse::<u64>() { | |
8 | return Ok(Self(v)); | |
9 | } | |
781aab86 | 10 | let number = take_while(value, |c| c.is_ascii_digit() || c == '.'); |
49aad941 FG |
11 | match number.parse::<f64>() { |
12 | Ok(v) => { | |
781aab86 FG |
13 | let suffix = skip_while(value, |c| { |
14 | c.is_whitespace() || c.is_ascii_digit() || c == '.' | |
15 | }); | |
49aad941 FG |
16 | match suffix.parse::<Unit>() { |
17 | Ok(u) => Ok(Self((v * u) as u64)), | |
18 | Err(error) => Err(format!( | |
19 | "couldn't parse {:?} into a known SI unit, {}", | |
20 | suffix, error | |
21 | )), | |
22 | } | |
23 | } | |
24 | Err(error) => Err(format!( | |
25 | "couldn't parse {:?} into a ByteSize, {}", | |
26 | value, error | |
27 | )), | |
28 | } | |
29 | } | |
30 | } | |
31 | ||
781aab86 FG |
32 | fn take_while<P>(s: &str, mut predicate: P) -> &str |
33 | where | |
34 | P: FnMut(char) -> bool, | |
35 | { | |
36 | let offset = s | |
37 | .chars() | |
38 | .take_while(|ch| predicate(*ch)) | |
39 | .map(|ch| ch.len_utf8()) | |
40 | .sum(); | |
41 | &s[..offset] | |
42 | } | |
43 | ||
44 | fn skip_while<P>(s: &str, mut predicate: P) -> &str | |
45 | where | |
46 | P: FnMut(char) -> bool, | |
47 | { | |
48 | let offset: usize = s | |
49 | .chars() | |
50 | .skip_while(|ch| predicate(*ch)) | |
51 | .map(|ch| ch.len_utf8()) | |
52 | .sum(); | |
53 | &s[(s.len() - offset)..] | |
54 | } | |
55 | ||
49aad941 FG |
56 | enum Unit { |
57 | Byte, | |
58 | // power of tens | |
59 | KiloByte, | |
60 | MegaByte, | |
61 | GigaByte, | |
62 | TeraByte, | |
63 | PetaByte, | |
64 | // power of twos | |
65 | KibiByte, | |
66 | MebiByte, | |
67 | GibiByte, | |
68 | TebiByte, | |
69 | PebiByte, | |
70 | } | |
71 | ||
72 | impl Unit { | |
73 | fn factor(&self) -> u64 { | |
74 | match self { | |
75 | Self::Byte => super::B, | |
76 | // power of tens | |
77 | Self::KiloByte => super::KB, | |
78 | Self::MegaByte => super::MB, | |
79 | Self::GigaByte => super::GB, | |
80 | Self::TeraByte => super::TB, | |
81 | Self::PetaByte => super::PB, | |
82 | // power of twos | |
83 | Self::KibiByte => super::KIB, | |
84 | Self::MebiByte => super::MIB, | |
85 | Self::GibiByte => super::GIB, | |
86 | Self::TebiByte => super::TIB, | |
87 | Self::PebiByte => super::PIB, | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
92 | mod impl_ops { | |
93 | use super::Unit; | |
94 | use std::ops; | |
95 | ||
96 | impl ops::Add<u64> for Unit { | |
97 | type Output = u64; | |
98 | ||
99 | fn add(self, other: u64) -> Self::Output { | |
100 | self.factor() + other | |
101 | } | |
102 | } | |
103 | ||
104 | impl ops::Add<Unit> for u64 { | |
105 | type Output = u64; | |
106 | ||
107 | fn add(self, other: Unit) -> Self::Output { | |
108 | self + other.factor() | |
109 | } | |
110 | } | |
111 | ||
112 | impl ops::Mul<u64> for Unit { | |
113 | type Output = u64; | |
114 | ||
115 | fn mul(self, other: u64) -> Self::Output { | |
116 | self.factor() * other | |
117 | } | |
118 | } | |
119 | ||
120 | impl ops::Mul<Unit> for u64 { | |
121 | type Output = u64; | |
122 | ||
123 | fn mul(self, other: Unit) -> Self::Output { | |
124 | self * other.factor() | |
125 | } | |
126 | } | |
127 | ||
128 | impl ops::Add<f64> for Unit { | |
129 | type Output = f64; | |
130 | ||
131 | fn add(self, other: f64) -> Self::Output { | |
132 | self.factor() as f64 + other | |
133 | } | |
134 | } | |
135 | ||
136 | impl ops::Add<Unit> for f64 { | |
137 | type Output = f64; | |
138 | ||
139 | fn add(self, other: Unit) -> Self::Output { | |
140 | other.factor() as f64 + self | |
141 | } | |
142 | } | |
143 | ||
144 | impl ops::Mul<f64> for Unit { | |
145 | type Output = f64; | |
146 | ||
147 | fn mul(self, other: f64) -> Self::Output { | |
148 | self.factor() as f64 * other | |
149 | } | |
150 | } | |
151 | ||
152 | impl ops::Mul<Unit> for f64 { | |
153 | type Output = f64; | |
154 | ||
155 | fn mul(self, other: Unit) -> Self::Output { | |
156 | other.factor() as f64 * self | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | impl std::str::FromStr for Unit { | |
162 | type Err = String; | |
163 | ||
164 | fn from_str(unit: &str) -> Result<Self, Self::Err> { | |
165 | match unit.to_lowercase().as_str() { | |
166 | "b" => Ok(Self::Byte), | |
167 | // power of tens | |
168 | "k" | "kb" => Ok(Self::KiloByte), | |
169 | "m" | "mb" => Ok(Self::MegaByte), | |
170 | "g" | "gb" => Ok(Self::GigaByte), | |
171 | "t" | "tb" => Ok(Self::TeraByte), | |
172 | "p" | "pb" => Ok(Self::PetaByte), | |
173 | // power of twos | |
174 | "ki" | "kib" => Ok(Self::KibiByte), | |
175 | "mi" | "mib" => Ok(Self::MebiByte), | |
176 | "gi" | "gib" => Ok(Self::GibiByte), | |
177 | "ti" | "tib" => Ok(Self::TebiByte), | |
178 | "pi" | "pib" => Ok(Self::PebiByte), | |
179 | _ => Err(format!("couldn't parse unit of {:?}", unit)), | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | #[cfg(test)] | |
185 | mod tests { | |
186 | use super::*; | |
187 | ||
188 | #[test] | |
189 | fn when_ok() { | |
190 | // shortcut for writing test cases | |
191 | fn parse(s: &str) -> u64 { | |
192 | s.parse::<ByteSize>().unwrap().0 | |
193 | } | |
194 | ||
195 | assert_eq!("0".parse::<ByteSize>().unwrap().0, 0); | |
196 | assert_eq!(parse("0"), 0); | |
197 | assert_eq!(parse("500"), 500); | |
198 | assert_eq!(parse("1K"), Unit::KiloByte * 1); | |
199 | assert_eq!(parse("1Ki"), Unit::KibiByte * 1); | |
200 | assert_eq!(parse("1.5Ki"), (1.5 * Unit::KibiByte) as u64); | |
201 | assert_eq!(parse("1KiB"), 1 * Unit::KibiByte); | |
202 | assert_eq!(parse("1.5KiB"), (1.5 * Unit::KibiByte) as u64); | |
203 | assert_eq!(parse("3 MB"), Unit::MegaByte * 3); | |
204 | assert_eq!(parse("4 MiB"), Unit::MebiByte * 4); | |
205 | assert_eq!(parse("6 GB"), 6 * Unit::GigaByte); | |
206 | assert_eq!(parse("4 GiB"), 4 * Unit::GibiByte); | |
207 | assert_eq!(parse("88TB"), 88 * Unit::TeraByte); | |
208 | assert_eq!(parse("521TiB"), 521 * Unit::TebiByte); | |
209 | assert_eq!(parse("8 PB"), 8 * Unit::PetaByte); | |
210 | assert_eq!(parse("8P"), 8 * Unit::PetaByte); | |
211 | assert_eq!(parse("12 PiB"), 12 * Unit::PebiByte); | |
212 | } | |
213 | ||
214 | #[test] | |
215 | fn when_err() { | |
216 | // shortcut for writing test cases | |
217 | fn parse(s: &str) -> Result<ByteSize, String> { | |
218 | s.parse::<ByteSize>() | |
219 | } | |
220 | ||
221 | assert!(parse("").is_err()); | |
222 | assert!(parse("a124GB").is_err()); | |
223 | } | |
224 | ||
225 | #[test] | |
226 | fn to_and_from_str() { | |
227 | // shortcut for writing test cases | |
228 | fn parse(s: &str) -> u64 { | |
229 | s.parse::<ByteSize>().unwrap().0 | |
230 | } | |
231 | ||
232 | assert_eq!(parse(&format!("{}", parse("128GB"))), 128 * Unit::GigaByte); | |
233 | assert_eq!( | |
234 | parse(&crate::to_string(parse("128.000 GiB"), true)), | |
235 | 128 * Unit::GibiByte | |
236 | ); | |
237 | } | |
238 | } |