humanize_rs/duration/
mod.rs1use std::str::from_utf8;
12use std::time::Duration;
13use ParseError;
14
15const DIGIT_MIN: u8 = b'0';
16const DIGIT_MAX: u8 = b'9';
17
18const NANOS: [u64; 7] = [
19 1, 1_000, 1_000_000, 1_000_000_000, 60 * 1_000_000_000, 3600 * 1_000_000_000, 24 * 3600 * 1_000_000_000, ];
27
28pub fn parse(s: &str) -> Result<Duration, ParseError> {
39 let input = s.trim();
40 if input.is_empty() {
41 return Err(ParseError::EmptyInput);
42 }
43
44 if input == "0" {
45 return Ok(Duration::new(0, 0));
46 }
47
48 let mut value: u64 = 0;
49
50 let bs = input.as_bytes();
51 let mut read: usize = 0;
52
53 while read < bs.len() {
54 let (v, consumed) = read_int(&bs[read..])?;
55 read += consumed;
56
57 let (unit, consumed) = read_unit(&bs[read..])?;
58 read += consumed;
59
60 let nanos = unit_to_nanos(unit)?;
61
62 value = v
63 .checked_mul(nanos)
64 .and_then(|res| value.checked_add(res))
65 .ok_or(ParseError::Overflow)?;
66 }
67
68 Ok(Duration::from_nanos(value))
69}
70
71fn read_int(bs: &[u8]) -> Result<(u64, usize), ParseError> {
72 let mut v: u64 = 0;
73 let mut read: usize = 0;
74 while read < bs.len() {
75 let c = bs[read];
76 if c < DIGIT_MIN || c > DIGIT_MAX {
77 break;
78 }
79
80 v = v
81 .checked_mul(10)
82 .and_then(|res| res.checked_add((c - DIGIT_MIN) as u64))
83 .ok_or(ParseError::Overflow)?;
84
85 read += 1;
86 }
87
88 if read == 0 {
89 return Err(ParseError::MissingValue);
90 }
91
92 Ok((v, read))
93}
94
95fn read_unit(bs: &[u8]) -> Result<(&str, usize), ParseError> {
96 let mut read: usize = 0;
97 while read < bs.len() {
98 let c = bs[read];
99 if DIGIT_MIN <= c && c <= DIGIT_MAX {
100 break;
101 }
102
103 read += 1;
104 }
105
106 if read == 0 {
107 return Err(ParseError::MissingUnit);
108 }
109
110 let unit = from_utf8(&bs[..read]).or(Err(ParseError::InvalidUnit))?;
111
112 Ok((unit.trim(), read))
113}
114
115fn unit_to_nanos(unit: &str) -> Result<u64, ParseError> {
116 match unit {
117 "ns" => Ok(NANOS[0]),
118 "us" => Ok(NANOS[1]),
119 "ms" => Ok(NANOS[2]),
120 "s" => Ok(NANOS[3]),
121 "m" => Ok(NANOS[4]),
122 "h" => Ok(NANOS[5]),
123 "d" => Ok(NANOS[6]),
124 _ => Err(ParseError::InvalidUnit),
125 }
126}
127
128#[cfg(test)]
129mod tests;