use crate::{options::Overflow, types::MonthCode};
use displaydoc::Display;
#[derive(Debug, Copy, Clone, PartialEq, Display)]
#[non_exhaustive]
pub enum DateError {
#[displaydoc("The {field} = {value} argument is out of range {min}..={max}")]
Range {
field: &'static str,
value: i32,
min: i32,
max: i32,
},
#[displaydoc("Unknown era")]
UnknownEra,
#[displaydoc("Unknown month code {0:?}")]
UnknownMonthCode(MonthCode),
}
impl core::error::Error for DateError {}
#[cfg(feature = "unstable")]
pub use unstable::DateFromFieldsError;
#[cfg(not(feature = "unstable"))]
pub(crate) use unstable::DateFromFieldsError;
mod unstable {
pub use super::*;
#[derive(Debug, Copy, Clone, PartialEq, Display)]
#[non_exhaustive]
pub enum DateFromFieldsError {
#[displaydoc("{0}")]
Range(RangeError),
#[displaydoc("Unknown era or invalid syntax")]
UnknownEra,
#[displaydoc("Invalid month code syntax")]
MonthCodeInvalidSyntax,
#[displaydoc("The specified month code does not exist in this calendar")]
MonthCodeNotInCalendar,
#[displaydoc("The specified month code exists in calendar, but not for this year")]
MonthCodeNotInYear,
#[displaydoc("Inconsistent year")]
InconsistentYear,
#[displaydoc("Inconsistent month")]
InconsistentMonth,
#[displaydoc("Not enough fields")]
NotEnoughFields,
}
impl core::error::Error for DateFromFieldsError {}
impl From<RangeError> for DateFromFieldsError {
#[inline]
fn from(value: RangeError) -> Self {
DateFromFieldsError::Range(value)
}
}
}
pub(crate) struct UnknownEraError;
impl From<UnknownEraError> for DateError {
#[inline]
fn from(_value: UnknownEraError) -> Self {
DateError::UnknownEra
}
}
impl From<UnknownEraError> for DateFromFieldsError {
#[inline]
fn from(_value: UnknownEraError) -> Self {
DateFromFieldsError::UnknownEra
}
}
#[derive(Debug)]
pub(crate) enum MonthCodeParseError {
InvalidSyntax,
}
impl From<MonthCodeParseError> for DateFromFieldsError {
#[inline]
fn from(value: MonthCodeParseError) -> Self {
match value {
MonthCodeParseError::InvalidSyntax => DateFromFieldsError::MonthCodeInvalidSyntax,
}
}
}
#[derive(Debug, PartialEq)]
pub(crate) enum MonthCodeError {
NotInCalendar,
NotInYear,
}
impl From<MonthCodeError> for DateFromFieldsError {
#[inline]
fn from(value: MonthCodeError) -> Self {
match value {
MonthCodeError::NotInCalendar => DateFromFieldsError::MonthCodeNotInCalendar,
MonthCodeError::NotInYear => DateFromFieldsError::MonthCodeNotInYear,
}
}
}
mod inner {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)] #[non_exhaustive]
pub enum EcmaReferenceYearError {
Unimplemented,
MonthCodeNotInCalendar,
}
}
#[cfg(feature = "unstable")]
pub use inner::EcmaReferenceYearError;
#[cfg(not(feature = "unstable"))]
pub(crate) use inner::EcmaReferenceYearError;
impl From<EcmaReferenceYearError> for DateFromFieldsError {
#[inline]
fn from(value: EcmaReferenceYearError) -> Self {
match value {
EcmaReferenceYearError::Unimplemented => DateFromFieldsError::NotEnoughFields,
EcmaReferenceYearError::MonthCodeNotInCalendar => {
DateFromFieldsError::MonthCodeNotInCalendar
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Display)]
#[displaydoc("The {field} = {value} argument is out of range {min}..={max}")]
#[allow(clippy::exhaustive_structs)]
pub struct RangeError {
pub field: &'static str,
pub value: i32,
pub min: i32,
pub max: i32,
}
impl core::error::Error for RangeError {}
impl From<RangeError> for DateError {
#[inline]
fn from(value: RangeError) -> Self {
let RangeError {
field,
value,
min,
max,
} = value;
DateError::Range {
field,
value,
min,
max,
}
}
}
pub(crate) fn range_check_with_overflow<T: Ord + Into<i32> + Copy>(
value: T,
field: &'static str,
bounds: core::ops::RangeInclusive<T>,
overflow: Overflow,
) -> Result<T, RangeError> {
if matches!(overflow, Overflow::Constrain) {
Ok(value.clamp(*bounds.start(), *bounds.end()))
} else {
range_check(value, field, bounds)
}
}
pub(crate) fn range_check<T: Ord + Into<i32> + Copy>(
value: T,
field: &'static str,
bounds: core::ops::RangeInclusive<T>,
) -> Result<T, RangeError> {
if !bounds.contains(&value) {
return Err(RangeError {
field,
value: value.into(),
min: (*bounds.start()).into(),
max: (*bounds.end()).into(),
});
}
Ok(value)
}