@@ -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} ;
@@ -193,6 +194,14 @@ pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
193194 . parse_next ( input)
194195}
195196
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+
196205pub fn parse ( input : & mut & str ) -> ModalResult < Vec < Item > > {
197206 let mut items = Vec :: new ( ) ;
198207 let mut date_seen = false ;
@@ -206,13 +215,10 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
206215 match item {
207216 Item :: DateTime ( ref dt) => {
208217 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" ,
214221 ) ) ;
215- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
216222 }
217223
218224 date_seen = true ;
@@ -223,11 +229,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
223229 }
224230 Item :: Date ( ref d) => {
225231 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" ) ) ;
231233 }
232234
233235 date_seen = true ;
@@ -237,31 +239,22 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
237239 }
238240 Item :: Time ( _) => {
239241 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" ) ) ;
245243 }
246244 time_seen = true ;
247245 }
248246 Item :: Year ( _) => {
249247 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" ) ) ;
255249 }
256250 year_seen = true ;
257251 }
258252 Item :: TimeZone ( _) => {
259253 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 ,
262256 "timezone cannot appear more than once" ,
263- ) ) ) ;
264- return Err ( ErrMode :: Backtrack ( ctx_err) ) ;
257+ ) ) ;
265258 }
266259 tz_seen = true ;
267260 }
@@ -276,7 +269,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
276269
277270 space. parse_next ( input) ?;
278271 if !input. is_empty ( ) {
279- return Err ( ErrMode :: Backtrack ( ContextError :: new ( ) ) ) ;
272+ return Err ( expect_error ( input , "unexpected input" ) ) ;
280273 }
281274
282275 Ok ( items)
@@ -535,4 +528,46 @@ mod tests {
535528 test_eq_fmt( "%Y-%m-%d %H:%M:%S %Z" , "Jul 17 06:14:49 2024 BRT" ) ,
536529 ) ;
537530 }
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+ }
538573}
0 commit comments