Skip to content

Commit

Permalink
Finalise datetime errors (#5771)
Browse files Browse the repository at this point in the history
Fixes #4336
  • Loading branch information
robertbastian authored Nov 5, 2024
1 parent c359486 commit 7067c87
Show file tree
Hide file tree
Showing 92 changed files with 1,242 additions and 1,049 deletions.
5 changes: 0 additions & 5 deletions components/calendar/src/japanese.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,6 @@ impl Date<JapaneseExtended> {
.new_japanese_date_inner(era, year, month, day)?;
Ok(Date::from_raw(inner, japanext_calendar))
}

#[doc(hidden)] // for testing
pub fn into_japanese_date(self) -> Date<Japanese> {
Date::from_raw(self.inner, self.calendar.0)
}
}

impl DateTime<Japanese> {
Expand Down
2 changes: 1 addition & 1 deletion components/calendar/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub enum FormattingEra {

impl FormattingEra {
/// Get a fallback era name suitable for display to the user when the real era name is not availabe
pub fn fallback_era(self) -> TinyStr16 {
pub fn fallback_name(self) -> TinyStr16 {
match self {
Self::Index(_idx, fallback) => fallback,
Self::Code(era) => era.0,
Expand Down
8 changes: 4 additions & 4 deletions components/datetime/src/fieldset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1085,15 +1085,15 @@ impl_zone_marker!(
/// use icu::calendar::Gregorian;
/// use icu::datetime::FixedCalendarDateTimeFormatter;
/// use icu::datetime::fieldset::Zs;
/// use icu::datetime::LoadError;
/// use icu::datetime::PatternLoadError;
/// use icu::locale::locale;
///
/// let result = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
/// &locale!("en").into(),
/// Zs::long(),
/// );
///
/// assert!(matches!(result, Err(LoadError::TypeTooNarrow(_))));
/// assert!(matches!(result, Err(PatternLoadError::TypeTooSpecific(_))));
/// ```
///
/// This style requires a [`ZoneVariant`], so
Expand Down Expand Up @@ -1302,15 +1302,15 @@ impl_zone_marker!(
/// use icu::calendar::Gregorian;
/// use icu::datetime::FixedCalendarDateTimeFormatter;
/// use icu::datetime::fieldset::Vs;
/// use icu::datetime::LoadError;
/// use icu::datetime::PatternLoadError;
/// use icu::locale::locale;
///
/// let result = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
/// &locale!("en").into(),
/// Vs::long(),
/// );
///
/// assert!(matches!(result, Err(LoadError::TypeTooNarrow(_))));
/// assert!(matches!(result, Err(PatternLoadError::TypeTooSpecific(_))));
/// ```
///
/// Since non-location names might change over time,
Expand Down
156 changes: 76 additions & 80 deletions components/datetime/src/format/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ use crate::time_zone::{
FormatTimeZone, FormatTimeZoneError, Iso8601Format, IsoFormat, IsoMinutes, IsoSeconds,
ResolvedNeoTimeZoneSkeleton,
};
#[cfg(doc)]
use crate::TypedDateTimeNames;
#[cfg(doc)]
use icu_calendar::types::YearInfo;

use core::fmt::{self, Write};
use fixed_decimal::FixedDecimal;
Expand Down Expand Up @@ -60,7 +64,7 @@ where
Ok(Ok(()))
} else {
result.with_part(writeable::Part::ERROR, |r| num.write_to(r))?;
Ok(Err(DateTimeWriteError::MissingFixedDecimalFormatter))
Ok(Err(DateTimeWriteError::FixedDecimalFormatterNotLoaded))
}
}

Expand Down Expand Up @@ -99,45 +103,67 @@ where
#[derive(Debug, PartialEq, Copy, Clone, displaydoc::Display)]
/// Error for `TryWriteable` implementations
pub enum DateTimeWriteError {
// Data not loaded
/// Missing FixedDecimalFormatter
#[displaydoc("FixedDecimalFormatter not loaded")]
MissingFixedDecimalFormatter,
/// Missing OrdinalRules
#[displaydoc("OrdinalRules not loaded")]
MissingOrdinalRules,
/// Missing WeekCalculator
#[displaydoc("WeekCalculator not loaded")]
MissingWeekCalculator,
/// TODO
#[displaydoc("Names for {0:?} not loaded")]
MissingNames(Field),

// Something not found in data
// TODO: Are these actionable? Can clients even invent their own months and days?
/// Missing month symbol
#[displaydoc("Cannot find symbol for month {0:?}")]
MissingMonthSymbol(MonthCode),
/// Missing era symbol
#[displaydoc("Cannot find symbol for era {0:?}")]
MissingEraSymbol(FormattingEra),
/// Missing weekday symbol
#[displaydoc("Cannot find symbol for weekday {0:?}")]
MissingWeekdaySymbol(IsoWeekday),

// Invalid input
/// Incomplete input
#[displaydoc("Incomplete input, missing value for {0:?}")]
MissingInputField(&'static str),
/// Cyclic year overflow
#[displaydoc("Cyclic year overflow, found {value}, maximum {max}")]
CyclicYearOverflow {
/// The [`MonthCode`] of the input is not valid for this calendar.
///
/// This is guaranteed not to happen for `icu::calendar` inputs, but may happen for custom inputs.
///
/// The output will contain the raw [`MonthCode`] as a fallback value.
#[displaydoc("Invalid month {0:?}")]
InvalidMonthCode(MonthCode),
/// The [`FormattingEra`] of the input is not valid for this calendar.
///
/// This is guaranteed not to happen for `icu::calendar` inputs, but may happen for custom inputs.
///
/// The output will contain [`FormattingEra::fallback_name`] as the fallback.
#[displaydoc("Invalid era {0:?}")]
InvalidEra(FormattingEra),
/// The [`YearInfo::cyclic`] of the input is not valid for this calendar.
///
/// This is guaranteed not to happen for `icu::calendar` inputs, but may happen for custom inputs.
///
/// The output will contain [`YearInfo::extended_year`] as a fallback value.
#[displaydoc("Invalid cyclic year {value} (maximum {max})")]
InvalidCyclicYear {
/// Value
value: usize,
/// Max
max: usize,
},

/// The [`FixedDecimalFormatter`] has not been loaded.
///
/// This *only* happens if the formatter has been created using
/// [`TypedDateTimeNames::with_pattern`], the pattern requires decimal
/// formatting, and the decimal formatter was not loaded.
///
/// The output will contain fallback values using Latin numerals.
#[displaydoc("FixedDecimalFormatter not loaded")]
FixedDecimalFormatterNotLoaded,
/// The localized names for a field have not been loaded.
///
/// This *only* happens if the formatter has been created using
/// [`TypedDateTimeNames::with_pattern`], and the pattern requires names
/// that were not loaded.
///
/// The output will contain fallback values using field identifiers (such as `tue` for `IsoWeekday::Tuesday`,
/// `M02` for month 2, etc.).
#[displaydoc("Names for {0:?} not loaded")]
NamesNotLoaded(Field),
/// An input field (such as "hour" or "month") is missing.
///
/// This *only* happens if the formatter has been created using
/// [`TypedDateTimeNames::with_pattern`], and the pattern requires fields
/// that are not returned by the input type.
///
/// The output will contain the string `{X}` instead, where `X` is the symbol for which the input is missing.
#[displaydoc("Incomplete input, missing value for {0:?}")]
MissingInputField(&'static str),
/// Unsupported field
///
/// This *only* happens if the formatter has been created using
/// [`TypedDateTimeNames::with_pattern`], and the pattern contains unsupported fields.
///
/// The output will contain the string `{unsupported:X}`, where `X` is the symbol of the unsupported field.
#[displaydoc("Unsupported field {0:?}")]
UnsupportedField(Field),
}
Expand Down Expand Up @@ -185,16 +211,14 @@ where
let era_symbol = datetime_names
.get_name_for_era(l, era)
.map_err(|e| match e {
GetSymbolForEraError::Missing => {
DateTimeWriteError::MissingEraSymbol(era)
}
GetSymbolForEraError::Missing => DateTimeWriteError::InvalidEra(era),
GetSymbolForEraError::MissingNames(f) => {
DateTimeWriteError::MissingNames(f)
DateTimeWriteError::NamesNotLoaded(f)
}
});
match era_symbol {
Err(e) => {
w.with_part(Part::ERROR, |w| w.write_str(&era.fallback_era()))?;
w.with_part(Part::ERROR, |w| w.write_str(&era.fallback_name()))?;
Err(e)
}
Ok(era) => Ok(w.write_str(era)?),
Expand Down Expand Up @@ -246,7 +270,7 @@ where
let value: usize = cyclic.get() as usize;
cyclics
.get(value - 1)
.ok_or(DateTimeWriteError::CyclicYearOverflow {
.ok_or(DateTimeWriteError::InvalidCyclicYear {
value,
max: cyclics.len() + 1,
})
Expand Down Expand Up @@ -296,9 +320,9 @@ where
.get_name_for_month(month, l, month_info.formatting_code)
.map_err(|e| match e {
GetNameForMonthError::Missing => {
DateTimeWriteError::MissingMonthSymbol(month_info.formatting_code)
DateTimeWriteError::InvalidMonthCode(month_info.formatting_code)
}
GetNameForMonthError::MissingNames(f) => DateTimeWriteError::MissingNames(f),
GetNameForMonthError::MissingNames(f) => DateTimeWriteError::NamesNotLoaded(f),
}) {
Err(e) => {
w.with_part(Part::ERROR, |w| w.write_str(&month_info.formatting_code.0))?;
Expand Down Expand Up @@ -327,8 +351,9 @@ where
Some(wd) => match datetime_names
.get_name_for_weekday(weekday, l, wd)
.map_err(|e| match e {
GetNameForWeekdayError::Missing => DateTimeWriteError::MissingWeekdaySymbol(wd),
GetNameForWeekdayError::MissingNames(f) => DateTimeWriteError::MissingNames(f),
GetNameForWeekdayError::MissingNames(f) => {
DateTimeWriteError::NamesNotLoaded(f)
}
}) {
Err(e) => {
w.with_part(Part::ERROR, |w| {
Expand Down Expand Up @@ -447,7 +472,7 @@ where
)
.map_err(|e| match e {
GetNameForDayPeriodError::MissingNames(f) => {
DateTimeWriteError::MissingNames(f)
DateTimeWriteError::NamesNotLoaded(f)
}
}) {
Err(e) => {
Expand Down Expand Up @@ -494,8 +519,8 @@ where
Err(DateTimeWriteError::MissingInputField(f))
}
Err(
e @ (FormatTimeZoneError::MissingFixedDecimalFormatter
| FormatTimeZoneError::MissingZoneSymbols),
e @ (FormatTimeZoneError::FixedDecimalFormatterNotLoaded
| FormatTimeZoneError::NamesNotLoaded),
) => {
if let Some(offset) = input.offset {
w.with_part(Part::ERROR, |w| {
Expand All @@ -510,11 +535,11 @@ where
write_value_missing(w, field)?;
}
Err(match e {
FormatTimeZoneError::MissingFixedDecimalFormatter => {
DateTimeWriteError::MissingFixedDecimalFormatter
FormatTimeZoneError::FixedDecimalFormatterNotLoaded => {
DateTimeWriteError::FixedDecimalFormatterNotLoaded
}
FormatTimeZoneError::MissingZoneSymbols => {
DateTimeWriteError::MissingNames(field)
FormatTimeZoneError::NamesNotLoaded => {
DateTimeWriteError::NamesNotLoaded(field)
}
_ => unreachable!(),
})
Expand Down Expand Up @@ -556,36 +581,7 @@ where
#[cfg(feature = "compiled_data")]
mod tests {
use super::*;
use crate::{fieldset::YMD, neo_skeleton::NeoSkeletonLength, provider::pattern::runtime};
use icu_calendar::types::FormattingEra;
use icu_decimal::options::{FixedDecimalFormatterOptions, GroupingStrategy};
use tinystr::tinystr;

#[test]
fn test_mixed_calendar_eras() {
use crate::neo::DateTimeFormatter;
use crate::options::length;
use icu_calendar::cal::JapaneseExtended;
use icu_calendar::Date;

let locale = icu::locale::locale!("en-u-ca-japanese");
let dtf = DateTimeFormatter::try_new(&locale.into(), YMD::medium())
.expect("DateTimeFormat construction succeeds");

let date = Date::try_new_gregorian(1800, 9, 1).expect("Failed to construct Date.");
let date = date
.to_calendar(JapaneseExtended::new())
.into_japanese_date()
.to_any();

writeable::assert_try_writeable_eq!(
dtf.strict_format(&date).unwrap(),
"Sep 1, 12 kansei-1789",
Err(DateTimeWriteError::MissingEraSymbol(FormattingEra::Code(
tinystr!(16, "kansei-1789").into()
)))
);
}

#[test]
fn test_format_number() {
Expand Down
1 change: 0 additions & 1 deletion components/datetime/src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub(crate) enum GetNameForMonthError {
MissingNames(Field),
}
pub(crate) enum GetNameForWeekdayError {
Missing,
MissingNames(Field),
}

Expand Down
Loading

0 comments on commit 7067c87

Please sign in to comment.