@@ -33,6 +33,7 @@ mod ordinal;
3333mod relative;
3434mod time;
3535mod weekday;
36+
3637mod epoch {
3738 use winnow:: { combinator:: preceded, ModalResult , Parser } ;
3839
@@ -41,6 +42,7 @@ mod epoch {
4142 s ( preceded ( "@" , dec_int) ) . parse_next ( input)
4243 }
4344}
45+
4446mod timezone {
4547 use super :: time;
4648 use winnow:: ModalResult ;
@@ -53,12 +55,11 @@ mod timezone {
5355use chrono:: NaiveDate ;
5456use chrono:: { DateTime , Datelike , FixedOffset , TimeZone , Timelike } ;
5557
56- use winnow:: error:: { StrContext , StrContextValue } ;
5758use winnow:: {
5859 ascii:: { digit1, multispace0} ,
5960 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 } ,
6263 token:: { none_of, one_of, take_while} ,
6364 ModalResult , Parser ,
6465} ;
@@ -210,6 +211,14 @@ pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
210211 . parse_next ( input)
211212}
212213
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+
213222pub fn parse ( input : & mut & str ) -> ModalResult < Vec < Item > > {
214223 let mut items = Vec :: new ( ) ;
215224 let mut date_seen = false ;
@@ -223,13 +232,10 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
223232 match item {
224233 Item :: DateTime ( ref dt) => {
225234 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" ,
231238 ) ) ;
232- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
233239 }
234240
235241 date_seen = true ;
@@ -240,45 +246,35 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
240246 }
241247 Item :: Date ( ref d) => {
242248 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" ) ) ;
248250 }
249251
250252 date_seen = true ;
251253 if d. year . is_some ( ) {
252254 year_seen = true ;
253255 }
254256 }
255- Item :: Time ( _ ) => {
257+ Item :: Time ( ref t ) => {
256258 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" ) ) ;
262260 }
263261 time_seen = true ;
262+ if t. offset . is_some ( ) {
263+ tz_seen = true ;
264+ }
264265 }
265266 Item :: Year ( _) => {
266267 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" ) ) ;
272269 }
273270 year_seen = true ;
274271 }
275272 Item :: TimeZone ( _) => {
276273 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 ,
279276 "timezone cannot appear more than once" ,
280- ) ) ) ;
281- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
277+ ) ) ;
282278 }
283279 tz_seen = true ;
284280 }
@@ -293,7 +289,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
293289
294290 space. parse_next ( input) ?;
295291 if !input. is_empty ( ) {
296- return Err ( ErrMode :: Backtrack ( ContextError :: new ( ) ) ) ;
292+ return Err ( expect_error ( input , "unexpected input" ) ) ;
297293 }
298294
299295 Ok ( items)
@@ -557,4 +553,46 @@ mod tests {
557553 test_eq_fmt( "%Y-%m-%d %H:%M:%S %:z" , "Jul 17 06:14:49 2024 BRT" ) ,
558554 ) ;
559555 }
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+ }
560598}
0 commit comments