Skip to content

Commit 294bd5e

Browse files
committed
use proper error mode in items::parse()
1 parent 6074530 commit 294bd5e

File tree

1 file changed

+68
-30
lines changed

1 file changed

+68
-30
lines changed

src/items/mod.rs

Lines changed: 68 additions & 30 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
};
@@ -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+
213222
pub 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

Comments
 (0)