xsd_types/lexical/duration/
mod.rs1use super::{date_time::parse_seconds_decimal, Lexical, LexicalFormOf};
2use static_regular_grammar::RegularGrammar;
3
4pub mod day_time_duration;
5pub use day_time_duration::{DayTimeDuration, DayTimeDurationBuf, InvalidDayTimeDuration};
6
7pub mod year_month_duration;
8pub use year_month_duration::{InvalidYearMonthDuration, YearMonthDuration, YearMonthDurationBuf};
9
10#[derive(RegularGrammar, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[grammar(sized(DurationBuf, derive(PartialEq, Eq, PartialOrd, Ord, Hash)))]
35pub struct Duration(str);
36
37impl Duration {
38 pub fn parts(&self) -> Parts {
39 enum State {
40 Sign,
41 DateNumber,
42 TimeNumber,
43 }
44
45 let mut state = State::Sign;
46 let mut result = Parts {
47 is_negative: false,
48 year: None,
49 month: None,
50 day: None,
51 hour: None,
52 minute: None,
53 second: None,
54 };
55
56 let mut start = 0;
57
58 for (i, c) in self.0.char_indices() {
59 state = match state {
60 State::Sign => match c {
61 '-' => {
62 result.is_negative = true;
63 State::Sign
64 }
65 'P' => {
66 start = i + 1;
67 State::DateNumber
68 }
69 _ => unreachable!(),
70 },
71 State::DateNumber => match c {
72 'Y' => {
73 result.year = Some(&self.0[start..i]);
74 start = i + 1;
75 State::DateNumber
76 }
77 'M' => {
78 result.month = Some(&self.0[start..i]);
79 start = i + 1;
80 State::DateNumber
81 }
82 'D' => {
83 result.day = Some(&self.0[start..i]);
84 start = i + 1;
85 State::DateNumber
86 }
87 'T' => {
88 start = i + 1;
89 State::TimeNumber
90 }
91 _ => State::DateNumber,
92 },
93 State::TimeNumber => match c {
94 'H' => {
95 result.hour = Some(&self.0[start..i]);
96 start = i + 1;
97 State::TimeNumber
98 }
99 'M' => {
100 result.minute = Some(&self.0[start..i]);
101 start = i + 1;
102 State::TimeNumber
103 }
104 'S' => {
105 result.second = Some(&self.0[start..i]);
106 start = i + 1;
107 State::TimeNumber
108 }
109 _ => State::TimeNumber,
110 },
111 }
112 }
113
114 result
115 }
116}
117
118impl Lexical for Duration {
119 type Error = InvalidDuration<String>;
120
121 fn parse(value: &str) -> Result<&Self, Self::Error> {
122 Self::new(value).map_err(|_| InvalidDuration(value.to_owned()))
123 }
124}
125
126impl LexicalFormOf<crate::Duration> for Duration {
127 type ValueError = std::convert::Infallible;
128
129 fn try_as_value(&self) -> Result<crate::Duration, Self::ValueError> {
130 Ok(self.parts().to_duration())
131 }
132}
133
134#[derive(Debug, PartialEq, Eq)]
135pub struct Parts<'a> {
136 pub is_negative: bool,
137 pub year: Option<&'a str>,
138 pub month: Option<&'a str>,
139 pub day: Option<&'a str>,
140 pub hour: Option<&'a str>,
141 pub minute: Option<&'a str>,
142 pub second: Option<&'a str>,
143}
144
145impl<'a> Parts<'a> {
146 pub fn new(
147 is_negative: bool,
148 year: Option<&'a str>,
149 month: Option<&'a str>,
150 day: Option<&'a str>,
151 hour: Option<&'a str>,
152 minute: Option<&'a str>,
153 second: Option<&'a str>,
154 ) -> Self {
155 Self {
156 is_negative,
157 year,
158 month,
159 day,
160 hour,
161 minute,
162 second,
163 }
164 }
165 fn to_duration(&self) -> crate::Duration {
166 let mut months = 0u32;
167
168 if let Some(y) = self.year {
169 let y: u32 = y.parse().unwrap();
170 months += y * 12;
171 }
172
173 if let Some(m) = self.month {
174 let m: u32 = m.parse().unwrap();
175 months += m;
176 }
177
178 let mut seconds = 0u32;
179
180 if let Some(d) = self.day {
181 let d: u32 = d.parse().unwrap();
182 seconds += d * 24 * 60 * 60;
183 }
184
185 if let Some(h) = self.hour {
186 let h: u32 = h.parse().unwrap();
187 seconds += h * 60 * 60;
188 }
189
190 if let Some(m) = self.minute {
191 let m: u32 = m.parse().unwrap();
192 seconds += m * 60;
193 }
194
195 let mut nano_seconds = 0u32;
196
197 if let Some(s) = self.second {
198 let (s, ns) = parse_seconds_decimal(s);
199 seconds += s;
200 nano_seconds = ns;
201 }
202
203 crate::Duration::new(self.is_negative, months, seconds, nano_seconds)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn parsing() {
213 let vectors = [
214 (
215 "P9Y11M",
216 Parts::new(false, Some("9"), Some("11"), None, None, None, None),
217 "P9Y11M",
218 ),
219 (
220 "P9Y12M",
221 Parts::new(false, Some("9"), Some("12"), None, None, None, None),
222 "P10Y",
223 ),
224 (
225 "-P9Y12M1DT24H01M1.0001S",
226 Parts::new(
227 true,
228 Some("9"),
229 Some("12"),
230 Some("1"),
231 Some("24"),
232 Some("01"),
233 Some("1.0001"),
234 ),
235 "-P10Y2DT1M1.0001S",
236 ),
237 ];
238
239 for (input, parts, normalized) in vectors {
240 let lexical_repr = Duration::new(input).unwrap();
241 assert_eq!(lexical_repr.parts(), parts);
242
243 let value = lexical_repr.try_as_value().unwrap();
244 assert_eq!(value.to_string().as_str(), normalized)
245 }
246 }
247}