diff --git a/components/datetime/src/external_loaders.rs b/components/datetime/src/external_loaders.rs index 4a536e59135..2e6cbd541a0 100644 --- a/components/datetime/src/external_loaders.rs +++ b/components/datetime/src/external_loaders.rs @@ -4,11 +4,12 @@ //! Internal traits and structs for loading data from other crates. +use icu_calendar::preferences::CalendarPreferences; use icu_decimal::options::DecimalFormatterOptions; use icu_decimal::{DecimalFormatter, DecimalFormatterPreferences}; use icu_provider::prelude::*; -use crate::scaffold::{FormattableAnyCalendar, FormattableAnyCalendarKind}; +use crate::scaffold::FormattableAnyCalendar; /// Trait for loading a DecimalFormatter. /// @@ -25,7 +26,7 @@ pub(crate) trait DecimalFormatterLoader { /// /// Implemented on the provider-specific loader types in this module. pub(crate) trait FormattableAnyCalendarLoader { - fn load(&self, kind: FormattableAnyCalendarKind) -> Result; + fn load(&self, prefs: CalendarPreferences) -> Result; } /// Loader for types from other crates using compiled data. @@ -47,8 +48,8 @@ impl DecimalFormatterLoader for ExternalLoaderCompiledData { #[cfg(feature = "compiled_data")] impl FormattableAnyCalendarLoader for ExternalLoaderCompiledData { #[inline] - fn load(&self, kind: FormattableAnyCalendarKind) -> Result { - FormattableAnyCalendar::try_new(kind) + fn load(&self, prefs: CalendarPreferences) -> Result { + FormattableAnyCalendar::try_new(prefs) } } @@ -77,8 +78,8 @@ where P: ?Sized + BufferProvider, { #[inline] - fn load(&self, kind: FormattableAnyCalendarKind) -> Result { - FormattableAnyCalendar::try_new_with_buffer_provider(self.0, kind) + fn load(&self, prefs: CalendarPreferences) -> Result { + FormattableAnyCalendar::try_new_with_buffer_provider(self.0, prefs) } } @@ -106,7 +107,7 @@ where P: DataProvider + ?Sized, { #[inline] - fn load(&self, kind: FormattableAnyCalendarKind) -> Result { - FormattableAnyCalendar::try_new_unstable(self.0, kind) + fn load(&self, prefs: CalendarPreferences) -> Result { + FormattableAnyCalendar::try_new_unstable(self.0, prefs) } } diff --git a/components/datetime/src/neo.rs b/components/datetime/src/neo.rs index c05e8bfe04c..b5405b9bcca 100644 --- a/components/datetime/src/neo.rs +++ b/components/datetime/src/neo.rs @@ -425,7 +425,7 @@ size_test!( pub struct DateTimeFormatter { pub(crate) selection: DateTimeZonePatternSelectionData, pub(crate) names: RawDateTimeNames, - pub(crate) calendar: UntaggedFormattableAnyCalendar, + pub(crate) calendar: FormattableAnyCalendar, } impl DateTimeFormatter @@ -503,8 +503,7 @@ where P: ?Sized + AllAnyCalendarFormattingDataMarkers, L: DecimalFormatterLoader + FormattableAnyCalendarLoader, { - let kind = FormattableAnyCalendarKind::from_preferences(prefs); - let calendar = FormattableAnyCalendarLoader::load(loader, kind)?; + let calendar = FormattableAnyCalendarLoader::load(loader, (&prefs).into())?; let names = RawDateTimeNames::new_without_number_formatting(); Self::try_new_internal_with_calendar_and_names( provider, @@ -549,8 +548,7 @@ where { let selection = DateTimeZonePatternSelectionData::try_new_with_skeleton( &FormattableAnyCalendarNamesLoader::<::Skel, _>::new( - provider_p, - calendar.kind(), + provider_p, &calendar, ), &::TimeSkeletonPatternsV1::bind(provider_p), &FSet::GluePatternV1::bind(provider_p), @@ -568,12 +566,10 @@ where }; let result = names.load_for_pattern( &FormattableAnyCalendarNamesLoader::<::Year, _>::new( - provider, - calendar.kind(), + provider, &calendar, ), &FormattableAnyCalendarNamesLoader::<::Month, _>::new( - provider, - calendar.kind(), + provider, &calendar, ), &::WeekdayNamesV1::bind(provider), &::DayPeriodNamesV1::bind(provider), @@ -605,7 +601,7 @@ where Ok(Self { selection, names, - calendar: calendar.into_untagged(), + calendar, }) } } @@ -781,7 +777,7 @@ impl FixedCalendarDateTimeFormatter DateTimeNames { prefs: DateTimeFormatterPreferences, calendar: AnyCalendar, ) -> Result { - let kind = calendar.kind(); let calendar = FormattableAnyCalendar::try_from_any_calendar(calendar) - .ok_or(UnsupportedCalendarError { kind })?; + .map_err(|c| UnsupportedCalendarError { kind: c.kind() })?; Ok(Self { inner: FixedCalendarDateTimeNames::new_without_number_formatting(prefs), calendar, @@ -1137,10 +1136,7 @@ impl DateTimeNames { formatter: DateTimeFormatter, ) -> Self { let metadata = DateTimeNamesMetadata::new_from_previous(&formatter.names); - Self::from_parts( - prefs, - (formatter.calendar.into_tagged(), formatter.names, metadata), - ) + Self::from_parts(prefs, (formatter.calendar, formatter.names, metadata)) } fn from_parts( diff --git a/components/datetime/src/scaffold/calendar.rs b/components/datetime/src/scaffold/calendar.rs index c6f77f77856..b1c00a8e55f 100644 --- a/components/datetime/src/scaffold/calendar.rs +++ b/components/datetime/src/scaffold/calendar.rs @@ -6,9 +6,10 @@ use crate::provider::{neo::*, *}; use crate::scaffold::UnstableSealed; -use crate::{DateTimeFormatterPreferences, MismatchedCalendarError}; +use crate::MismatchedCalendarError; use core::marker::PhantomData; use icu_calendar::cal::{self, *}; +use icu_calendar::preferences::{CalendarAlgorithm, CalendarPreferences, HijriCalendarAlgorithm}; use icu_calendar::{AnyCalendar, AnyCalendarKind, AsCalendar, Date, IntoAnyCalendar, Ref}; use icu_provider::marker::NeverMarker; use icu_provider::prelude::*; @@ -347,235 +348,159 @@ impl FormattableAnyCalendarKind { }; Some(res) } - - pub(crate) fn from_preferences(mut prefs: DateTimeFormatterPreferences) -> Self { - if let Some(res) = Self::try_from_any_calendar_kind(AnyCalendarKind::new((&prefs).into())) { - return res; - } - - // Calendar not supported by DateTimeFormatter - // Currently this is CalendarAlgorithm::Iso8601, CalendarAlgorithm::Hijri(Rgsa) - // Let AnyCalendarKind constructor select an appropriate fallback - prefs.calendar_algorithm = None; - if let Some(res) = Self::try_from_any_calendar_kind(AnyCalendarKind::new((&prefs).into())) { - return res; - } - - debug_assert!(false, "all locale-default calendars are supported"); - // fall back to something non-Gregorian to make errors more obvious - FormattableAnyCalendarKind::Coptic - } } #[test] fn test_calendar_fallback() { - use icu_locale_core::locale; + use icu_locale_core::{locale, Locale}; assert_eq!( - FormattableAnyCalendarKind::from_preferences(locale!("en-TH-u-ca-iso8601").into()), - FormattableAnyCalendarKind::Buddhist + FormattableAnyCalendar::try_new(locale!("en-TH-u-ca-iso8601").into()), + FormattableAnyCalendar::try_new(locale!("und-u-ca-buddhist").into()), ); assert_eq!( - FormattableAnyCalendarKind::from_preferences(locale!("en-TH").into()), - FormattableAnyCalendarKind::Buddhist + FormattableAnyCalendar::try_new(locale!("en-TH").into()), + FormattableAnyCalendar::try_new(locale!("und-u-ca-buddhist").into()), ); assert_eq!( - FormattableAnyCalendarKind::from_preferences(locale!("en-SA-u-ca-islamic").into()), - FormattableAnyCalendarKind::HijriUmmAlQura + FormattableAnyCalendar::try_new(locale!("en-SA-u-ca-islamic").into()), + FormattableAnyCalendar::try_new( + Locale::try_from_str("und-u-ca-islamic-umalqura") + .unwrap() + .into() + ), ); assert_eq!( - FormattableAnyCalendarKind::from_preferences(locale!("en-IL-u-ca-islamic").into()), - FormattableAnyCalendarKind::HijriTabularTypeIIFriday + FormattableAnyCalendar::try_new(locale!("en-IL-u-ca-islamic").into()), + FormattableAnyCalendar::try_new( + Locale::try_from_str("und-u-ca-islamic-civil") + .unwrap() + .into() + ), ); } /// A version of [`AnyCalendar`] for the calendars supported in the any-calendar formatter. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct FormattableAnyCalendar { any_calendar: AnyCalendar, - kind: FormattableAnyCalendarKind, } impl FormattableAnyCalendar { pub(crate) fn from_calendar(calendar: impl IntoFormattableAnyCalendar) -> Self { - let any_calendar = calendar.to_any(); - let kind = any_calendar.kind(); - let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(any_calendar.kind()) - .unwrap_or_else(|| { - debug_assert!(false, "{kind:?} is not a FormattableAnyCalendarKind"); - FormattableAnyCalendarKind::Coptic - }); - Self { any_calendar, kind } + Self { + any_calendar: calendar.to_any(), + } } - pub(crate) fn try_from_any_calendar(any_calendar: AnyCalendar) -> Option { - let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(any_calendar.kind())?; - Some(Self { any_calendar, kind }) + pub(crate) fn try_from_any_calendar(any_calendar: AnyCalendar) -> Result { + match FormattableAnyCalendarKind::try_from_any_calendar_kind(any_calendar.kind()) { + Some(_) => Ok(Self { any_calendar }), + None => Err(any_calendar), + } + } + + pub(crate) fn any_calendar(&self) -> &AnyCalendar { + &self.any_calendar } pub(crate) fn kind(&self) -> FormattableAnyCalendarKind { - self.kind + FormattableAnyCalendarKind::try_from_any_calendar_kind(self.any_calendar.kind()) + .unwrap_or_else(|| { + debug_assert!(false, "unreachable by invariant"); + // fall back to something non-Gregorian to make errors more obvious + FormattableAnyCalendarKind::Coptic + }) + } + + pub(crate) fn take_any_calendar(self) -> AnyCalendar { + self.any_calendar } #[cfg(feature = "compiled_data")] - pub(crate) fn try_new(kind: FormattableAnyCalendarKind) -> Result { - use FormattableAnyCalendarKind::*; - let any_calendar = match kind { - Buddhist => AnyCalendar::Buddhist(cal::Buddhist), - Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()), - Coptic => AnyCalendar::Coptic(cal::Coptic), - Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()), - Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()), - EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style( - cal::EthiopianEraStyle::AmeteAlem, - )), - Gregorian => AnyCalendar::Gregorian(cal::Gregorian), - Hebrew => AnyCalendar::Hebrew(cal::Hebrew), - Indian => AnyCalendar::Indian(cal::Indian), - HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Friday, - )), - HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Thursday, - )), - HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()), - Japanese => AnyCalendar::Japanese(cal::Japanese::new()), - Persian => AnyCalendar::Persian(cal::Persian), - Roc => AnyCalendar::Roc(cal::Roc), - }; - Ok(Self { any_calendar, kind }) + pub(crate) fn try_new(prefs: CalendarPreferences) -> Result { + Self::try_new_unstable(&icu_calendar::provider::Baked, prefs) } #[cfg(feature = "serde")] pub(crate) fn try_new_with_buffer_provider

( provider: &P, - kind: FormattableAnyCalendarKind, + prefs: CalendarPreferences, ) -> Result where P: ?Sized + BufferProvider, { - use FormattableAnyCalendarKind::*; - let any_calendar = match kind { - Buddhist => AnyCalendar::Buddhist(cal::Buddhist), - Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()), - Coptic => AnyCalendar::Coptic(cal::Coptic), - Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()), - Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()), - EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style( - cal::EthiopianEraStyle::AmeteAlem, - )), - Gregorian => AnyCalendar::Gregorian(cal::Gregorian), - Hebrew => AnyCalendar::Hebrew(cal::Hebrew), - Indian => AnyCalendar::Indian(cal::Indian), - HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Friday, - )), - HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Thursday, - )), - HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()), - Japanese => { - AnyCalendar::Japanese(cal::Japanese::try_new_with_buffer_provider(provider)?) - } - Persian => AnyCalendar::Persian(cal::Persian), - Roc => AnyCalendar::Roc(cal::Roc), - }; - Ok(Self { any_calendar, kind }) + Self::try_new_unstable(&provider.as_deserializing(), prefs) } pub(crate) fn try_new_unstable

( provider: &P, - kind: FormattableAnyCalendarKind, + mut prefs: CalendarPreferences, ) -> Result where P: ?Sized + DataProvider, { - use FormattableAnyCalendarKind::*; - let any_calendar = match kind { + use CalendarAlgorithm::*; + let any_calendar = match prefs.resolved_algorithm() { Buddhist => AnyCalendar::Buddhist(cal::Buddhist), Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()), Coptic => AnyCalendar::Coptic(cal::Coptic), Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()), - Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()), - EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style( + Ethiopic => AnyCalendar::Ethiopian(cal::Ethiopian::new()), + Ethioaa => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style( cal::EthiopianEraStyle::AmeteAlem, )), - Gregorian => AnyCalendar::Gregorian(cal::Gregorian), + Gregory => AnyCalendar::Gregorian(cal::Gregorian), Hebrew => AnyCalendar::Hebrew(cal::Hebrew), Indian => AnyCalendar::Indian(cal::Indian), - HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Friday, - )), - HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular( - hijri::TabularAlgorithmLeapYears::TypeII, - hijri::TabularAlgorithmEpoch::Thursday, - )), - HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()), + Hijri(Some(HijriCalendarAlgorithm::Civil)) => { + AnyCalendar::HijriTabular(cal::Hijri::new_tabular( + hijri::TabularAlgorithmLeapYears::TypeII, + hijri::TabularAlgorithmEpoch::Friday, + )) + } + Hijri(Some(HijriCalendarAlgorithm::Tbla)) => { + AnyCalendar::HijriTabular(cal::Hijri::new_tabular( + hijri::TabularAlgorithmLeapYears::TypeII, + hijri::TabularAlgorithmEpoch::Thursday, + )) + } + Hijri(Some(HijriCalendarAlgorithm::Umalqura)) => { + AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()) + } Japanese => AnyCalendar::Japanese(cal::Japanese::try_new_unstable(provider)?), Persian => AnyCalendar::Persian(cal::Persian), Roc => AnyCalendar::Roc(cal::Roc), + Iso8601 | Hijri(_) => { + // unsupported + prefs.calendar_algorithm = None; + return Self::try_new_unstable(provider, prefs); + } + _ => { + // unknown + AnyCalendar::Gregorian(Gregorian) + } }; - Ok(Self { any_calendar, kind }) - } - - pub(crate) fn into_untagged(self) -> UntaggedFormattableAnyCalendar { - UntaggedFormattableAnyCalendar { - any_calendar: self.any_calendar, - } - } -} - -#[derive(Debug, Clone)] -pub(crate) struct UntaggedFormattableAnyCalendar { - // Invariant: the kind must be representable as an FormattableAnyCalendarKind - any_calendar: AnyCalendar, -} - -/// A version of [`FormattableAnyCalendar`] that is smaller on the stack. -impl UntaggedFormattableAnyCalendar { - pub(crate) fn into_tagged(self) -> FormattableAnyCalendar { - let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(self.any_calendar.kind()) - .unwrap_or_else(|| { - debug_assert!(false, "unreachable by invariant"); - // fall back to something non-Gregorian to make errors more obvious - FormattableAnyCalendarKind::Coptic - }); - FormattableAnyCalendar { - any_calendar: self.any_calendar, - kind, - } - } - - pub(crate) fn any_calendar(&self) -> &AnyCalendar { - &self.any_calendar - } - - pub(crate) fn take_any_calendar(self) -> AnyCalendar { - self.any_calendar + Ok(Self { any_calendar }) } } -pub(crate) struct FormattableAnyCalendarNamesLoader { +pub(crate) struct FormattableAnyCalendarNamesLoader<'a, H, P> { provider: P, - kind: FormattableAnyCalendarKind, + calendar: &'a FormattableAnyCalendar, _helper: PhantomData, } -impl FormattableAnyCalendarNamesLoader { - pub(crate) fn new(provider: P, kind: FormattableAnyCalendarKind) -> Self { +impl<'a, H, P> FormattableAnyCalendarNamesLoader<'a, H, P> { + pub(crate) fn new(provider: P, calendar: &'a FormattableAnyCalendar) -> Self { Self { provider, - kind, + calendar, _helper: PhantomData, } } } -impl BoundDataProvider for FormattableAnyCalendarNamesLoader +impl BoundDataProvider for FormattableAnyCalendarNamesLoader<'_, H, P> where M: DynamicDataMarker, H: CalMarkers, @@ -596,7 +521,7 @@ where fn load_bound(&self, req: DataRequest) -> Result, DataError> { use FormattableAnyCalendarKind::*; let p = &self.provider; - match self.kind { + match self.calendar.kind() { Buddhist => H::Buddhist::bind(p).load_bound(req), Chinese => H::Chinese::bind(p).load_bound(req), Coptic => H::Coptic::bind(p).load_bound(req), @@ -615,7 +540,7 @@ where } fn bound_marker(&self) -> DataMarkerInfo { use FormattableAnyCalendarKind::*; - match self.kind { + match self.calendar.kind() { Buddhist => H::Buddhist::INFO, Chinese => H::Chinese::INFO, Coptic => H::Coptic::INFO, diff --git a/components/datetime/src/scaffold/mod.rs b/components/datetime/src/scaffold/mod.rs index d3045706ed7..b75a5378be4 100644 --- a/components/datetime/src/scaffold/mod.rs +++ b/components/datetime/src/scaffold/mod.rs @@ -17,7 +17,6 @@ pub use calendar::CalMarkers; pub use calendar::CldrCalendar; pub use calendar::ConvertCalendar; pub(crate) use calendar::FormattableAnyCalendar; -pub(crate) use calendar::FormattableAnyCalendarKind; pub(crate) use calendar::FormattableAnyCalendarNamesLoader; pub use calendar::FormattableHijriRules; pub use calendar::FullDataCalMarkers; @@ -25,7 +24,6 @@ pub use calendar::InFixedCalendar; pub use calendar::InSameCalendar; pub use calendar::IntoFormattableAnyCalendar; pub use calendar::NoDataCalMarkers; -pub(crate) use calendar::UntaggedFormattableAnyCalendar; pub(crate) use fieldset_traits::datetime_marker_helper; pub use fieldset_traits::AllAnyCalendarExternalDataMarkers;