@@ -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
} ;
@@ -193,6 +194,14 @@ pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
193
194
. parse_next ( input)
194
195
}
195
196
197
+ fn expect_error ( input : & mut & str , reason : & ' static str ) -> ErrMode < ContextError > {
198
+ ErrMode :: Cut ( ContextError :: new ( ) ) . add_context (
199
+ input,
200
+ & input. checkpoint ( ) ,
201
+ StrContext :: Expected ( StrContextValue :: Description ( reason) ) ,
202
+ )
203
+ }
204
+
196
205
pub fn parse ( input : & mut & str ) -> ModalResult < Vec < Item > > {
197
206
let mut items = Vec :: new ( ) ;
198
207
let mut date_seen = false ;
@@ -206,13 +215,10 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
206
215
match item {
207
216
Item :: DateTime ( ref dt) => {
208
217
if date_seen || time_seen {
209
- let mut ctx_err = ContextError :: new ( ) ;
210
- ctx_err. push ( StrContext :: Expected (
211
- winnow:: error:: StrContextValue :: Description (
212
- "date or time cannot appear more than once" ,
213
- ) ,
218
+ return Err ( expect_error (
219
+ input,
220
+ "date or time cannot appear more than once" ,
214
221
) ) ;
215
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
216
222
}
217
223
218
224
date_seen = true ;
@@ -223,11 +229,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
223
229
}
224
230
Item :: Date ( ref d) => {
225
231
if date_seen {
226
- let mut ctx_err = ContextError :: new ( ) ;
227
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
228
- "date cannot appear more than once" ,
229
- ) ) ) ;
230
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
232
+ return Err ( expect_error ( input, "date cannot appear more than once" ) ) ;
231
233
}
232
234
233
235
date_seen = true ;
@@ -237,31 +239,22 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
237
239
}
238
240
Item :: Time ( _) => {
239
241
if time_seen {
240
- let mut ctx_err = ContextError :: new ( ) ;
241
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
242
- "time cannot appear more than once" ,
243
- ) ) ) ;
244
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
242
+ return Err ( expect_error ( input, "time cannot appear more than once" ) ) ;
245
243
}
246
244
time_seen = true ;
247
245
}
248
246
Item :: Year ( _) => {
249
247
if year_seen {
250
- let mut ctx_err = ContextError :: new ( ) ;
251
- ctx_err. push ( StrContext :: Expected ( StrContextValue :: Description (
252
- "year cannot appear more than once" ,
253
- ) ) ) ;
254
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
248
+ return Err ( expect_error ( input, "year cannot appear more than once" ) ) ;
255
249
}
256
250
year_seen = true ;
257
251
}
258
252
Item :: TimeZone ( _) => {
259
253
if tz_seen {
260
- let mut ctx_err = ContextError :: new ( ) ;
261
- ctx_err . push ( StrContext :: Expected ( StrContextValue :: Description (
254
+ return Err ( expect_error (
255
+ input ,
262
256
"timezone cannot appear more than once" ,
263
- ) ) ) ;
264
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
257
+ ) ) ;
265
258
}
266
259
tz_seen = true ;
267
260
}
@@ -276,7 +269,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
276
269
277
270
space. parse_next ( input) ?;
278
271
if !input. is_empty ( ) {
279
- return Err ( ErrMode :: Backtrack ( ContextError :: new ( ) ) ) ;
272
+ return Err ( expect_error ( input , "unexpected input" ) ) ;
280
273
}
281
274
282
275
Ok ( items)
@@ -535,4 +528,46 @@ mod tests {
535
528
test_eq_fmt( "%Y-%m-%d %H:%M:%S %Z" , "Jul 17 06:14:49 2024 BRT" ) ,
536
529
) ;
537
530
}
531
+
532
+ #[ test]
533
+ fn invalid ( ) {
534
+ let result = parse ( & mut "2025-05-19 2024-05-20 06:14:49" ) ;
535
+ assert ! ( result. is_err( ) ) ;
536
+ assert ! ( result
537
+ . unwrap_err( )
538
+ . to_string( )
539
+ . contains( "date or time cannot appear more than once" ) ) ;
540
+
541
+ let result = parse ( & mut "2025-05-19 2024-05-20" ) ;
542
+ assert ! ( result. is_err( ) ) ;
543
+ assert ! ( result
544
+ . unwrap_err( )
545
+ . to_string( )
546
+ . contains( "date cannot appear more than once" ) ) ;
547
+
548
+ let result = parse ( & mut "06:14:49 06:14:49" ) ;
549
+ assert ! ( result. is_err( ) ) ;
550
+ assert ! ( result
551
+ . unwrap_err( )
552
+ . to_string( )
553
+ . contains( "time cannot appear more than once" ) ) ;
554
+
555
+ let result = parse ( & mut "2025-05-19 2024" ) ;
556
+ assert ! ( result. is_err( ) ) ;
557
+ assert ! ( result
558
+ . unwrap_err( )
559
+ . to_string( )
560
+ . contains( "year cannot appear more than once" ) ) ;
561
+
562
+ let result = parse ( & mut "2025-05-19 +00:00 +01:00" ) ;
563
+ assert ! ( result. is_err( ) ) ;
564
+ assert ! ( result
565
+ . unwrap_err( )
566
+ . to_string( )
567
+ . contains( "timezone cannot appear more than once" ) ) ;
568
+
569
+ let result = parse ( & mut "2025-05-19 abcdef" ) ;
570
+ assert ! ( result. is_err( ) ) ;
571
+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "unexpected input" ) ) ;
572
+ }
538
573
}
0 commit comments