1#![forbid(unsafe_code)]
62
63use std::ops::Mul;
64
65use nom::branch::alt;
66use nom::bytes::complete::{is_not, tag};
67use nom::character::complete::{alpha0, i128, space0};
68use nom::combinator::{all_consuming, map, map_parser, map_res, opt, value};
69use nom::sequence::{delimited, tuple};
70use nom::{Finish, IResult};
71use proc_macro::TokenStream;
72use quote::quote;
73use syn::{parse_str, Error as SynError, Expr};
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76enum Unit {
77 Byte,
78 Kilobyte,
79 Kibibyte,
80 Megabyte,
81 Mebibyte,
82 Gigabyte,
83 Gibibyte,
84 Terabyte,
85 Tebibyte,
86 Petabyte,
87 Pebibyte,
88 Exabyte,
89 Exbibyte,
90 Zettabyte,
91 Zebibyte,
92 Yottabyte,
93 Yobibyte,
94}
95
96impl Unit {
97 const fn multiplier(&self) -> i128 {
98 match self {
99 Self::Byte => 1,
100 Self::Kilobyte => 1000,
101 Self::Kibibyte => 1024,
102 Self::Megabyte => 1000 * 1000,
103 Self::Mebibyte => 1024 * 1024,
104 Self::Gigabyte => 1000 * 1000 * 1000,
105 Self::Gibibyte => 1024 * 1024 * 1024,
106 Self::Terabyte => 1000 * 1000 * 1000 * 1000,
107 Self::Tebibyte => 1024 * 1024 * 1024 * 1024,
108 Self::Petabyte => 1000 * 1000 * 1000 * 1000 * 1000,
109 Self::Pebibyte => 1024 * 1024 * 1024 * 1024 * 1024,
110 Self::Exabyte => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
111 Self::Exbibyte => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
112 Self::Zettabyte => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
113 Self::Zebibyte => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
114 Self::Yottabyte => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
115 Self::Yobibyte => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
116 }
117 }
118
119 fn parse(input: &str) -> IResult<&str, Self> {
120 let mut parser = alt((
121 Self::parse_byte,
122 Self::parse_kilobyte,
123 Self::parse_kibibyte,
124 Self::parse_megabyte,
125 Self::parse_mebibyte,
126 Self::parse_gigabyte,
127 Self::parse_gibibyte,
128 Self::parse_terabyte,
129 Self::parse_tebibyte,
130 Self::parse_petabyte,
131 Self::parse_pebibyte,
132 Self::parse_exabyte,
133 Self::parse_exbibyte,
134 Self::parse_zettabyte,
135 Self::parse_zebibyte,
136 Self::parse_yottabyte,
137 Self::parse_yobibyte,
138 ));
139 parser(input)
140 }
141
142 fn parse_byte(input: &str) -> IResult<&str, Self> {
143 let parser = all_consuming(opt(tag("B")));
144 let mut parser = value(Self::Byte, parser);
145 parser(input)
146 }
147
148 fn parse_kilobyte(input: &str) -> IResult<&str, Self> {
149 let parser = alt((all_consuming(tag("K")), all_consuming(tag("KB"))));
150 let mut parser = value(Self::Kilobyte, parser);
151 parser(input)
152 }
153
154 fn parse_kibibyte(input: &str) -> IResult<&str, Self> {
155 let parser = alt((all_consuming(tag("Ki")), all_consuming(tag("KiB"))));
156 let mut parser = value(Self::Kibibyte, parser);
157 parser(input)
158 }
159
160 fn parse_megabyte(input: &str) -> IResult<&str, Self> {
161 let parser = alt((all_consuming(tag("M")), all_consuming(tag("MB"))));
162 let mut parser = value(Self::Megabyte, parser);
163 parser(input)
164 }
165
166 fn parse_mebibyte(input: &str) -> IResult<&str, Self> {
167 let parser = alt((all_consuming(tag("Mi")), all_consuming(tag("MiB"))));
168 let mut parser = value(Self::Mebibyte, parser);
169 parser(input)
170 }
171
172 fn parse_gigabyte(input: &str) -> IResult<&str, Self> {
173 let parser = alt((all_consuming(tag("G")), all_consuming(tag("GB"))));
174 let mut parser = value(Self::Gigabyte, parser);
175 parser(input)
176 }
177
178 fn parse_gibibyte(input: &str) -> IResult<&str, Self> {
179 let parser = alt((all_consuming(tag("Gi")), all_consuming(tag("GiB"))));
180 let mut parser = value(Self::Gibibyte, parser);
181 parser(input)
182 }
183
184 fn parse_terabyte(input: &str) -> IResult<&str, Self> {
185 let parser = alt((all_consuming(tag("T")), all_consuming(tag("TB"))));
186 let mut parser = value(Self::Terabyte, parser);
187 parser(input)
188 }
189
190 fn parse_tebibyte(input: &str) -> IResult<&str, Self> {
191 let parser = alt((all_consuming(tag("Ti")), all_consuming(tag("TiB"))));
192 let mut parser = value(Self::Tebibyte, parser);
193 parser(input)
194 }
195
196 fn parse_petabyte(input: &str) -> IResult<&str, Self> {
197 let parser = alt((all_consuming(tag("P")), all_consuming(tag("PB"))));
198 let mut parser = value(Self::Petabyte, parser);
199 parser(input)
200 }
201
202 fn parse_pebibyte(input: &str) -> IResult<&str, Self> {
203 let parser = alt((all_consuming(tag("Pi")), all_consuming(tag("PiB"))));
204 let mut parser = value(Self::Pebibyte, parser);
205 parser(input)
206 }
207
208 fn parse_exabyte(input: &str) -> IResult<&str, Self> {
209 let parser = alt((all_consuming(tag("E")), all_consuming(tag("EB"))));
210 let mut parser = value(Self::Exabyte, parser);
211 parser(input)
212 }
213
214 fn parse_exbibyte(input: &str) -> IResult<&str, Self> {
215 let parser = alt((all_consuming(tag("Ei")), all_consuming(tag("EiB"))));
216 let mut parser = value(Self::Exbibyte, parser);
217 parser(input)
218 }
219
220 fn parse_zettabyte(input: &str) -> IResult<&str, Self> {
221 let parser = alt((all_consuming(tag("Z")), all_consuming(tag("ZB"))));
222 let mut parser = value(Self::Zettabyte, parser);
223 parser(input)
224 }
225
226 fn parse_zebibyte(input: &str) -> IResult<&str, Self> {
227 let parser = alt((all_consuming(tag("Zi")), all_consuming(tag("ZiB"))));
228 let mut parser = value(Self::Zebibyte, parser);
229 parser(input)
230 }
231
232 fn parse_yottabyte(input: &str) -> IResult<&str, Self> {
233 let parser = alt((all_consuming(tag("Y")), all_consuming(tag("YB"))));
234 let mut parser = value(Self::Yottabyte, parser);
235 parser(input)
236 }
237
238 fn parse_yobibyte(input: &str) -> IResult<&str, Self> {
239 let parser = alt((all_consuming(tag("Yi")), all_consuming(tag("YiB"))));
240 let mut parser = value(Self::Yobibyte, parser);
241 parser(input)
242 }
243}
244
245macro_rules! impl_mul {
246 ($($t:ty),+ => $o:ty) => {
247 $(
248 impl Mul<Unit> for $t {
249 type Output = $o;
250
251 fn mul(self, rhs: Unit) -> Self::Output {
252 (self as Self::Output) * (rhs.multiplier() as Self::Output)
253 }
254 }
255 )*
256 };
257}
258impl_mul!(u8, u16, u32, u64, u128, usize => u128);
259impl_mul!(i8, i16, i32, i64, i128, isize => i128);
260
261enum Value {
262 Number(i128),
263 Expression(Expr),
264}
265
266impl Value {
267 fn parse(input: &str) -> IResult<&str, Self> {
268 let number_parser = map(i128, Self::Number);
269 let identifier_parser = {
270 let parser = delimited(tag("{"), is_not("}"), tag("}"));
271 map_res(parser, |expr| -> Result<Self, SynError> {
272 let expr = parse_str::<Expr>(expr)?;
273 let expression = Self::Expression(expr);
274 Ok(expression)
275 })
276 };
277 let mut parser = alt((number_parser, identifier_parser));
278 parser(input)
279 }
280}
281
282fn parse_human_bytesize(input: &str) -> IResult<&str, (Value, Unit)> {
283 let parser = tuple((space0, Value::parse, space0, map_parser(alpha0, Unit::parse), space0));
284 let parser = all_consuming(parser);
285 let mut parser = map(parser, |(_, value, _, unit, _)| (value, unit)); parser(input)
287}
288
289#[proc_macro]
351pub fn human_bytesize(input: TokenStream) -> TokenStream {
352 let input = input.to_string();
353 match parse_human_bytesize(&input).finish() {
354 Ok((_, (Value::Number(value), unit))) => {
355 let result = value * unit;
356 let result = quote! {
357 #result
358 };
359 TokenStream::from(result)
360 },
361 Ok((_, (Value::Expression(expr), unit))) => {
362 let unit = unit.multiplier();
363 let result = quote! {
364 ((#expr) * #unit)
365 };
366 TokenStream::from(result)
367 },
368 Err(_) => panic!("Invalid format! Please use format like '100 KiB' or '100KiB'"),
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375
376 #[test]
377 fn test_unit_parse() {
378 assert!(matches!(Unit::parse(""), Ok((_, Unit::Byte))));
380 assert!(matches!(Unit::parse("B"), Ok((_, Unit::Byte))));
381 assert!(matches!(Unit::parse("K"), Ok((_, Unit::Kilobyte))));
382 assert!(matches!(Unit::parse("KB"), Ok((_, Unit::Kilobyte))));
383 assert!(matches!(Unit::parse("Ki"), Ok((_, Unit::Kibibyte))));
384 assert!(matches!(Unit::parse("KiB"), Ok((_, Unit::Kibibyte))));
385 assert!(matches!(Unit::parse("M"), Ok((_, Unit::Megabyte))));
386 assert!(matches!(Unit::parse("MB"), Ok((_, Unit::Megabyte))));
387 assert!(matches!(Unit::parse("Mi"), Ok((_, Unit::Mebibyte))));
388 assert!(matches!(Unit::parse("MiB"), Ok((_, Unit::Mebibyte))));
389 assert!(matches!(Unit::parse("G"), Ok((_, Unit::Gigabyte))));
390 assert!(matches!(Unit::parse("GB"), Ok((_, Unit::Gigabyte))));
391 assert!(matches!(Unit::parse("Gi"), Ok((_, Unit::Gibibyte))));
392 assert!(matches!(Unit::parse("GiB"), Ok((_, Unit::Gibibyte))));
393 assert!(matches!(Unit::parse("T"), Ok((_, Unit::Terabyte))));
394 assert!(matches!(Unit::parse("TB"), Ok((_, Unit::Terabyte))));
395 assert!(matches!(Unit::parse("Ti"), Ok((_, Unit::Tebibyte))));
396 assert!(matches!(Unit::parse("TiB"), Ok((_, Unit::Tebibyte))));
397 assert!(matches!(Unit::parse("P"), Ok((_, Unit::Petabyte))));
398 assert!(matches!(Unit::parse("PB"), Ok((_, Unit::Petabyte))));
399 assert!(matches!(Unit::parse("Pi"), Ok((_, Unit::Pebibyte))));
400 assert!(matches!(Unit::parse("PiB"), Ok((_, Unit::Pebibyte))));
401 assert!(matches!(Unit::parse("E"), Ok((_, Unit::Exabyte))));
402 assert!(matches!(Unit::parse("EB"), Ok((_, Unit::Exabyte))));
403 assert!(matches!(Unit::parse("Ei"), Ok((_, Unit::Exbibyte))));
404 assert!(matches!(Unit::parse("EiB"), Ok((_, Unit::Exbibyte))));
405 assert!(matches!(Unit::parse("Z"), Ok((_, Unit::Zettabyte))));
406 assert!(matches!(Unit::parse("ZB"), Ok((_, Unit::Zettabyte))));
407 assert!(matches!(Unit::parse("Zi"), Ok((_, Unit::Zebibyte))));
408 assert!(matches!(Unit::parse("ZiB"), Ok((_, Unit::Zebibyte))));
409 assert!(matches!(Unit::parse("Y"), Ok((_, Unit::Yottabyte))));
410 assert!(matches!(Unit::parse("YB"), Ok((_, Unit::Yottabyte))));
411 assert!(matches!(Unit::parse("Yi"), Ok((_, Unit::Yobibyte))));
412 assert!(matches!(Unit::parse("YiB"), Ok((_, Unit::Yobibyte))));
413
414 assert!(matches!(Unit::parse(" "), Err(_)));
416 assert!(matches!(Unit::parse(" B"), Err(_)));
417 assert!(matches!(Unit::parse(" B"), Err(_)));
418 assert!(matches!(Unit::parse("B "), Err(_)));
419 assert!(matches!(Unit::parse("B "), Err(_)));
420 assert!(matches!(Unit::parse("XYZ"), Err(_)));
421 }
422
423 #[test]
424 fn test_parse_human_bytesize() {
425 assert!(matches!(
427 parse_human_bytesize("8"),
428 Ok((_, (Value::Number(8), Unit::Byte)))
429 ));
430 assert!(matches!(
431 parse_human_bytesize("34 B"),
432 Ok((_, (Value::Number(34), Unit::Byte)))
433 ));
434 assert!(matches!(
435 parse_human_bytesize("82KB"),
436 Ok((_, (Value::Number(82), Unit::Kilobyte)))
437 ));
438 assert!(matches!(
439 parse_human_bytesize("7 KiB"),
440 Ok((_, (Value::Number(7), Unit::Kibibyte)))
441 ));
442 assert!(matches!(
443 parse_human_bytesize("987 MB"),
444 Ok((_, (Value::Number(987), Unit::Megabyte)))
445 ));
446 assert!(matches!(
447 parse_human_bytesize("150MiB"),
448 Ok((_, (Value::Number(150), Unit::Mebibyte)))
449 ));
450 assert!(matches!(
451 parse_human_bytesize("99 GB"),
452 Ok((_, (Value::Number(99), Unit::Gigabyte)))
453 ));
454 assert!(matches!(
455 parse_human_bytesize("1 GiB"),
456 Ok((_, (Value::Number(1), Unit::Gibibyte)))
457 ));
458 assert!(matches!(
459 parse_human_bytesize("678TB"),
460 Ok((_, (Value::Number(678), Unit::Terabyte)))
461 ));
462 assert!(matches!(
463 parse_human_bytesize("123TiB"),
464 Ok((_, (Value::Number(123), Unit::Tebibyte)))
465 ));
466 assert!(matches!(
467 parse_human_bytesize("54 PB"),
468 Ok((_, (Value::Number(54), Unit::Petabyte)))
469 ));
470 assert!(matches!(
471 parse_human_bytesize("19 PiB"),
472 Ok((_, (Value::Number(19), Unit::Pebibyte)))
473 ));
474 assert!(matches!(
475 parse_human_bytesize("556 EB"),
476 Ok((_, (Value::Number(556), Unit::Exabyte)))
477 ));
478 assert!(matches!(
479 parse_human_bytesize("153EiB"),
480 Ok((_, (Value::Number(153), Unit::Exbibyte)))
481 ));
482 assert!(matches!(
483 parse_human_bytesize("4ZB"),
484 Ok((_, (Value::Number(4), Unit::Zettabyte)))
485 ));
486 assert!(matches!(
487 parse_human_bytesize("34ZiB"),
488 Ok((_, (Value::Number(34), Unit::Zebibyte)))
489 ));
490 assert!(matches!(
491 parse_human_bytesize("750YB"),
492 Ok((_, (Value::Number(750), Unit::Yottabyte)))
493 ));
494 assert!(matches!(
495 parse_human_bytesize("56YiB"),
496 Ok((_, (Value::Number(56), Unit::Yobibyte)))
497 ));
498 assert!(matches!(
499 parse_human_bytesize("{ var } EB"),
500 Ok((_, (Value::Expression(_), Unit::Exabyte)))
501 ));
502 assert!(matches!(
503 parse_human_bytesize("0 "),
504 Ok((_, (Value::Number(0), Unit::Byte)))
505 ));
506 assert!(matches!(
507 parse_human_bytesize(" 13M"),
508 Ok((_, (Value::Number(13), Unit::Megabyte)))
509 ));
510 assert!(matches!(
511 parse_human_bytesize("230 G"),
512 Ok((_, (Value::Number(230), Unit::Gigabyte)))
513 ));
514 assert!(matches!(
515 parse_human_bytesize(" 2 PiB "),
516 Ok((_, (Value::Number(2), Unit::Pebibyte)))
517 ));
518
519 assert!(matches!(parse_human_bytesize("18BB"), Err(_)));
521 assert!(matches!(parse_human_bytesize("1 0TB"), Err(_)));
522 assert!(matches!(parse_human_bytesize("10 XB"), Err(_)));
523 assert!(matches!(parse_human_bytesize("ABC XYZ"), Err(_)));
524 }
525}