deserialize_file_size/
lib.rs1#![allow(clippy::single_char_pattern)] use std::fmt;
37use serde::de::{self, Visitor};
38
39pub fn parse_file_size(s: &str) -> Option<usize> {
43 let mut s = s.trim().to_string();
44 s[..].make_ascii_lowercase();
45 let s = if s.contains("ib") {
46 s
47 } else {
48 s.replace("kb", "k") .replace("mb", "m")
50 .replace("gb", "g")
51 .replace("k", "kib") .replace("m", "mib")
53 .replace("g", "gib")
54 };
55
56 let bytes: u128 = byte_unit::Byte::from_str(&s)
57 .ok()?
58 .get_bytes();
59
60 usize::try_from(bytes).ok()
61}
62
63pub fn deserialize_file_size<'de, D>(deserializer: D) -> Result<usize, D::Error>
99 where D: serde::de::Deserializer<'de>
100{
101 struct SizeStringOrUsize;
102
103 impl<'de> Visitor<'de> for SizeStringOrUsize {
104 type Value = usize;
105
106 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
107 formatter.write_str("file size string or integer number of bytes")
108 }
109
110 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
111 where
112 E: de::Error,
113 {
114 match parse_file_size(value) {
115 Some(size) => Ok(size),
116 None => {
117 Err(serde::de::Error::custom(format!(
118 "parsing file size input '{}' failed; expected number followed \
119 by human size abbreviation (e.g. 10k/5mb/15GiB) or integer number \
120 of bytes",
121 value,
122 )))
123 }
124 }
125 }
126
127 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
128 where E: de::Error,
129 {
130 Ok(value as usize)
131 }
132 }
133
134 deserializer.deserialize_any(SizeStringOrUsize)
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn check_file_size_parser_against_several_inputs() {
143 assert_eq!(parse_file_size("1k") , Some(1024));
144 assert_eq!(parse_file_size("1kib") , Some(1024));
145 assert_eq!(parse_file_size("1K") , Some(1024));
146 assert_eq!(parse_file_size("1Kb") , Some(1024));
147 assert_eq!(parse_file_size("1kb") , Some(1024));
148 assert_eq!(parse_file_size("1 kb") , Some(1024));
149 assert_eq!(parse_file_size("1 k") , Some(1024));
150 assert_eq!(parse_file_size("1 kib") , Some(1024));
151
152 assert_eq!(parse_file_size("1m") , Some(1024 * 1024));
153 assert_eq!(parse_file_size("1mib") , Some(1024 * 1024));
154 assert_eq!(parse_file_size("1M") , Some(1024 * 1024));
155 assert_eq!(parse_file_size("1Mb") , Some(1024 * 1024));
156 assert_eq!(parse_file_size("1MB") , Some(1024 * 1024));
157 assert_eq!(parse_file_size("1mb") , Some(1024 * 1024));
158 assert_eq!(parse_file_size("1 MB") , Some(1024 * 1024));
159
160 assert_eq!(parse_file_size("1g") , Some(1024 * 1024 * 1024));
161 assert_eq!(parse_file_size("1gib") , Some(1024 * 1024 * 1024));
162 assert_eq!(parse_file_size("1G") , Some(1024 * 1024 * 1024));
163 assert_eq!(parse_file_size("1Gb") , Some(1024 * 1024 * 1024));
164 assert_eq!(parse_file_size("1gb") , Some(1024 * 1024 * 1024));
165 assert_eq!(parse_file_size("1 gb") , Some(1024 * 1024 * 1024));
166 assert_eq!(parse_file_size("1 g") , Some(1024 * 1024 * 1024));
167 assert_eq!(parse_file_size("1 Gib") , Some(1024 * 1024 * 1024));
168
169 assert_eq!(parse_file_size("48G") , Some( 48 * 1024 * 1024 * 1024));
170 assert_eq!(parse_file_size("96G") , Some( 96 * 1024 * 1024 * 1024));
171 assert_eq!(parse_file_size("2G") , Some( 2 * 1024 * 1024 * 1024));
172 assert_eq!(parse_file_size("128G"), Some(128 * 1024 * 1024 * 1024));
173 }
174
175 #[test]
176 fn check_deserialize_file_size() {
177 #[derive(serde::Deserialize)]
178 struct A {
179 #[serde(deserialize_with = "deserialize_file_size")]
180 sz: usize,
181 }
182
183 assert_eq!(
184 serde_json::from_str::<A>(r#"{"sz":"1mb"}"#).unwrap().sz,
185 1024 * 1024,
186 );
187 assert_eq!(
188 serde_json::from_str::<A>(r#"{"sz":4096}"#).unwrap().sz,
189 4096,
190 );
191 }
192}