1use std::time::Duration;
4
5pub fn parse_bytes(input: &str) -> Result<usize, String> {
7 let s = input.trim();
8 if s.is_empty() {
9 return Err("empty size".into());
10 }
11 let (num_str, unit) = split_num_unit(s)?;
12 let n: f64 = num_str
13 .parse()
14 .map_err(|_| format!("invalid size number: {num_str}"))?;
15 if n < 0.0 {
16 return Err("size must be non-negative".into());
17 }
18 let mult = match unit {
19 "" | "b" | "B" => 1.0,
20 "k" | "K" | "kb" | "KB" | "KiB" => 1024.0,
21 "m" | "M" | "mb" | "MB" | "MiB" => 1024.0 * 1024.0,
22 "g" | "G" | "gb" | "GB" | "GiB" => 1024.0 * 1024.0 * 1024.0,
23 other => return Err(format!("unknown size unit: {other}")),
24 };
25 Ok((n * mult) as usize)
26}
27
28pub fn parse_duration(input: &str) -> Result<Duration, String> {
30 let s = input.trim();
31 if s.is_empty() {
32 return Err("empty duration".into());
33 }
34 let (num_str, unit) = split_num_unit(s)?;
35 let n: f64 = num_str
36 .parse()
37 .map_err(|_| format!("invalid duration number: {num_str}"))?;
38 if n < 0.0 {
39 return Err("duration must be non-negative".into());
40 }
41 let secs = match unit {
42 "" | "s" | "sec" | "secs" | "second" | "seconds" => n,
43 "ms" | "millis" | "millisecond" | "milliseconds" => n / 1000.0,
44 "m" | "min" | "mins" | "minute" | "minutes" => n * 60.0,
45 "h" | "hr" | "hour" | "hours" => n * 3600.0,
46 other => return Err(format!("unknown duration unit: {other}")),
47 };
48 Ok(Duration::from_secs_f64(secs))
49}
50
51fn split_num_unit(s: &str) -> Result<(&str, &str), String> {
52 let end = s
53 .char_indices()
54 .find(|(_, c)| !(c.is_ascii_digit() || *c == '.'))
55 .map(|(i, _)| i)
56 .unwrap_or(s.len());
57 if end == 0 {
58 return Err(format!("missing number in `{s}`"));
59 }
60 Ok((&s[..end], s[end..].trim()))
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn bytes_units() {
69 assert_eq!(parse_bytes("1024").unwrap(), 1024);
70 assert_eq!(parse_bytes("2 KiB").unwrap(), 2048);
71 assert_eq!(parse_bytes("1MiB").unwrap(), 1024 * 1024);
72 assert_eq!(parse_bytes("50 MiB").unwrap(), 50 * 1024 * 1024);
73 }
74
75 #[test]
76 fn duration_units() {
77 assert_eq!(parse_duration("30s").unwrap(), Duration::from_secs(30));
78 assert_eq!(parse_duration("500ms").unwrap(), Duration::from_millis(500));
79 assert_eq!(parse_duration("2m").unwrap(), Duration::from_secs(120));
80 }
81}