1#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct ParseSizeError {
7 original: String,
8 kind: ParseSizeErrorKind,
9}
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12enum ParseSizeErrorKind {
13 InvalidFormat,
14 InvalidInt(std::num::ParseIntError),
15 Overflow,
16}
17
18impl ParseSizeError {
19 fn format(original: &str) -> ParseSizeError {
20 ParseSizeError {
21 original: original.to_string(),
22 kind: ParseSizeErrorKind::InvalidFormat,
23 }
24 }
25
26 fn int(original: &str, err: std::num::ParseIntError) -> ParseSizeError {
27 ParseSizeError {
28 original: original.to_string(),
29 kind: ParseSizeErrorKind::InvalidInt(err),
30 }
31 }
32
33 fn overflow(original: &str) -> ParseSizeError {
34 ParseSizeError {
35 original: original.to_string(),
36 kind: ParseSizeErrorKind::Overflow,
37 }
38 }
39}
40
41impl std::error::Error for ParseSizeError {}
42
43impl std::fmt::Display for ParseSizeError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 use self::ParseSizeErrorKind::*;
46
47 match self.kind {
48 InvalidFormat => write!(
49 f,
50 "invalid format for size '{}', which should be a non-empty \
51 sequence of digits followed by an optional 'K', 'M' or 'G' \
52 suffix",
53 self.original
54 ),
55 InvalidInt(ref err) => write!(
56 f,
57 "invalid integer found in size '{}': {}",
58 self.original, err
59 ),
60 Overflow => write!(f, "size too big in '{}'", self.original),
61 }
62 }
63}
64
65impl From<ParseSizeError> for std::io::Error {
66 fn from(size_err: ParseSizeError) -> std::io::Error {
67 std::io::Error::new(std::io::ErrorKind::Other, size_err)
68 }
69}
70
71pub fn parse_human_readable_size(size: &str) -> Result<u64, ParseSizeError> {
80 let digits_end =
81 size.as_bytes().iter().take_while(|&b| b.is_ascii_digit()).count();
82 let digits = &size[..digits_end];
83 if digits.is_empty() {
84 return Err(ParseSizeError::format(size));
85 }
86 let value =
87 digits.parse::<u64>().map_err(|e| ParseSizeError::int(size, e))?;
88
89 let suffix = &size[digits_end..];
90 if suffix.is_empty() {
91 return Ok(value);
92 }
93 let bytes = match suffix {
94 "K" => value.checked_mul(1 << 10),
95 "M" => value.checked_mul(1 << 20),
96 "G" => value.checked_mul(1 << 30),
97 _ => return Err(ParseSizeError::format(size)),
98 };
99 bytes.ok_or_else(|| ParseSizeError::overflow(size))
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn suffix_none() {
108 let x = parse_human_readable_size("123").unwrap();
109 assert_eq!(123, x);
110 }
111
112 #[test]
113 fn suffix_k() {
114 let x = parse_human_readable_size("123K").unwrap();
115 assert_eq!(123 * (1 << 10), x);
116 }
117
118 #[test]
119 fn suffix_m() {
120 let x = parse_human_readable_size("123M").unwrap();
121 assert_eq!(123 * (1 << 20), x);
122 }
123
124 #[test]
125 fn suffix_g() {
126 let x = parse_human_readable_size("123G").unwrap();
127 assert_eq!(123 * (1 << 30), x);
128 }
129
130 #[test]
131 fn invalid_empty() {
132 assert!(parse_human_readable_size("").is_err());
133 }
134
135 #[test]
136 fn invalid_non_digit() {
137 assert!(parse_human_readable_size("a").is_err());
138 }
139
140 #[test]
141 fn invalid_overflow() {
142 assert!(parse_human_readable_size("9999999999999999G").is_err());
143 }
144
145 #[test]
146 fn invalid_suffix() {
147 assert!(parse_human_readable_size("123T").is_err());
148 }
149}