1use crate::partial_date::{partial_date, partial_end_date};
2use crate::partial_time::{partial_end_base_time, partial_time};
3use core::str;
4use winnow::combinator::{alt, opt, preceded, trace};
5use winnow::error::ParserError;
6use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial};
7use winnow::token::literal;
8use winnow::{seq, Parser, Result};
9use winnow_datetime::types::PartialDateTime;
10
11pub(crate) fn partial_datetime<'i, Input, Error>(
13 input: &mut Input,
14) -> Result<PartialDateTime, Error>
15where
16 Input: StreamIsPartial + Stream + Compare<&'i str>,
17 <Input as Stream>::Slice: AsBStr,
18 <Input as Stream>::Token: AsChar + Clone,
19 Error: ParserError<Input>,
20{
21 trace("partial_datetime", move |input: &mut Input| {
22 seq!((
23 opt(partial_date),
24 opt(preceded(alt((literal(" "), literal("T"))), partial_time))
25 ))
26 .verify(|(d, t)| d.is_some() || t.is_some())
27 .map(|(d, t)| PartialDateTime { date: d, time: t })
28 .parse_next(input)
29 })
30 .parse_next(input)
31}
32
33pub(crate) fn partial_end_datetime<'i, Input, Error>(
34 input: &mut Input,
35 start_datetime: &PartialDateTime,
36) -> Result<PartialDateTime, Error>
37where
38 Input: StreamIsPartial + Stream + Compare<&'i str>,
39 <Input as Stream>::Slice: AsBStr,
40 <Input as Stream>::Token: AsChar + Clone,
41 Error: ParserError<Input>,
42{
43 trace(
44 "partial_end_datetime",
45 move |input: &mut Input| match start_datetime {
46 PartialDateTime {
47 date: start_date,
48 time: start_time,
49 } => {
50 let mut end_date = None;
51 let mut end_time = None;
52
53 if start_date.is_none() && start_date.is_none() {
54 return Err(ParserError::from_input(input));
55 }
56
57 if let Some(d) = start_date {
58 end_date = partial_end_date(input, d).map(Some)?;
59 }
60
61 if let Some(t) = start_time {
62 _ = literal(" ").parse_next(input)?;
63
64 end_time = partial_end_base_time(input, t).map(Some)?;
65 }
66
67 Ok(PartialDateTime {
68 date: end_date,
69 time: end_time,
70 })
71 }
72 },
73 )
74 .parse_next(input)
75}
76#[cfg(test)]
77mod parsers {
78 use crate::partial_datetime::{partial_datetime, partial_end_datetime};
79 use winnow::error::InputError;
80
81 use winnow_datetime::types::{PartialDate, PartialDateTime};
82
83 #[test]
84 fn partial_datetime_parsing() {
85 assert_eq!(
87 partial_datetime::<_, InputError<_>>(&mut "2015").unwrap(),
88 PartialDateTime {
89 date: Some(PartialDate::Year { year: Some(2015) }),
90 time: None,
91 }
92 );
93 assert_eq!(
95 partial_datetime::<_, InputError<_>>(&mut "2015-06-26").unwrap(),
96 PartialDateTime {
97 date: Some(PartialDate::YMD {
98 year: Some(2015),
99 month: Some(6),
100 day: Some(26)
101 }),
102 time: None,
103 }
104 );
105 assert_eq!(
106 partial_datetime::<_, InputError<_>>(&mut "2015-06").unwrap(),
107 PartialDateTime {
108 date: Some(PartialDate::YMD {
109 year: Some(2015),
110 month: Some(6),
111 day: None
112 }),
113 time: None,
114 }
115 );
116 assert_eq!(
118 partial_datetime::<_, InputError<_>>(&mut "2015-W05-6").unwrap(),
119 PartialDateTime {
120 date: Some(PartialDate::YWD {
121 year: Some(2015),
122 week: Some(5),
123 day: Some(6)
124 }),
125 time: None,
126 }
127 );
128 assert_eq!(
129 partial_datetime::<_, InputError<_>>(&mut "2015-W05-1").unwrap(),
130 PartialDateTime {
131 date: Some(PartialDate::YWD {
132 year: Some(2015),
133 week: Some(5),
134 day: Some(1)
135 }),
136 time: None,
137 }
138 );
139 assert_eq!(
140 partial_datetime::<_, InputError<_>>(&mut "2015-W05").unwrap(),
141 PartialDateTime {
142 date: Some(PartialDate::YWD {
143 year: Some(2015),
144 week: Some(5),
145 day: None
146 }),
147 time: None,
148 }
149 );
150 assert_eq!(
152 partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(),
153 PartialDateTime {
154 date: Some(PartialDate::YDDD {
155 year: Some(2015),
156 day: Some(156)
157 }),
158 time: None,
159 }
160 );
161 assert_eq!(
162 partial_datetime::<_, InputError<_>>(&mut "2015-156").unwrap(),
163 PartialDateTime {
164 date: Some(PartialDate::YDDD {
165 year: Some(2015),
166 day: Some(156)
167 }),
168 time: None,
169 }
170 );
171 }
172
173 #[test]
174 fn partial_ymd() {
175 assert_eq!(
176 partial_end_datetime::<_, InputError<_>>(
177 &mut "2015-06-26",
178 &PartialDateTime {
179 date: Some(PartialDate::YMD {
180 year: Some(2015),
181 month: Some(6),
182 day: Some(25)
183 }),
184 time: None,
185 }
186 )
187 .unwrap(),
188 PartialDateTime {
189 date: Some(PartialDate::YMD {
190 year: Some(2015),
191 month: Some(6),
192 day: Some(26)
193 }),
194 time: None,
195 }
196 );
197 assert_eq!(
198 partial_end_datetime::<_, InputError<_>>(
199 &mut "06-26",
200 &PartialDateTime {
201 date: Some(PartialDate::YMD {
202 year: Some(2015),
203 month: Some(6),
204 day: Some(25)
205 }),
206 time: None,
207 }
208 )
209 .unwrap(),
210 PartialDateTime {
211 date: Some(PartialDate::YMD {
212 year: Some(2015),
213 month: Some(6),
214 day: Some(26)
215 }),
216 time: None,
217 }
218 );
219 assert_eq!(
220 partial_end_datetime::<_, InputError<_>>(
221 &mut "26",
222 &PartialDateTime {
223 date: Some(PartialDate::YMD {
224 year: Some(2015),
225 month: Some(6),
226 day: Some(25)
227 }),
228 time: None,
229 }
230 )
231 .unwrap(),
232 PartialDateTime {
233 date: Some(PartialDate::YMD {
234 year: Some(2015),
235 month: Some(6),
236 day: Some(26)
237 }),
238 time: None,
239 }
240 );
241 }
242
243 #[test]
244 fn partial_ywd() {
245 assert_eq!(
246 partial_end_datetime::<_, InputError<_>>(
247 &mut "2024-W51-4",
248 &PartialDateTime {
249 date: Some(PartialDate::YWD {
250 year: Some(2024),
251 week: Some(51),
252 day: Some(3)
253 }),
254 time: None,
255 }
256 )
257 .unwrap(),
258 PartialDateTime {
259 date: Some(PartialDate::YWD {
260 year: Some(2024),
261 week: Some(51),
262 day: Some(4)
263 }),
264 time: None,
265 }
266 );
267 assert_eq!(
268 partial_end_datetime::<_, InputError<_>>(
269 &mut "W51-4",
270 &PartialDateTime {
271 date: Some(PartialDate::YWD {
272 year: Some(2024),
273 week: Some(51),
274 day: Some(3)
275 }),
276 time: None,
277 }
278 )
279 .unwrap(),
280 PartialDateTime {
281 date: Some(PartialDate::YWD {
282 year: Some(2024),
283 week: Some(51),
284 day: Some(4)
285 }),
286 time: None,
287 }
288 );
289 assert_eq!(
290 partial_end_datetime::<_, InputError<_>>(
291 &mut "4",
292 &PartialDateTime {
293 date: Some(PartialDate::YWD {
294 year: Some(2024),
295 week: Some(51),
296 day: Some(3)
297 }),
298 time: None,
299 }
300 )
301 .unwrap(),
302 PartialDateTime {
303 date: Some(PartialDate::YWD {
304 year: Some(2024),
305 week: Some(51),
306 day: Some(4)
307 }),
308 time: None,
309 }
310 );
311 }
312
313 #[test]
314 fn partial_yddd() {
315 assert_eq!(
316 partial_end_datetime::<_, InputError<_>>(
317 &mut "083",
318 &PartialDateTime {
319 date: Some(PartialDate::YDDD {
320 year: Some(2025),
321 day: Some(82)
322 }),
323 time: None,
324 }
325 )
326 .unwrap(),
327 PartialDateTime {
328 date: Some(PartialDate::YDDD {
329 year: Some(2025),
330 day: Some(83)
331 }),
332 time: None,
333 }
334 );
335 }
336}