1use std::time::Duration;
8
9pub fn parse_duration(d: &str) -> Result<Duration, String> {
22 let d_trimmed = d.trim();
23 let chars = d_trimmed.as_bytes();
24 let mut value = 0;
25 let mut i = 0;
26 while i < chars.len() && chars[i].is_ascii_digit() {
27 value = value * 10 + (chars[i] - b'0') as u32;
28 i += 1;
29 }
30 if i == 0 {
31 return Err(format!("duration '{}' doesn't start with digits", d));
32 }
33 let ok_as = |func: fn(u64) -> Duration| -> Result<_, String> { Ok(func(value as u64)) };
34
35 match d_trimmed[i..].trim() {
36 "ms" | "millis" | "millisecond" | "milliseconds" => ok_as(Duration::from_millis),
37 "s" | "sec" | "secs" | "second" | "seconds" => ok_as(Duration::from_secs),
38 "m" | "min" | "mins" | "minute" | "minutes" => ok_as(Duration::from_mins),
39 "h" | "hour" | "hours" => ok_as(Duration::from_hours),
40 "d" | "day" | "days" => ok_as(|d: u64| Duration::from_hours(d * 24)),
41 unit => Err(format!(
42 "unit {} not recognised. must be one of s/sec/seconds, m/min/minutes, h/hours, d/days",
43 unit
44 )),
45 }
46}
47
48#[cfg(test)]
49mod tests {
50
51 use super::*;
52
53 #[test]
54 fn char_conversion() {
55 assert_eq!(b'9' - b'0', 9);
56 }
57
58 #[test]
59 fn parse_duration_test() {
60 assert_eq!(parse_duration("1s"), Ok(Duration::from_secs(1)));
61 assert_eq!(parse_duration("12s"), Ok(Duration::from_secs(12)));
62 assert_eq!(parse_duration(" 12 secs "), Ok(Duration::from_secs(12)));
63 assert_eq!(parse_duration("2m"), Ok(Duration::from_mins(2)));
64 assert_eq!(parse_duration("6 hours"), Ok(Duration::from_hours(6)));
65 assert_eq!(parse_duration("1d"), Ok(Duration::from_hours(1 * 24)));
66 assert_eq!(parse_duration("365d"), Ok(Duration::from_hours(365 * 24)));
67
68 assert!(parse_duration("d 3").is_err());
69 assert!(parse_duration("d3").is_err());
70 assert!(parse_duration("3da").is_err());
71 assert!(parse_duration("3_days").is_err());
72 assert!(parse_duration("_3d").is_err());
73 assert!(parse_duration("3 3d").is_err());
74 assert!(parse_duration("3.3d").is_err());
75 }
76}