Skip to content

Commit

Permalink
Merge ParseError with Error, convert to alias
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Mar 12, 2024
1 parent 45d22e8 commit 81bfe78
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 88 deletions.
34 changes: 34 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ use core::fmt;
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// There is not enough information to create a date/time.
///
/// An example is parsing a string with not enough date/time fields, or the result of a
/// time that is ambiguous during a time zone transition (due to for example DST).
Ambiguous,

/// A date or datetime does not exist.
///
/// Examples are:
Expand All @@ -14,25 +20,53 @@ pub enum Error {
/// - a leap second on a non-minute boundary.
DoesNotExist,

/// Some of the date or time components are not consistent with each other.
///
/// An example is parsing 'Sunday 2023-04-21', while that date is a Friday.
Inconsistent,

/// One or more of the arguments to a function are invalid.
///
/// An example is creating a `NaiveTime` with 25 as the hour value.
InvalidArgument,

/// Character does not match with the expected format (during parsing).
InvalidCharacter,

/// The result, or an intermediate value necessary for calculating a result, would be out of
/// range.
///
/// An example is a date for the year 500.000, which is out of the range supported by chrono's
/// types.
OutOfRange,

/// All formatting items have been read but there is a remaining input.
TooLong,

/// The input string has been prematurely ended.
// TEMPORARY
TooShort,

/// The format string contains a formatting specifier that is not supported.
UnsupportedSpecifier,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Ambiguous => write!(f, "not enough information for a unique date and time"),
Error::DoesNotExist => write!(f, "date or datetime does not exist"),
Error::Inconsistent => {
write!(f, "some of the date or time components are not consistent with each other")
}
Error::InvalidArgument => write!(f, "invalid parameter"),
Error::InvalidCharacter => write!(f, "input doesn't match with the expected format"),
Error::OutOfRange => write!(f, "date outside of the supported range"),
Error::TooLong => write!(f, "trailing input"),
Error::TooShort => write!(f, "premature end of input"),
Error::UnsupportedSpecifier => {
write!(f, "format string contains a formatting specifier that is not supported")
}
}
}
}
Expand Down
83 changes: 9 additions & 74 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@
use alloc::boxed::Box;
use core::fmt;
use core::str::FromStr;
#[cfg(feature = "std")]
use std::error::Error;

use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
use crate::{Error, Month, ParseMonthError, ParseWeekdayError, Weekday};

mod formatting;
mod parsed;
Expand Down Expand Up @@ -381,82 +379,19 @@ impl<'a> Item<'a> {
}

/// An error from the `parse` function.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub struct ParseError(ParseErrorKind);

impl ParseError {
/// The category of parse error
pub const fn kind(&self) -> ParseErrorKind {
self.0
}
}

/// The category of parse error
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub enum ParseErrorKind {
/// Given field is out of permitted range.
OutOfRange,

/// There is no possible date and time value with given set of fields.
///
/// This does not include the out-of-range conditions, which are trivially invalid.
/// It includes the case that there are one or more fields that are inconsistent to each other.
Impossible,

/// Given set of fields is not enough to make a requested date and time value.
///
/// Note that there *may* be a case that given fields constrain the possible values so much
/// that there is a unique possible value. Chrono only tries to be correct for
/// most useful sets of fields however, as such constraint solving can be expensive.
NotEnough,

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

/// The input string has been prematurely ended.
TooShort,

/// All formatting items have been read but there is a remaining input.
TooLong,

/// There was an error on the formatting string, or there were non-supported formating items.
BadFormat,
}
pub type ParseError = Error;

/// Same as `Result<T, ParseError>`.
pub type ParseResult<T> = Result<T, ParseError>;

impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ParseErrorKind::OutOfRange => write!(f, "input is out of range"),
ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"),
ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"),
ParseErrorKind::Invalid => write!(f, "input contains invalid characters"),
ParseErrorKind::TooShort => write!(f, "premature end of input"),
ParseErrorKind::TooLong => write!(f, "trailing input"),
ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"),
}
}
}

#[cfg(feature = "std")]
impl Error for ParseError {
#[allow(deprecated)]
fn description(&self) -> &str {
"parser error, see to_string() for details"
}
}

// to be used in this module and submodules
pub(crate) const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
pub(crate) const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
pub(crate) const OUT_OF_RANGE: Error = Error::InvalidArgument;
const IMPOSSIBLE: Error = Error::Inconsistent;
const NOT_ENOUGH: Error = Error::Ambiguous;
const INVALID: Error = Error::InvalidCharacter;
const TOO_SHORT: Error = Error::TooShort;
pub(crate) const TOO_LONG: Error = Error::TooLong;
const BAD_FORMAT: Error = Error::UnsupportedSpecifier;

// this implementation is here only because we need some private code from `scan`

Expand Down
2 changes: 1 addition & 1 deletion src/format/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,6 @@ mod tests {
fn test_issue_1010() {
let dt = crate::NaiveDateTime::parse_from_str("\u{c}SUN\u{e}\u{3000}\0m@J\u{3000}\0\u{3000}\0m\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}\0SU\u{c}\u{c}",
"\u{c}\u{c}%A\u{c}\u{b}\0SUN\u{c}\u{c}\u{c}SUNN\u{c}\u{c}\u{c}SUN\u{c}\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}%a");
assert_eq!(dt, Err(ParseError(ParseErrorKind::Invalid)));
assert_eq!(dt, Err(Error::InvalidCharacter));
}
}
18 changes: 5 additions & 13 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone};
use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday};
use crate::{DateTime, Datelike, Error, TimeDelta, Timelike, Weekday};

/// A type to hold parsed fields of date and time that can check all fields are consistent.
///
Expand Down Expand Up @@ -59,8 +59,8 @@ use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday};
///
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[cfg_attr(feature = "alloc", doc = "```rust")]
/// use chrono::format::{ParseErrorKind, Parsed};
/// use chrono::Weekday;
/// use chrono::format::Parsed;
/// use chrono::{Error, Weekday};
///
/// let mut parsed = Parsed::default();
/// parsed
Expand All @@ -85,12 +85,7 @@ use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday};
/// .set_minute(26)?
/// .set_second(40)?
/// .set_offset(0)?;
/// let result = parsed.to_datetime();
///
/// assert!(result.is_err());
/// if let Err(error) = result {
/// assert_eq!(error.kind(), ParseErrorKind::Impossible);
/// }
/// assert_eq!(parsed.to_datetime(), Err(Error::Inconsistent));
/// # Ok::<(), chrono::ParseError>(())
/// ```
///
Expand Down Expand Up @@ -766,16 +761,13 @@ impl Parsed {

Ok(datetime)
} else if let Some(timestamp) = self.timestamp {
use super::ParseError as PE;
use super::ParseErrorKind;

// If the date fields are inconsistent, out of range, or if the date does not exist,
// there is no point proceeding.
//
// If the date or time fields are not enough it is not an error (which is the only error
// `to_naive_time` can return, so no need to check).
match date {
Err(PE(ParseErrorKind::NotEnough)) => {}
Err(Error::Ambiguous) => {}
Err(e) => return Err(e),
_ => {}
}
Expand Down

0 comments on commit 81bfe78

Please sign in to comment.