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