1use regex::Regex;
2use error::Error;
3
4const SEC_IN_MIN : u64 = 60;
5const SEC_IN_HR : u64 = SEC_IN_MIN * 60;
6const SEC_IN_DAY : u64 = SEC_IN_HR * 24;
7const SEC_IN_WK : u64 = SEC_IN_DAY * 7;
8const SEC_IN_YR : u64 = SEC_IN_DAY * 365;
9
10fn multiplier_from_unit(units : &str) -> Result<u64, Error> {
11 return match units.as_ref() {
12 "s" => Ok(1),
13 "m" => Ok(SEC_IN_MIN),
14 "h" => Ok(SEC_IN_HR),
15 "d" => Ok(SEC_IN_DAY),
16 "w" => Ok(SEC_IN_WK),
17 "y" => Ok(SEC_IN_YR),
18 _ => Err(Error::UnknownSuffix)
19 }
20}
21
22pub fn parse_duration(string : &String) -> Result<u64, Error> {
23 lazy_static! {
24 static ref TIME_RE: Regex = Regex::new(r"(?P<value>\d+)(?P<units>[a-z])").unwrap();
25 }
26
27 let caps_iter = TIME_RE.captures_iter(string);
28
29 let mut expected_length = 0;
30 let mut total_duration_in_ms : u64 = 0;
31 for caps in caps_iter {
32 let value_str = &caps["value"];
33 let units = &caps["units"];
34
35 expected_length += value_str.len() + units.len();
36
37 let value : u64 = match value_str.parse() {
38 Ok(val) => val,
39 Err(_) => return Err(Error::UnparsableNumber)
40 };
41
42 let duration_in_ms = match value.checked_mul(multiplier_from_unit(units)?) {
43 Some(val) => val,
44 None => return Err(Error::Overflow)
45 };
46
47 total_duration_in_ms = match total_duration_in_ms.checked_add(duration_in_ms) {
48 Some(val) => val,
49 None => return Err(Error::Overflow)
50 };
51 }
52
53 if expected_length == 0 {
54 return Err(Error::NoData);
55 }
56
57 if expected_length != string.len() {
58 return Err(Error::UnparsableExtras);
59 }
60
61 return Ok(total_duration_in_ms);
62}