Skip to content

Commit

Permalink
add ParseErrorKind::TooMany
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoGorelli committed May 22, 2023
1 parent bf9f876 commit 9c893e4
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 57 deletions.
8 changes: 4 additions & 4 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parse(&mut parsed, s, ITEMS.iter(), false, false)?;
parsed.to_datetime()
}

Expand All @@ -595,7 +595,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parse(&mut parsed, s, ITEMS.iter(), false, false)?;
parsed.to_datetime()
}

Expand All @@ -620,7 +620,7 @@ impl DateTime<FixedOffset> {
/// ```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parse(&mut parsed, s, StrftimeItems::new(fmt), false, false)?;
parsed.to_datetime()
}

Expand Down Expand Up @@ -654,7 +654,7 @@ impl DateTime<FixedOffset> {
fmt: &str,
) -> ParseResult<(DateTime<FixedOffset>, &'a str)> {
let mut parsed = Parsed::new();
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt), false, false)?;
parsed.to_datetime().map(|d| (d, remainder))
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ pub enum ParseErrorKind {
/// most useful sets of fields however, as such constraint solving can be expensive.
NotEnough,

/// The input string contains directives which aren't valid for the given type, e.g. "hour"
/// for NaiveDate.
TooMany,

/// The input string has some invalid character sequence for given formatting items.
Invalid,

Expand Down Expand Up @@ -424,6 +428,7 @@ impl Error for ParseError {
const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
const TOO_MANY: ParseError = ParseError(ParseErrorKind::TooMany);
const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
Expand Down
128 changes: 99 additions & 29 deletions src/format/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use core::usize;
use super::scan;
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed};
use super::{ParseError, ParseErrorKind, ParseResult};
use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_SHORT};
use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_MANY, TOO_SHORT};
use crate::{DateTime, FixedOffset, Weekday};

fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
Expand Down Expand Up @@ -244,12 +244,18 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
/// so one can prepend any number of zeroes before numbers.
///
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
pub fn parse<'a, I, B>(
parsed: &mut Parsed,
s: &str,
items: I,
is_date: bool,
is_time: bool,
) -> ParseResult<()>
where
I: Iterator<Item = B>,
B: Borrow<Item<'a>>,
{
parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
parse_internal(parsed, s, items, is_date, is_time).map(|_| ()).map_err(|(_s, e)| e)
}

/// Tries to parse given string into `parsed` with given formatting items.
Expand All @@ -270,12 +276,14 @@ pub fn parse_and_remainder<'a, 'b, I, B>(
parsed: &mut Parsed,
s: &'b str,
items: I,
is_date: bool,
is_time: bool,
) -> ParseResult<&'b str>
where
I: Iterator<Item = B>,
B: Borrow<Item<'a>>,
{
match parse_internal(parsed, s, items) {
match parse_internal(parsed, s, items, is_date, is_time) {
Ok(s) => Ok(s),
Err((s, ParseError(ParseErrorKind::TooLong))) => Ok(s),
Err((_s, e)) => Err(e),
Expand All @@ -286,6 +294,8 @@ fn parse_internal<'a, 'b, I, B>(
parsed: &mut Parsed,
mut s: &'b str,
items: I,
is_date: bool,
is_time: bool,
) -> Result<&'b str, (&'b str, ParseError)>
where
I: Iterator<Item = B>,
Expand Down Expand Up @@ -364,26 +374,86 @@ where
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;

let (width, signed, set): (usize, bool, Setter) = match *spec {
Year => (4, true, Parsed::set_year),
YearDiv100 => (2, false, Parsed::set_year_div_100),
YearMod100 => (2, false, Parsed::set_year_mod_100),
IsoYear => (4, true, Parsed::set_isoyear),
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
Month => (2, false, Parsed::set_month),
Day => (2, false, Parsed::set_day),
WeekFromSun => (2, false, Parsed::set_week_from_sun),
WeekFromMon => (2, false, Parsed::set_week_from_mon),
IsoWeek => (2, false, Parsed::set_isoweek),
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
Ordinal => (3, false, Parsed::set_ordinal),
Hour => (2, false, Parsed::set_hour),
Hour12 => (2, false, Parsed::set_hour12),
Minute => (2, false, Parsed::set_minute),
Second => (2, false, Parsed::set_second),
Nanosecond => (9, false, Parsed::set_nanosecond),
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
Year => match is_time {
true => return Err((s, TOO_MANY)),
false => (4, true, Parsed::set_year),
},
YearDiv100 => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_year_div_100),
},
YearMod100 => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_year_mod_100),
},
IsoYear => match is_time {
true => return Err((s, TOO_MANY)),
false => (4, true, Parsed::set_isoyear),
},
IsoYearDiv100 => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_isoyear_div_100),
},
IsoYearMod100 => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_isoyear_mod_100),
},
Month => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_month),
},
Day => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_day),
},
WeekFromSun => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_week_from_sun),
},
WeekFromMon => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_week_from_mon),
},
IsoWeek => match is_time {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_isoweek),
},
NumDaysFromSun => match is_time {
true => return Err((s, TOO_MANY)),
false => (1, false, set_weekday_with_num_days_from_sunday),
},
WeekdayFromMon => match is_time {
true => return Err((s, TOO_MANY)),
false => (1, false, set_weekday_with_number_from_monday),
},
Ordinal => match is_time {
true => return Err((s, TOO_MANY)),
false => (3, false, Parsed::set_ordinal),
},
Hour => match is_date {
false => (2, false, Parsed::set_hour),
true => return Err((s, TOO_MANY)),
},
Hour12 => match is_date {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_hour12),
},
Minute => match is_date {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_minute),
},
Second => match is_date {
true => return Err((s, TOO_MANY)),
false => (2, false, Parsed::set_second),
},
Nanosecond => match is_date {
true => return Err((s, TOO_MANY)),
false => (9, false, Parsed::set_nanosecond),
},
Timestamp => match is_date {
true => return Err((s, TOO_MANY)),
false => (usize::MAX, false, Parsed::set_timestamp),
},

// for the future expansion
Internal(ref int) => match int._dummy {},
Expand Down Expand Up @@ -555,10 +625,10 @@ impl str::FromStr for DateTime<FixedOffset> {
];

let mut parsed = Parsed::new();
match parse_internal(&mut parsed, s, DATE_ITEMS.iter()) {
match parse_internal(&mut parsed, s, DATE_ITEMS.iter(), false, false) {
Err((remainder, e)) if e.0 == ParseErrorKind::TooLong => {
if remainder.starts_with('T') || remainder.starts_with(' ') {
parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?;
parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter(), false, false)?;
} else {
return Err(INVALID);
}
Expand All @@ -578,7 +648,7 @@ fn test_parse() {
// workaround for Rust issue #22255
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, items.iter())?;
parse(&mut parsed, s, items.iter(), false, false)?;
Ok(parsed)
}

Expand Down Expand Up @@ -1432,7 +1502,7 @@ fn test_rfc2822() {

fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter(), false, false)?;
parsed.to_datetime()
}

Expand Down Expand Up @@ -1539,7 +1609,7 @@ fn test_rfc3339() {

fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?;
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter(), false, false)?;
parsed.to_datetime()
}

Expand Down
19 changes: 9 additions & 10 deletions src/naive/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,13 +526,12 @@ impl NaiveDate {
/// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()));
/// ```
///
/// Time and offset is ignored for the purpose of parsing.
/// Time and offset are errors.
///
/// ```
/// # use chrono::NaiveDate;
/// # let parse_from_str = NaiveDate::parse_from_str;
/// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
/// Ok(NaiveDate::from_ymd_opt(2014, 5, 17).unwrap()));
/// assert!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z").is_err());
/// ```
///
/// Out-of-bound dates or insufficient fields are errors.
Expand All @@ -553,7 +552,7 @@ impl NaiveDate {
/// ```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parse(&mut parsed, s, StrftimeItems::new(fmt), true, false)?;
parsed.to_naive_date()
}

Expand All @@ -575,7 +574,7 @@ impl NaiveDate {
/// ```
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDate, &'a str)> {
let mut parsed = Parsed::new();
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt), true, false)?;
parsed.to_naive_date().map(|d| (d, remainder))
}

Expand Down Expand Up @@ -2050,7 +2049,7 @@ impl str::FromStr for NaiveDate {
];

let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parse(&mut parsed, s, ITEMS.iter(), true, false)?;
parsed.to_naive_date()
}
}
Expand Down Expand Up @@ -2932,10 +2931,9 @@ mod tests {
#[test]
fn test_date_parse_from_str() {
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
assert_eq!(
NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
Ok(ymd(2014, 5, 7))
); // ignore time and offset
assert!(
NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z").is_err(),
); // error on time and offset
assert_eq!(
NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u=%Y-%j"),
Ok(ymd(2015, 2, 2))
Expand All @@ -2947,6 +2945,7 @@ mod tests {
assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err());
assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err());
assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient
assert!(NaiveDate::parse_from_str("2014-01-01 00", "%Y-%m-%d %H").is_err()); // too many

assert_eq!(
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
Expand Down
6 changes: 3 additions & 3 deletions src/naive/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl NaiveDateTime {
///```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parse(&mut parsed, s, StrftimeItems::new(fmt), false, false)?;
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
}

Expand All @@ -321,7 +321,7 @@ impl NaiveDateTime {
/// ```
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDateTime, &'a str)> {
let mut parsed = Parsed::new();
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt), false, false)?;
parsed.to_naive_datetime_with_offset(0).map(|d| (d, remainder)) // no offset adjustment
}

Expand Down Expand Up @@ -1801,7 +1801,7 @@ impl str::FromStr for NaiveDateTime {
];

let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parse(&mut parsed, s, ITEMS.iter(), false, false)?;
parsed.to_naive_datetime_with_offset(0)
}
}
Expand Down
11 changes: 5 additions & 6 deletions src/naive/time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,12 @@ impl NaiveTime {
/// Ok(NaiveTime::from_hms_micro_opt(13, 23, 45, 678_900).unwrap()));
/// ```
///
/// Date and offset is ignored for the purpose of parsing.
/// Date and offset are errors.
///
/// ```
/// # use chrono::NaiveTime;
/// # let parse_from_str = NaiveTime::parse_from_str;
/// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
/// Ok(NaiveTime::from_hms_opt(12, 34, 56).unwrap()));
/// assert!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z").is_err());
/// ```
///
/// [Leap seconds](#leap-second-handling) are correctly handled by
Expand Down Expand Up @@ -480,7 +479,7 @@ impl NaiveTime {
/// ```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parse(&mut parsed, s, StrftimeItems::new(fmt), false, true)?;
parsed.to_naive_time()
}

Expand All @@ -502,7 +501,7 @@ impl NaiveTime {
/// ```
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveTime, &'a str)> {
let mut parsed = Parsed::new();
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt), false, true)?;
parsed.to_naive_time().map(|t| (t, remainder))
}

Expand Down Expand Up @@ -1313,7 +1312,7 @@ impl str::FromStr for NaiveTime {
];

let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parse(&mut parsed, s, ITEMS.iter(), false, true)?;
parsed.to_naive_time()
}
}
Expand Down
Loading

0 comments on commit 9c893e4

Please sign in to comment.