1#[derive(Debug, PartialEq)]
40pub enum Error {
41 ParseError(String),
42}
43
44enum InternalError {
45 Overflow,
46}
47
48pub fn parse_duration(string: &str) -> Result<i64, Error> {
56 let mut s = string;
58 let mut d: i64 = 0; let mut neg = false;
60
61 if s != "" {
63 let c = s.chars().nth(0).unwrap();
64 if c == '-' || c == '+' {
65 neg = c == '-';
66 s = &s[1..];
67 }
68 }
69 if s == "0" {
71 return Ok(0);
72 }
73 if s == "" {
74 return Err(Error::ParseError(format!("invalid duration: {}", string)));
75 }
76 while s != "" {
77 let mut v: i64;
79 let mut f: i64 = 0;
80 let mut scale: f64 = 1f64;
82
83 let c = s.chars().nth(0).unwrap();
85 if !(c == '.' || '0' <= c && c <= '9') {
86 return Err(Error::ParseError(format!("invalid duration: {}", string)));
87 }
88 let pl = s.len();
90 match leading_int(s) {
91 Ok((_v, _s)) => {
92 v = _v;
93 s = _s;
94 }
95 Err(_) => {
96 return Err(Error::ParseError(format!("invalid duration: {}", string)));
97 }
98 }
99 let pre = pl != s.len(); let mut post = false;
103 if s != "" && s.chars().nth(0).unwrap() == '.' {
104 s = &s[1..];
105 let pl = s.len();
106 match leading_fraction(s) {
107 (f_, scale_, s_) => {
108 f = f_;
109 scale = scale_;
110 s = s_;
111 }
112 }
113 post = pl != s.len();
114 }
115 if !pre && !post {
116 return Err(Error::ParseError(format!("invalid duration: {}", string)));
118 }
119
120 let mut i = 0;
122 while i < s.len() {
123 let c = s.chars().nth(i).unwrap();
124 if c == '.' || '0' <= c && c <= '9' {
125 break;
126 }
127 i += 1;
128 }
129 if i == 0 {
130 return Err(Error::ParseError(format!("missing unit in duration: {}", string)));
131 }
132 let u = &s[..i];
133 s = &s[i..];
134 let unit = match u {
135 "ns" => 1i64,
136 "us" => 1000i64,
137 "µs" => 1000i64, "μs" => 1000i64, "ms" => 1000000i64,
140 "s" => 1000000000i64,
141 "m" => 60000000000i64,
142 "h" => 3600000000000i64,
143 _ => {
144 return Err(Error::ParseError(format!("unknown unit {} in duration {}", u, string)));
145 }
146 };
147 if v > (1 << 63 - 1) / unit {
148 return Err(Error::ParseError(format!("invalid duration {}", string)));
150 }
151 v *= unit;
152 if f > 0 {
153 v += (f as f64 * (unit as f64 / scale)) as i64;
156 if v < 0 {
157 return Err(Error::ParseError(format!("invalid duration {}", string)));
159 }
160 }
161 d += v;
162 if d < 0 {
163 return Err(Error::ParseError(format!("invalid duration {}", string)));
165 }
166 }
167 if neg {
168 d = -d;
169 }
170 Ok(d)
171}
172
173fn leading_int(s: &str) -> Result<(i64, &str), InternalError> {
175 let mut x = 0;
176 let mut i = 0;
177 while i < s.len() {
178 let c = s.chars().nth(i).unwrap();
179 if c < '0' || c > '9' {
180 break
181 }
182 if x > (1<<63-1)/10 {
183 return Err(InternalError::Overflow);
184 }
185 let d = i64::from(c.to_digit(10).unwrap());
186 x = x * 10 + d;
187 if x < 0 {
188 return Err(InternalError::Overflow);
190 }
191 i += 1;
192 }
193 Ok((x, &s[i..]))
194}
195
196fn leading_fraction(s: &str) -> (i64, f64, &str) {
203 let mut i = 0;
204 let mut x = 0i64;
205 let mut scale = 1f64;
206 let mut overflow = false;
207 while i < s.len() {
208 let c = s.chars().nth(i).unwrap();
209 if c < '0' || c > '9' {
210 break;
211 }
212 if overflow {
213 continue;
214 }
215 if x > (1 << 63 - 1) / 10 {
216 overflow = true;
218 continue;
219 }
220 let d = i64::from(c.to_digit(10).unwrap());
221 let y = x * 10 + d;
222 if y < 0 {
223 overflow = true;
224 continue;
225 }
226 x = y;
227 scale *= 10f64;
228 i += 1;
229 }
230 (x, scale, &s[i..])
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn test_parse_duration() -> Result<(), Error> {
239 assert_eq!(parse_duration("50ns")?, 50);
240 assert_eq!(parse_duration("3ms")?, 3000000);
241 assert_eq!(parse_duration("2us")?, 2000);
242 assert_eq!(parse_duration("4s")?, 4000000000);
243 assert_eq!(parse_duration("1h45m")?, 6300000000000);
244 assert_eq!(
245 parse_duration("1").unwrap_err(),
246 Error::ParseError(String::from("missing unit in duration: 1")),
247 );
248 Ok(())
249 }
250}
251