Skip to content

Commit 25749f1

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

File tree

1 file changed

+64
-27
lines changed

1 file changed

+64
-27
lines changed

src/items/mod.rs

Lines changed: 64 additions & 27 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,7 +55,8 @@ mod timezone {
5355
use chrono::NaiveDate;
5456
use chrono::{DateTime, Datelike, FixedOffset, TimeZone, Timelike};
5557

56-
use winnow::error::{StrContext, StrContextValue};
58+
use winnow::error::{AddContext, StrContext, StrContextValue};
59+
use winnow::stream::Stream;
5760
use winnow::{
5861
ascii::{digit1, multispace0},
5962
combinator::{alt, delimited, not, opt, peek, preceded, repeat, separated, trace},
@@ -193,6 +196,14 @@ pub fn parse_one(input: &mut &str) -> ModalResult<Item> {
193196
.parse_next(input)
194197
}
195198

199+
fn expect_error(input: &mut &str, reason: &'static str) -> ErrMode<ContextError> {
200+
ErrMode::Cut(ContextError::new()).add_context(
201+
input,
202+
&input.checkpoint(),
203+
StrContext::Expected(StrContextValue::Description(reason)),
204+
)
205+
}
206+
196207
pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
197208
let mut items = Vec::new();
198209
let mut date_seen = false;
@@ -206,13 +217,10 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
206217
match item {
207218
Item::DateTime(ref dt) => {
208219
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-
),
220+
return Err(expect_error(
221+
input,
222+
"date or time cannot appear more than once",
214223
));
215-
return Err(ErrMode::Backtrack(ctx_err));
216224
}
217225

218226
date_seen = true;
@@ -223,11 +231,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
223231
}
224232
Item::Date(ref d) => {
225233
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));
234+
return Err(expect_error(input, "date cannot appear more than once"));
231235
}
232236

233237
date_seen = true;
@@ -237,31 +241,22 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
237241
}
238242
Item::Time(_) => {
239243
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));
244+
return Err(expect_error(input, "time cannot appear more than once"));
245245
}
246246
time_seen = true;
247247
}
248248
Item::Year(_) => {
249249
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));
250+
return Err(expect_error(input, "year cannot appear more than once"));
255251
}
256252
year_seen = true;
257253
}
258254
Item::TimeZone(_) => {
259255
if tz_seen {
260-
let mut ctx_err = ContextError::new();
261-
ctx_err.push(StrContext::Expected(StrContextValue::Description(
256+
return Err(expect_error(
257+
input,
262258
"timezone cannot appear more than once",
263-
)));
264-
return Err(ErrMode::Backtrack(ctx_err));
259+
));
265260
}
266261
tz_seen = true;
267262
}
@@ -276,7 +271,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
276271

277272
space.parse_next(input)?;
278273
if !input.is_empty() {
279-
return Err(ErrMode::Backtrack(ContextError::new()));
274+
return Err(expect_error(input, "unexpected input"));
280275
}
281276

282277
Ok(items)
@@ -535,4 +530,46 @@ mod tests {
535530
test_eq_fmt("%Y-%m-%d %H:%M:%S %Z", "Jul 17 06:14:49 2024 BRT"),
536531
);
537532
}
533+
534+
#[test]
535+
fn invalid() {
536+
let result = parse(&mut "2025-05-19 2024-05-20 06:14:49");
537+
assert!(result.is_err());
538+
assert!(result
539+
.unwrap_err()
540+
.to_string()
541+
.contains("date or time cannot appear more than once"));
542+
543+
let result = parse(&mut "2025-05-19 2024-05-20");
544+
assert!(result.is_err());
545+
assert!(result
546+
.unwrap_err()
547+
.to_string()
548+
.contains("date cannot appear more than once"));
549+
550+
let result = parse(&mut "06:14:49 06:14:49");
551+
assert!(result.is_err());
552+
assert!(result
553+
.unwrap_err()
554+
.to_string()
555+
.contains("time cannot appear more than once"));
556+
557+
let result = parse(&mut "2025-05-19 2024");
558+
assert!(result.is_err());
559+
assert!(result
560+
.unwrap_err()
561+
.to_string()
562+
.contains("year cannot appear more than once"));
563+
564+
let result = parse(&mut "2025-05-19 +00:00 +01:00");
565+
assert!(result.is_err());
566+
assert!(result
567+
.unwrap_err()
568+
.to_string()
569+
.contains("timezone cannot appear more than once"));
570+
571+
let result = parse(&mut "2025-05-19 abcdef");
572+
assert!(result.is_err());
573+
assert!(result.unwrap_err().to_string().contains("unexpected input"));
574+
}
538575
}

0 commit comments

Comments
 (0)