Skip to content

Commit ac56d87

Browse files
committed
feat: expose Unit
1 parent 7fec802 commit ac56d87

File tree

3 files changed

+111
-23
lines changed

3 files changed

+111
-23
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Add `Unit` enum.
6+
- Add `UnitParseError` type.
7+
58
## 2.2.0
69

710
- Add `ByteSize::as_*()` methods to return equivalent sizes in KB, GiB, etc.

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ mod parse;
5555
#[cfg(feature = "serde")]
5656
mod serde;
5757

58-
pub use crate::display::Display;
59-
use crate::display::Format;
58+
pub use self::display::Display;
59+
use self::display::Format;
60+
pub use self::parse::{Unit, UnitParseError};
6061

6162
/// Number of bytes in 1 kilobyte.
6263
pub const KB: u64 = 1_000;

src/parse.rs

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use alloc::{format, string::String};
2-
use core::str;
1+
use alloc::{borrow::ToOwned as _, format, string::String};
2+
use core::{fmt, str};
33

44
use super::ByteSize;
55

@@ -50,21 +50,60 @@ where
5050
&s[(s.len() - offset)..]
5151
}
5252

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.
5469
Byte,
70+
5571
// power of tens
72+
/// Kilobyte (10^3 bytes).
5673
KiloByte,
74+
75+
/// Megabyte (10^6 bytes)
5776
MegaByte,
77+
78+
/// Gigabyte (10^9 bytes)
5879
GigaByte,
80+
81+
/// Terabyte (10^12 bytes)
5982
TeraByte,
83+
84+
/// Petabyte (10^15 bytes)
6085
PetaByte,
86+
87+
/// Exabyte (10^18 bytes)
6188
ExaByte,
89+
6290
// power of twos
91+
/// Kibibyte (2^10 bytes)
6392
KibiByte,
93+
94+
/// Mebibyte (2^20 bytes)
6495
MebiByte,
96+
97+
/// Gibibyte (2^30 bytes)
6598
GibiByte,
99+
100+
/// Tebibyte (2^40 bytes)
66101
TebiByte,
102+
103+
/// Pebibyte (2^50 bytes)
67104
PebiByte,
105+
106+
/// Exbibyte (2^60 bytes)
68107
ExbiByte,
69108
}
70109

@@ -160,30 +199,75 @@ mod impl_ops {
160199
}
161200

162201
impl str::FromStr for Unit {
163-
type Err = String;
202+
type Err = UnitParseError;
164203

165204
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))),
183244
}
184245
}
185246
}
186247

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+
187271
#[cfg(test)]
188272
mod tests {
189273
use alloc::string::ToString as _;

0 commit comments

Comments
 (0)