1use pom::parser::*;
2use pom::Parser;
3
4use std::str::{self, FromStr};
5
6#[derive(Debug, PartialEq)]
7struct Duration {
8 years: Option<f32>,
9 months: Option<f32>,
10 weeks: Option<f32>,
11 days: Option<f32>,
12 hours: Option<f32>,
13 minutes: Option<f32>,
14 seconds: Option<f32>,
15}
16
17fn number_separator() -> Parser<u8, ()> {
18 one_of(b".,").discard()
20}
21
22fn number() -> Parser<u8, f32> {
23 let integer = one_of(b"0123456789").repeat(0..);
24 let frac = number_separator() + one_of(b"0123456789").repeat(1..);
25 let number = integer + frac.opt();
26 number
27 .collect()
28 .convert(str::from_utf8)
29 .convert(f32::from_str)
30}
31
32fn date_part() -> Parser<u8, (Option<f32>, Option<f32>, Option<f32>, Option<f32>)> {
33 ((number() - sym(b'Y')).opt()
34 + (number() - sym(b'M')).opt()
35 + (number() - sym(b'W')).opt()
36 + (number() - sym(b'D')).opt())
37 .map(|(((years, months), weeks), days)| (years, months, weeks, days))
38}
39
40fn time_part() -> Parser<u8, (Option<f32>, Option<f32>, Option<f32>)> {
41 sym(b'T')
42 * ((number() - sym(b'H')).opt()
43 + (number() - sym(b'M')).opt()
44 + (number() - sym(b'S')).opt())
45 .map(|((hours, minutes), seconds)| (hours, minutes, seconds))
46}
47
48fn parser() -> Parser<u8, Duration> {
49 sym(b'P')
50 * (time_part().map(|(hours, minutes, seconds)| Duration {
51 years: None,
52 months: None,
53 weeks: None,
54 days: None,
55 hours,
56 minutes,
57 seconds,
58 }) | (date_part() + time_part()).map(|(date_elements, time_elements)| {
59 let (years, months, weeks, days) = date_elements;
60 let (hours, minutes, seconds) = time_elements;
61 Duration {
62 years,
63 months,
64 weeks,
65 days,
66 hours,
67 minutes,
68 seconds,
69 }
70 }))
71}
72
73fn main() {
76 let input = "P3Y6M4DT12H30M5S";
77 let result = parser().parse(input.as_bytes());
78
79 assert_eq!(
80 Duration {
81 years: Some(3f32),
82 months: Some(6f32),
83 weeks: None,
84 days: Some(4f32),
85 hours: Some(12f32),
86 minutes: Some(30f32),
87 seconds: Some(5f32)
88 },
89 result.unwrap()
90 );
91}