Skip to content

Commit f1e20cc

Browse files
committed
use proper error mode in items::parse()
1 parent e96aedd commit f1e20cc

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
};
@@ -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,45 +229,35 @@ 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;
234236
if d.year.is_some() {
235237
year_seen = true;
236238
}
237239
}
238-
Item::Time(_) => {
240+
Item::Time(ref t) => {
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;
245+
if t.offset.is_some() {
246+
tz_seen = true;
247+
}
247248
}
248249
Item::Year(_) => {
249250
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));
251+
return Err(expect_error(input, "year cannot appear more than once"));
255252
}
256253
year_seen = true;
257254
}
258255
Item::TimeZone(_) => {
259256
if tz_seen {
260-
let mut ctx_err = ContextError::new();
261-
ctx_err.push(StrContext::Expected(StrContextValue::Description(
257+
return Err(expect_error(
258+
input,
262259
"timezone cannot appear more than once",
263-
)));
264-
return Err(ErrMode::Backtrack(ctx_err));
260+
));
265261
}
266262
tz_seen = true;
267263
}
@@ -276,7 +272,7 @@ pub fn parse(input: &mut &str) -> ModalResult<Vec<Item>> {
276272

277273
space.parse_next(input)?;
278274
if !input.is_empty() {
279-
return Err(ErrMode::Backtrack(ContextError::new()));
275+
return Err(expect_error(input, "unexpected input"));
280276
}
281277

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

0 commit comments

Comments
 (0)