@@ -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,45 +229,35 @@ 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 ;
234
236
if d. year . is_some ( ) {
235
237
year_seen = true ;
236
238
}
237
239
}
238
- Item :: Time ( _ ) => {
240
+ Item :: Time ( ref t ) => {
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 ;
245
+ if t. offset . is_some ( ) {
246
+ tz_seen = true ;
247
+ }
247
248
}
248
249
Item :: Year ( _) => {
249
250
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) ) ;
251
+ return Err ( expect_error ( input, "year cannot appear more than once" ) ) ;
255
252
}
256
253
year_seen = true ;
257
254
}
258
255
Item :: TimeZone ( _) => {
259
256
if tz_seen {
260
- let mut ctx_err = ContextError :: new ( ) ;
261
- ctx_err . push ( StrContext :: Expected ( StrContextValue :: Description (
257
+ return Err ( expect_error (
258
+ input ,
262
259
"timezone cannot appear more than once" ,
263
- ) ) ) ;
264
- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
260
+ ) ) ;
265
261
}
266
262
tz_seen = true ;
267
263
}
@@ -276,7 +272,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
276
272
277
273
space. parse_next ( input) ?;
278
274
if !input. is_empty ( ) {
279
- return Err ( ErrMode :: Backtrack ( ContextError :: new ( ) ) ) ;
275
+ return Err ( expect_error ( input , "unexpected input" ) ) ;
280
276
}
281
277
282
278
Ok ( items)
@@ -540,4 +536,46 @@ mod tests {
540
536
test_eq_fmt( "%Y-%m-%d %H:%M:%S %Z" , "Jul 17 06:14:49 2024 BRT" ) ,
541
537
) ;
542
538
}
539
+
540
+ #[ test]
541
+ fn invalid ( ) {
542
+ let result = parse ( & mut "2025-05-19 2024-05-20 06:14:49" ) ;
543
+ assert ! ( result. is_err( ) ) ;
544
+ assert ! ( result
545
+ . unwrap_err( )
546
+ . to_string( )
547
+ . contains( "date or time cannot appear more than once" ) ) ;
548
+
549
+ let result = parse ( & mut "2025-05-19 2024-05-20" ) ;
550
+ assert ! ( result. is_err( ) ) ;
551
+ assert ! ( result
552
+ . unwrap_err( )
553
+ . to_string( )
554
+ . contains( "date cannot appear more than once" ) ) ;
555
+
556
+ let result = parse ( & mut "06:14:49 06:14:49" ) ;
557
+ assert ! ( result. is_err( ) ) ;
558
+ assert ! ( result
559
+ . unwrap_err( )
560
+ . to_string( )
561
+ . contains( "time cannot appear more than once" ) ) ;
562
+
563
+ let result = parse ( & mut "2025-05-19 2024" ) ;
564
+ assert ! ( result. is_err( ) ) ;
565
+ assert ! ( result
566
+ . unwrap_err( )
567
+ . to_string( )
568
+ . contains( "year cannot appear more than once" ) ) ;
569
+
570
+ let result = parse ( & mut "2025-05-19 +00:00 +01:00" ) ;
571
+ assert ! ( result. is_err( ) ) ;
572
+ assert ! ( result
573
+ . unwrap_err( )
574
+ . to_string( )
575
+ . contains( "timezone cannot appear more than once" ) ) ;
576
+
577
+ let result = parse ( & mut "2025-05-19 abcdef" ) ;
578
+ assert ! ( result. is_err( ) ) ;
579
+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "unexpected input" ) ) ;
580
+ }
543
581
}
0 commit comments