use nom::branch::alt; use nom::bytes::complete::is_a; use nom::bytes::complete::tag; use nom::IResult; use serde::ser::SerializeStruct; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use std::time; #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)] pub struct Duration { pub input: String, pub value: time::Duration, } impl<'a> From<&'a str> for Duration { fn from(s: &str) -> Self { match duration(s) { Ok((_, v)) => v, Err(_) => Duration::default(), } } } impl fmt::Display for Duration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.input) } } impl Serialize for Duration { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { if serializer.is_human_readable() { serializer.serialize_some(&self.value) } else { let mut val = serializer.serialize_struct("Duration", 2)?; val.serialize_field("input", &self.input)?; val.serialize_field("value", &self.value)?; val.end() } } } pub fn duration(i: &str) -> IResult<&str, Duration> { duration_raw(i) } pub fn duration_raw(i: &str) -> IResult<&str, Duration> { let (i, v) = part(i)?; let (i, u) = unit(i)?; Ok(( i, Duration { input: format!("{}{}", v, u), value: match u { "ns" => time::Duration::new(0, v as u32), "µs" => time::Duration::new(0, v as u32 * 1000), "ms" => time::Duration::new(0, v as u32 * 1000 * 1000), "s" => time::Duration::new(v, 0), "m" => time::Duration::new(v * 60, 0), "h" => time::Duration::new(v * 60 * 60, 0), "d" => time::Duration::new(v * 60 * 60 * 24, 0), "w" => time::Duration::new(v * 60 * 60 * 24 * 7, 0), _ => time::Duration::new(0, 0), }, }, )) } fn part(i: &str) -> IResult<&str, u64> { let (i, v) = is_a("1234567890")(i)?; let v = u64::from_str(v).unwrap(); Ok((i, v)) } fn unit(i: &str) -> IResult<&str, &str> { alt((tag("ns"), tag("µs"), tag("ms"), tag("s"), tag("m"), tag("h"), tag("d"), tag("w")))(i) } #[cfg(test)] mod tests { use super::*; #[test] fn duration_nil() { let sql = "0ns"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("0ns", format!("{}", out)); assert_eq!(out.value, Duration::from("0ns").value); } #[test] fn duration_basic() { let sql = "1s"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("1s", format!("{}", out)); assert_eq!(out.value, Duration::from("1s").value); } #[test] fn duration_simple() { let sql = "1000ms"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("1000ms", format!("{}", out)); assert_eq!(out.value, Duration::from("1s").value); } #[test] fn duration_complex() { let sql = "86400s"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("86400s", format!("{}", out)); assert_eq!(out.value, Duration::from("1d").value); } #[test] fn duration_days() { let sql = "5d"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("5d", format!("{}", out)); assert_eq!(out.value, Duration::from("5d").value); } #[test] fn duration_weeks() { let sql = "4w"; let res = duration(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("4w", format!("{}", out)); assert_eq!(out.value, Duration::from("4w").value); } }