Skip to content

Commit 3cb1a70

Browse files
authored
Merge pull request #20 from jRimbault/from-str
Implement the FromStr trait for ByteSize
2 parents 0282291 + 3749005 commit 3cb1a70

File tree

2 files changed

+222
-2
lines changed

2 files changed

+222
-2
lines changed

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
//! assert_eq!("518 GB".to_string(), ByteSize::gb(518).to_string(false));
2828
//! ```
2929
30+
mod parse;
31+
3032
#[cfg(feature = "serde")]
3133
#[macro_use]
3234
extern crate serde;
@@ -58,8 +60,8 @@ pub const TIB: u64 = 1_099_511_627_776;
5860
/// bytes size for 1 pebibyte
5961
pub const PIB: u64 = 1_125_899_906_842_624;
6062

61-
static UNITS: &'static str = "KMGTPE";
62-
static UNITS_SI: &'static str = "kMGTPE";
63+
static UNITS: &str = "KMGTPE";
64+
static UNITS_SI: &str = "kMGTPE";
6365
static LN_KB: f64 = 6.931471806; // ln 1024
6466
static LN_KIB: f64 = 6.907755279; // ln 1000
6567

src/parse.rs

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

0 commit comments

Comments
 (0)