#[derive(Debug, PartialEq)]
pub enum Error {
ParseError(String),
}
enum InternalError {
Overflow,
}
pub fn parse_duration(string: &str) -> Result<i64, Error> {
let mut s = string;
let mut d: i64 = 0; let mut neg = false;
if s != "" {
let c = s.chars().nth(0).unwrap();
if c == '-' || c == '+' {
neg = c == '-';
s = &s[1..];
}
}
if s == "0" {
return Ok(0);
}
if s == "" {
return Err(Error::ParseError(format!("invalid duration: {}", string)));
}
while s != "" {
let mut v: i64;
let mut f: i64 = 0;
let mut scale: f64 = 1f64;
let c = s.chars().nth(0).unwrap();
if !(c == '.' || '0' <= c && c <= '9') {
return Err(Error::ParseError(format!("invalid duration: {}", string)));
}
let pl = s.len();
match leading_int(s) {
Ok((_v, _s)) => {
v = _v;
s = _s;
}
Err(_) => {
return Err(Error::ParseError(format!("invalid duration: {}", string)));
}
}
let pre = pl != s.len();
let mut post = false;
if s != "" && s.chars().nth(0).unwrap() == '.' {
s = &s[1..];
let pl = s.len();
match leading_fraction(s) {
(f_, scale_, s_) => {
f = f_;
scale = scale_;
s = s_;
}
}
post = pl != s.len();
}
if !pre && !post {
return Err(Error::ParseError(format!("invalid duration: {}", string)));
}
let mut i = 0;
while i < s.len() {
let c = s.chars().nth(i).unwrap();
if c == '.' || '0' <= c && c <= '9' {
break;
}
i += 1;
}
if i == 0 {
return Err(Error::ParseError(format!("missing unit in duration: {}", string)));
}
let u = &s[..i];
s = &s[i..];
let unit = match u {
"ns" => 1i64,
"us" => 1000i64,
"µs" => 1000i64, "μs" => 1000i64, "ms" => 1000000i64,
"s" => 1000000000i64,
"m" => 60000000000i64,
"h" => 3600000000000i64,
_ => {
return Err(Error::ParseError(format!("unknown unit {} in duration {}", u, string)));
}
};
if v > (1 << 63 - 1) / unit {
return Err(Error::ParseError(format!("invalid duration {}", string)));
}
v *= unit;
if f > 0 {
v += (f as f64 * (unit as f64 / scale)) as i64;
if v < 0 {
return Err(Error::ParseError(format!("invalid duration {}", string)));
}
}
d += v;
if d < 0 {
return Err(Error::ParseError(format!("invalid duration {}", string)));
}
}
if neg {
d = -d;
}
Ok(d)
}
fn leading_int(s: &str) -> Result<(i64, &str), InternalError> {
let mut x = 0;
let mut i = 0;
while i < s.len() {
let c = s.chars().nth(i).unwrap();
if c < '0' || c > '9' {
break
}
if x > (1<<63-1)/10 {
return Err(InternalError::Overflow);
}
let d = i64::from(c.to_digit(10).unwrap());
x = x * 10 + d;
if x < 0 {
return Err(InternalError::Overflow);
}
i += 1;
}
Ok((x, &s[i..]))
}
fn leading_fraction(s: &str) -> (i64, f64, &str) {
let mut i = 0;
let mut x = 0i64;
let mut scale = 1f64;
let mut overflow = false;
while i < s.len() {
let c = s.chars().nth(i).unwrap();
if c < '0' || c > '9' {
break;
}
if overflow {
continue;
}
if x > (1 << 63 - 1) / 10 {
overflow = true;
continue;
}
let d = i64::from(c.to_digit(10).unwrap());
let y = x * 10 + d;
if y < 0 {
overflow = true;
continue;
}
x = y;
scale *= 10f64;
i += 1;
}
(x, scale, &s[i..])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_duration() -> Result<(), Error> {
assert_eq!(parse_duration("50ns")?, 50);
assert_eq!(parse_duration("3ms")?, 3000000);
assert_eq!(parse_duration("2us")?, 2000);
assert_eq!(parse_duration("4s")?, 4000000000);
assert_eq!(parse_duration("1h45m")?, 6300000000000);
assert_eq!(
parse_duration("1").unwrap_err(),
Error::ParseError(String::from("missing unit in duration: 1")),
);
Ok(())
}
}