@@ -33,6 +33,7 @@ mod ordinal;
33
33
mod relative;
34
34
mod time;
35
35
mod weekday;
36
+
36
37
mod epoch {
37
38
use winnow:: { combinator:: preceded, ModalResult , Parser } ;
38
39
@@ -41,6 +42,7 @@ mod epoch {
41
42
s ( preceded ( "@" , dec_int) ) . parse_next ( input)
42
43
}
43
44
}
45
+
44
46
mod timezone {
45
47
use super :: time;
46
48
use winnow:: ModalResult ;
@@ -53,12 +55,11 @@ mod timezone {
53
55
use chrono:: NaiveDate ;
54
56
use chrono:: { DateTime , Datelike , FixedOffset , TimeZone , Timelike } ;
55
57
56
- use winnow:: error:: { StrContext , StrContextValue } ;
57
58
use winnow:: {
58
59
ascii:: { digit1, multispace0} ,
59
60
combinator:: { alt, delimited, not, opt, peek, preceded, repeat, separated, trace} ,
60
- error:: { ContextError , ErrMode , ParserError } ,
61
- stream:: AsChar ,
61
+ error:: { AddContext , ContextError , ErrMode , ParserError , StrContext , StrContextValue } ,
62
+ stream:: { AsChar , Stream } ,
62
63
token:: { none_of, one_of, take_while} ,
63
64
ModalResult , Parser ,
64
65
} ;
@@ -210,6 +211,14 @@ pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
210
211
. parse_next ( input)
211
212
}
212
213
214
+ fn expect_error ( input : & mut & str , reason : & ' static str ) -> ErrMode < ContextError > {
215
+ ErrMode :: Cut ( ContextError :: new ( ) ) . add_context (
216
+ input,
217
+ & input. checkpoint ( ) ,
218
+ StrContext :: Expected ( StrContextValue :: Description ( reason) ) ,
219
+ )
220
+ }
221
+
213
222
pub fn parse ( input : & mut & str ) -> ModalResult < Vec < Item > > {
214
223
let mut items = Vec :: new ( ) ;
215
224
let mut date_seen = false ;
@@ -223,13 +232,10 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
223
232
match item {
224
233
Item :: DateTime ( ref dt) => {
225
234
if date_seen || time_seen {
226
- let mut ctx_err = ContextError :: new ( ) ;
227
- ctx_err. push ( StrContext :: Expected (
228
- winnow:: error:: StrContextValue :: Description (
229
- "date or time cannot appear more than once" ,
230
- ) ,
235
+ return Err ( expect_error (
236
+ input,
237
+ "date or time cannot appear more than once" ,
231
238
) ) ;
232
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
233
239
}
234
240
235
241
date_seen = true ;
@@ -240,45 +246,35 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
240
246
}
241
247
Item :: Date ( ref d) => {
242
248
if date_seen {
243
- let mut ctx_err = ContextError :: new ( ) ;
244
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
245
- "date cannot appear more than once" ,
246
- ) ) ) ;
247
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
249
+ return Err ( expect_error ( input, "date cannot appear more than once" ) ) ;
248
250
}
249
251
250
252
date_seen = true ;
251
253
if d. year . is_some ( ) {
252
254
year_seen = true ;
253
255
}
254
256
}
255
- Item :: Time ( _ ) => {
257
+ Item :: Time ( ref t ) => {
256
258
if time_seen {
257
- let mut ctx_err = ContextError :: new ( ) ;
258
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
259
- "time cannot appear more than once" ,
260
- ) ) ) ;
261
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
259
+ return Err ( expect_error ( input, "time cannot appear more than once" ) ) ;
262
260
}
263
261
time_seen = true ;
262
+ if t. offset . is_some ( ) {
263
+ tz_seen = true ;
264
+ }
264
265
}
265
266
Item :: Year ( _) => {
266
267
if year_seen {
267
- let mut ctx_err = ContextError :: new ( ) ;
268
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
269
- "year cannot appear more than once" ,
270
- ) ) ) ;
271
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
268
+ return Err ( expect_error ( input, "year cannot appear more than once" ) ) ;
272
269
}
273
270
year_seen = true ;
274
271
}
275
272
Item :: TimeZone ( _) => {
276
273
if tz_seen {
277
- let mut ctx_err = ContextError :: new ( ) ;
278
- ctx_err . push ( StrContext :: Expected ( StrContextValue :: Description (
274
+ return Err ( expect_error (
275
+ input ,
279
276
"timezone cannot appear more than once" ,
280
- ) ) ) ;
281
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
277
+ ) ) ;
282
278
}
283
279
tz_seen = true ;
284
280
}
@@ -293,7 +289,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
293
289
294
290
space. parse_next ( input) ?;
295
291
if !input. is_empty ( ) {
296
- return Err ( ErrMode :: Backtrack ( ContextError :: new ( ) ) ) ;
292
+ return Err ( expect_error ( input , "unexpected input" ) ) ;
297
293
}
298
294
299
295
Ok ( items)
@@ -557,4 +553,46 @@ mod tests {
557
553
test_eq_fmt( "%Y-%m-%d %H:%M:%S %:z" , "Jul 17 06:14:49 2024 BRT" ) ,
558
554
) ;
559
555
}
556
+
557
+ #[ test]
558
+ fn invalid ( ) {
559
+ let result = parse ( & mut "2025-05-19 2024-05-20 06:14:49" ) ;
560
+ assert ! ( result. is_err( ) ) ;
561
+ assert ! ( result
562
+ . unwrap_err( )
563
+ . to_string( )
564
+ . contains( "date or time cannot appear more than once" ) ) ;
565
+
566
+ let result = parse ( & mut "2025-05-19 2024-05-20" ) ;
567
+ assert ! ( result. is_err( ) ) ;
568
+ assert ! ( result
569
+ . unwrap_err( )
570
+ . to_string( )
571
+ . contains( "date cannot appear more than once" ) ) ;
572
+
573
+ let result = parse ( & mut "06:14:49 06:14:49" ) ;
574
+ assert ! ( result. is_err( ) ) ;
575
+ assert ! ( result
576
+ . unwrap_err( )
577
+ . to_string( )
578
+ . contains( "time cannot appear more than once" ) ) ;
579
+
580
+ let result = parse ( & mut "2025-05-19 2024" ) ;
581
+ assert ! ( result. is_err( ) ) ;
582
+ assert ! ( result
583
+ . unwrap_err( )
584
+ . to_string( )
585
+ . contains( "year cannot appear more than once" ) ) ;
586
+
587
+ let result = parse ( & mut "2025-05-19 +00:00 +01:00" ) ;
588
+ assert ! ( result. is_err( ) ) ;
589
+ assert ! ( result
590
+ . unwrap_err( )
591
+ . to_string( )
592
+ . contains( "timezone cannot appear more than once" ) ) ;
593
+
594
+ let result = parse ( & mut "2025-05-19 abcdef" ) ;
595
+ assert ! ( result. is_err( ) ) ;
596
+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "unexpected input" ) ) ;
597
+ }
560
598
}
0 commit comments