Skip to content

Commit eaaf3bc

Browse files
authored
Merge pull request #29 from joeroback/serde_improvements
serde improvements
2 parents db44636 + c7a785b commit eaaf3bc

File tree

4 files changed

+108
-10
lines changed

4 files changed

+108
-10
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Cargo.lock
22
target
33
.idea
4+
.vscode

Cargo.toml

+10-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ keywords = ["byte", "byte-size", "utility", "human-readable", "format"]
1212
license = "Apache-2.0"
1313

1414
[dependencies]
15-
serde = { version = "1.0", optional = true, features = ["derive"] }
15+
serde = { version = "1.0", optional = true }
16+
17+
[dev-dependencies]
18+
serde = { version = "1.0", features = ["derive"] }
19+
serde_json = "1.0"
20+
toml = "0.5"
21+
22+
[features]
23+
default = []
24+
serde = ["dep:serde"]

src/lib.rs

+95-7
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
mod parse;
3131

3232
#[cfg(feature = "serde")]
33-
#[macro_use]
3433
extern crate serde;
34+
#[cfg(feature = "serde")]
35+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
36+
#[cfg(feature = "serde")]
37+
use std::convert::TryFrom;
3538

36-
use std::fmt::{Debug, Display, Formatter, Result};
39+
use std::fmt::{self, Debug, Display, Formatter};
3740
use std::ops::{Add, AddAssign, Mul, MulAssign};
3841

3942
/// byte size for 1 byte
@@ -107,7 +110,6 @@ pub fn pib<V: Into<u64>>(size: V) -> u64 {
107110

108111
/// Byte size representation
109112
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
110-
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
111113
pub struct ByteSize(pub u64);
112114

113115
impl ByteSize {
@@ -206,13 +208,13 @@ pub fn to_string(bytes: u64, si_prefix: bool) -> String {
206208
}
207209

208210
impl Display for ByteSize {
209-
fn fmt(&self, f: &mut Formatter) -> Result {
211+
fn fmt(&self, f: &mut Formatter) ->fmt::Result {
210212
f.pad(&to_string(self.0, false))
211213
}
212214
}
213215

214216
impl Debug for ByteSize {
215-
fn fmt(&self, f: &mut Formatter) -> Result {
217+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
216218
write!(f, "{}", self)
217219
}
218220
}
@@ -292,6 +294,70 @@ impl<T> MulAssign<T> for ByteSize
292294
}
293295
}
294296

297+
#[cfg(feature = "serde")]
298+
impl<'de> Deserialize<'de> for ByteSize {
299+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300+
where
301+
D: Deserializer<'de>,
302+
{
303+
struct ByteSizeVistor;
304+
305+
impl<'de> de::Visitor<'de> for ByteSizeVistor {
306+
type Value = ByteSize;
307+
308+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
309+
formatter.write_str("an integer or string")
310+
}
311+
312+
fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
313+
if let Ok(val) = u64::try_from(value) {
314+
Ok(ByteSize(val))
315+
} else {
316+
Err(E::invalid_value(
317+
de::Unexpected::Signed(value),
318+
&"integer overflow",
319+
))
320+
}
321+
}
322+
323+
fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
324+
Ok(ByteSize(value))
325+
}
326+
327+
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
328+
if let Ok(val) = value.parse() {
329+
Ok(val)
330+
} else {
331+
Err(E::invalid_value(
332+
de::Unexpected::Str(value),
333+
&"parsable string",
334+
))
335+
}
336+
}
337+
}
338+
339+
if deserializer.is_human_readable() {
340+
deserializer.deserialize_any(ByteSizeVistor)
341+
} else {
342+
deserializer.deserialize_u64(ByteSizeVistor)
343+
}
344+
}
345+
}
346+
347+
#[cfg(feature = "serde")]
348+
impl Serialize for ByteSize {
349+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
350+
where
351+
S: Serializer,
352+
{
353+
if serializer.is_human_readable() {
354+
<str>::serialize(self.to_string().as_str(), serializer)
355+
} else {
356+
self.0.serialize(serializer)
357+
}
358+
}
359+
}
360+
295361
#[cfg(test)]
296362
mod tests {
297363
use super::*;
@@ -327,8 +393,8 @@ mod tests {
327393

328394
x += MB as u64;
329395
x += MB as u32;
330-
x += 10 as u16;
331-
x += 1 as u8;
396+
x += 10u16;
397+
x += 1u8;
332398
assert_eq!(x.as_u64(), 3_000_011);
333399
}
334400

@@ -412,4 +478,26 @@ mod tests {
412478
fn test_to_string() {
413479
assert_to_string("609.0 PB", ByteSize::pb(609), false);
414480
}
481+
482+
#[cfg(feature = "serde")]
483+
#[test]
484+
fn test_serde() {
485+
#[derive(Serialize, Deserialize)]
486+
struct S {
487+
x: ByteSize,
488+
}
489+
490+
let s: S = serde_json::from_str(r#"{ "x": "5 B" }"#).unwrap();
491+
assert_eq!(s.x, ByteSize(5));
492+
493+
let s: S = serde_json::from_str(r#"{ "x": 1048576 }"#).unwrap();
494+
assert_eq!(s.x, "1 MiB".parse::<ByteSize>().unwrap());
495+
496+
let s: S = toml::from_str(r#"x = "2.5 MiB""#).unwrap();
497+
assert_eq!(s.x, "2.5 MiB".parse::<ByteSize>().unwrap());
498+
499+
// i64 MAX
500+
let s: S = toml::from_str(r#"x = "9223372036854775807""#).unwrap();
501+
assert_eq!(s.x, "9223372036854775807".parse::<ByteSize>().unwrap());
502+
}
415503
}

src/parse.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ impl std::str::FromStr for ByteSize {
99
}
1010
let number: String = value
1111
.chars()
12-
.take_while(|c| c.is_digit(10) || c == &'.')
12+
.take_while(|c| c.is_ascii_digit() || c == &'.')
1313
.collect();
1414
match number.parse::<f64>() {
1515
Ok(v) => {
1616
let suffix: String = value
1717
.chars()
18-
.skip_while(|c| c.is_whitespace() || c.is_digit(10) || c == &'.')
18+
.skip_while(|c| c.is_whitespace() || c.is_ascii_digit() || c == &'.')
1919
.collect();
2020
match suffix.parse::<Unit>() {
2121
Ok(u) => Ok(Self((v * u) as u64)),

0 commit comments

Comments
 (0)