time_parse/duration/
duration_nom.rs1use std::time::Duration;
2
3use anyhow::bail;
4use anyhow::Result;
5use nom::bytes::complete::tag;
6use nom::combinator::opt;
7use nom::sequence::terminated;
8use nom::IResult;
9
10fn num(input: &str) -> IResult<&str, u64> {
11 let (input, num) = nom::character::complete::digit1(input)?;
12 let num = num
13 .parse()
14 .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::TooLarge)))?;
15
16 Ok((input, num))
17}
18
19fn period_num(input: &str) -> IResult<&str, (u64, u32)> {
20 let (input, whole) = num(input)?;
21 let (input, dot) = opt(tag("."))(input)?;
22 if dot.is_none() {
23 return Ok((input, (whole, 0)));
24 }
25
26 let (input, frac) = nom::character::complete::digit1(input)?;
27
28 Ok((input, (whole, super::to_nanos(frac).expect("TODO"))))
29}
30
31fn time(input: &str) -> IResult<&str, (u64, u32)> {
32 let (input, _) = tag("T")(input)?;
33 let (input, h) = opt(terminated(num, tag("H")))(input)?;
34 let (input, m) = opt(terminated(num, tag("M")))(input)?;
35 let (input, s) = opt(terminated(period_num, tag("S")))(input)?;
36
37 Ok((
38 input,
39 ((
40 h.unwrap_or(0) * super::SECS_PER_HOUR
41 + m.unwrap_or(0) * super::SECS_PER_MINUTE
42 + s.map(|(s, _ns)| s).unwrap_or(0),
43 s.map(|(_s, ns)| ns).unwrap_or(0),
44 )),
45 ))
46}
47
48fn period(input: &str) -> IResult<&str, (u64, u32)> {
49 let (input, _) = tag("P")(input)?;
50 let (input, w) = opt(terminated(num, tag("W")))(input)?;
51 let (input, d) = opt(terminated(num, tag("D")))(input)?;
52 let (input, t) = opt(time)(input)?;
53
54 Ok((
55 input,
56 (
57 w.unwrap_or(0) * super::SECS_PER_WEEK
58 + d.unwrap_or(0) * super::SECS_PER_DAY
59 + t.map(|(s, _ns)| s).unwrap_or(0),
60 t.map(|(_s, ns)| ns).unwrap_or(0),
61 ),
62 ))
63}
64
65pub fn parse(input: &str) -> Result<Duration> {
66 match period(input) {
67 Ok(("", (s, ns))) => Ok(Duration::new(s, ns)),
68 other => bail!("invalid: {:?}", other),
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use std::time::Duration;
75
76 #[test]
77 fn duration() {
78 use super::parse;
79 assert_eq!(Duration::new(7, 0), parse("PT7S").unwrap());
80 assert_eq!(Duration::new(7, 5_000_000), parse("PT7.005S").unwrap());
81 assert_eq!(Duration::new(2 * 60, 0), parse("PT2M").unwrap());
82 assert_eq!(
83 Duration::new((2 * 24 + 1) * 60 * 60, 0),
84 parse("P2DT1H").unwrap()
85 );
86 assert!(parse("PT2").is_err());
87 assert!(parse("PT22").is_err());
88 assert!(parse("PT2M2").is_err());
89 assert!(parse("T2S").is_err());
90 assert!(parse("P2S").is_err());
91 }
92}