|
1 | | -use alloc::{format, string::String}; |
2 | | -use core::str; |
| 1 | +use alloc::{borrow::ToOwned as _, format, string::String}; |
| 2 | +use core::{fmt, str}; |
3 | 3 |
|
4 | 4 | use super::ByteSize; |
5 | 5 |
|
@@ -50,21 +50,60 @@ where |
50 | 50 | &s[(s.len() - offset)..] |
51 | 51 | } |
52 | 52 |
|
53 | | -enum Unit { |
| 53 | +/// Scale unit. |
| 54 | +/// |
| 55 | +/// ``` |
| 56 | +/// use bytesize::Unit; |
| 57 | +/// |
| 58 | +/// assert_eq!( |
| 59 | +/// "GiB".parse::<Unit>().unwrap(), |
| 60 | +/// Unit::GibiByte, |
| 61 | +/// ); |
| 62 | +/// |
| 63 | +/// "gibibyte".parse::<Unit>().unwrap_err(); |
| 64 | +/// ``` |
| 65 | +#[non_exhaustive] |
| 66 | +#[derive(Debug, Clone, PartialEq)] |
| 67 | +pub enum Unit { |
| 68 | + /// Single byte. |
54 | 69 | Byte, |
| 70 | + |
55 | 71 | // power of tens |
| 72 | + /// Kilobyte (10^3 bytes). |
56 | 73 | KiloByte, |
| 74 | + |
| 75 | + /// Megabyte (10^6 bytes) |
57 | 76 | MegaByte, |
| 77 | + |
| 78 | + /// Gigabyte (10^9 bytes) |
58 | 79 | GigaByte, |
| 80 | + |
| 81 | + /// Terabyte (10^12 bytes) |
59 | 82 | TeraByte, |
| 83 | + |
| 84 | + /// Petabyte (10^15 bytes) |
60 | 85 | PetaByte, |
| 86 | + |
| 87 | + /// Exabyte (10^18 bytes) |
61 | 88 | ExaByte, |
| 89 | + |
62 | 90 | // power of twos |
| 91 | + /// Kibibyte (2^10 bytes) |
63 | 92 | KibiByte, |
| 93 | + |
| 94 | + /// Mebibyte (2^20 bytes) |
64 | 95 | MebiByte, |
| 96 | + |
| 97 | + /// Gibibyte (2^30 bytes) |
65 | 98 | GibiByte, |
| 99 | + |
| 100 | + /// Tebibyte (2^40 bytes) |
66 | 101 | TebiByte, |
| 102 | + |
| 103 | + /// Pebibyte (2^50 bytes) |
67 | 104 | PebiByte, |
| 105 | + |
| 106 | + /// Exbibyte (2^60 bytes) |
68 | 107 | ExbiByte, |
69 | 108 | } |
70 | 109 |
|
@@ -160,30 +199,75 @@ mod impl_ops { |
160 | 199 | } |
161 | 200 |
|
162 | 201 | impl str::FromStr for Unit { |
163 | | - type Err = String; |
| 202 | + type Err = UnitParseError; |
164 | 203 |
|
165 | 204 | fn from_str(unit: &str) -> Result<Self, Self::Err> { |
166 | | - match unit.to_lowercase().as_str() { |
167 | | - "b" => Ok(Self::Byte), |
168 | | - // power of tens |
169 | | - "k" | "kb" => Ok(Self::KiloByte), |
170 | | - "m" | "mb" => Ok(Self::MegaByte), |
171 | | - "g" | "gb" => Ok(Self::GigaByte), |
172 | | - "t" | "tb" => Ok(Self::TeraByte), |
173 | | - "p" | "pb" => Ok(Self::PetaByte), |
174 | | - "e" | "eb" => Ok(Self::ExaByte), |
175 | | - // power of twos |
176 | | - "ki" | "kib" => Ok(Self::KibiByte), |
177 | | - "mi" | "mib" => Ok(Self::MebiByte), |
178 | | - "gi" | "gib" => Ok(Self::GibiByte), |
179 | | - "ti" | "tib" => Ok(Self::TebiByte), |
180 | | - "pi" | "pib" => Ok(Self::PebiByte), |
181 | | - "ei" | "eib" => Ok(Self::ExbiByte), |
182 | | - _ => Err(format!("couldn't parse unit of {unit:?}")), |
| 205 | + match () { |
| 206 | + _ if unit.eq_ignore_ascii_case("b") => Ok(Self::Byte), |
| 207 | + _ if unit.eq_ignore_ascii_case("k") | unit.eq_ignore_ascii_case("kb") => { |
| 208 | + Ok(Self::KiloByte) |
| 209 | + } |
| 210 | + _ if unit.eq_ignore_ascii_case("m") | unit.eq_ignore_ascii_case("mb") => { |
| 211 | + Ok(Self::MegaByte) |
| 212 | + } |
| 213 | + _ if unit.eq_ignore_ascii_case("g") | unit.eq_ignore_ascii_case("gb") => { |
| 214 | + Ok(Self::GigaByte) |
| 215 | + } |
| 216 | + _ if unit.eq_ignore_ascii_case("t") | unit.eq_ignore_ascii_case("tb") => { |
| 217 | + Ok(Self::TeraByte) |
| 218 | + } |
| 219 | + _ if unit.eq_ignore_ascii_case("p") | unit.eq_ignore_ascii_case("pb") => { |
| 220 | + Ok(Self::PetaByte) |
| 221 | + } |
| 222 | + _ if unit.eq_ignore_ascii_case("e") | unit.eq_ignore_ascii_case("eb") => { |
| 223 | + Ok(Self::ExaByte) |
| 224 | + } |
| 225 | + _ if unit.eq_ignore_ascii_case("ki") | unit.eq_ignore_ascii_case("kib") => { |
| 226 | + Ok(Self::KibiByte) |
| 227 | + } |
| 228 | + _ if unit.eq_ignore_ascii_case("mi") | unit.eq_ignore_ascii_case("mib") => { |
| 229 | + Ok(Self::MebiByte) |
| 230 | + } |
| 231 | + _ if unit.eq_ignore_ascii_case("gi") | unit.eq_ignore_ascii_case("gib") => { |
| 232 | + Ok(Self::GibiByte) |
| 233 | + } |
| 234 | + _ if unit.eq_ignore_ascii_case("ti") | unit.eq_ignore_ascii_case("tib") => { |
| 235 | + Ok(Self::TebiByte) |
| 236 | + } |
| 237 | + _ if unit.eq_ignore_ascii_case("pi") | unit.eq_ignore_ascii_case("pib") => { |
| 238 | + Ok(Self::PebiByte) |
| 239 | + } |
| 240 | + _ if unit.eq_ignore_ascii_case("ei") | unit.eq_ignore_ascii_case("eib") => { |
| 241 | + Ok(Self::ExbiByte) |
| 242 | + } |
| 243 | + _ => Err(UnitParseError(to_string_truncate(unit))), |
183 | 244 | } |
184 | 245 | } |
185 | 246 | } |
186 | 247 |
|
| 248 | +fn to_string_truncate(unit: &str) -> String { |
| 249 | + const MAX_UNIT_LEN: usize = 3; |
| 250 | + |
| 251 | + if unit.len() > MAX_UNIT_LEN { |
| 252 | + format!("{unit}...") |
| 253 | + } else { |
| 254 | + unit.to_owned() |
| 255 | + } |
| 256 | +} |
| 257 | + |
| 258 | +/// Error returned when parsing a [`Unit`] fails. |
| 259 | +#[derive(Debug)] |
| 260 | +pub struct UnitParseError(String); |
| 261 | + |
| 262 | +impl fmt::Display for UnitParseError { |
| 263 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 264 | + write!(f, "Failed to parse unit \"{}\"", self.0) |
| 265 | + } |
| 266 | +} |
| 267 | + |
| 268 | +#[cfg(feature = "std")] |
| 269 | +impl std::error::Error for UnitParseError {} |
| 270 | + |
187 | 271 | #[cfg(test)] |
188 | 272 | mod tests { |
189 | 273 | use alloc::string::ToString as _; |
|
0 commit comments