Skip to content

Commit 4615d76

Browse files
committed
use proper error mode in items::parse()
1 parent cc7c143 commit 4615d76

File tree

1 file changed

+64
-29
lines changed

1 file changed

+64
-29
lines changed

src/items/mod.rs

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod ordinal;
3333
mod relative;
3434
mod time;
3535
mod weekday;
36+
3637
mod 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+
4446
mod timezone {
4547
use super::time;
4648
use winnow::ModalResult;
@@ -53,12 +55,11 @@ mod timezone {
5355
use chrono::NaiveDate;
5456
use chrono::{DateTime, Datelike, FixedOffset, TimeZone, Timelike};
5557

56-
use winnow::error::{StrContext, StrContextValue};
5758
use 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+
196205
pub 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

Comments
 (0)