From 84986f7321b49ac16c0ffc65c3ce71f9c4499f78 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 9 May 2024 13:58:53 -0700 Subject: [PATCH] Create BoundProvider, NeoFormatter, and TypedNeoFormatter (#4877) Part of #1317 This PR adds `TypedNeoFormatter` and `NeoFormatter` that use a type parameter to handle all datetime formatting. It also adds `BoundProvider` to the icu_provider crate. --- CHANGELOG.md | 1 + components/datetime/src/calendar.rs | 358 +++--- components/datetime/src/format/neo.rs | 404 +++++-- components/datetime/src/lib.rs | 1 + components/datetime/src/neo.rs | 1395 +++++++++++++++++------ components/datetime/src/neo_skeleton.rs | 123 +- components/datetime/src/provider/mod.rs | 24 + components/datetime/src/provider/neo.rs | 24 +- components/datetime/src/raw/neo.rs | 56 +- provider/core/src/data_provider.rs | 127 +++ provider/core/src/lib.rs | 4 + provider/core/src/marker.rs | 11 +- 12 files changed, 1829 insertions(+), 699 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d017a90a15b..c8f5fb866f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - `icu_provider` - (Small breakage) `DataPayload::new_owned()` is no longer `const`, this was a mistake (https://github.com/unicode-org/icu4x/pull/4456) - Add `NeverMarker` to allow for DataProvider bounds that never return data (https://github.com/unicode-org/icu4x/issues/4186) + - Add `BoundProvider` to allow temporal separation between key and request (https://github.com/unicode-org/icu4x/pull/4877) - `icu_provider_blob` - Blob v2 no longer allocates (https://github.com/unicode-org/icu4x/pull/4383) - FFI: diff --git a/components/datetime/src/calendar.rs b/components/datetime/src/calendar.rs index e296b3f48a8..b056d148cde 100644 --- a/components/datetime/src/calendar.rs +++ b/components/datetime/src/calendar.rs @@ -19,6 +19,8 @@ use tinystr::{tinystr, TinyAsciiStr}; #[cfg(any(feature = "datagen", feature = "experimental"))] use crate::provider::neo::*; +#[cfg(feature = "experimental")] +use core::marker::PhantomData; #[cfg(any(feature = "datagen", feature = "experimental"))] use icu_provider::NeverMarker; @@ -71,7 +73,7 @@ pub trait CldrCalendar: InternalCldrCalendar { #[cfg(any(feature = "datagen", feature = "experimental"))] /// The data marker for loading skeleton patterns for this calendar. - type DateSkeletonPatternsV1Marker: KeyedDataMarker>; + type SkeletaV1Marker: KeyedDataMarker>; /// Checks if a given BCP 47 identifier is allowed to be used with this calendar /// @@ -81,86 +83,6 @@ pub trait CldrCalendar: InternalCldrCalendar { } } -#[cfg(feature = "experimental")] -pub(crate) trait YearNamesV1Provider { - fn load(&self, req: DataRequest) -> Result, DataError>; -} - -#[cfg(feature = "experimental")] -impl YearNamesV1Provider for P -where - M: KeyedDataMarker>, - P: DataProvider + ?Sized, -{ - fn load(&self, req: DataRequest) -> Result, DataError> { - DataProvider::::load(self, req) - } -} - -#[cfg(feature = "experimental")] -pub(crate) trait MonthNamesV1Provider { - fn load(&self, req: DataRequest) -> Result, DataError>; -} - -#[cfg(feature = "experimental")] -impl MonthNamesV1Provider for P -where - M: KeyedDataMarker>, - P: DataProvider + ?Sized, -{ - fn load(&self, req: DataRequest) -> Result, DataError> { - DataProvider::::load(self, req) - } -} - -#[cfg(feature = "experimental")] -pub(crate) trait WeekdayNamesV1Provider { - fn load(&self, req: DataRequest) -> Result, DataError>; -} - -#[cfg(feature = "experimental")] -impl WeekdayNamesV1Provider for P -where - M: KeyedDataMarker>, - P: DataProvider + ?Sized, -{ - fn load(&self, req: DataRequest) -> Result, DataError> { - DataProvider::::load(self, req) - } -} - -#[cfg(feature = "experimental")] -pub(crate) trait DayPeriodNamesV1Provider { - fn load(&self, req: DataRequest) -> Result, DataError>; -} - -#[cfg(feature = "experimental")] -impl DayPeriodNamesV1Provider for P -where - M: KeyedDataMarker>, - P: DataProvider + ?Sized, -{ - fn load(&self, req: DataRequest) -> Result, DataError> { - DataProvider::::load(self, req) - } -} - -#[cfg(feature = "experimental")] -pub(crate) trait DatePatternV1Provider { - fn load(&self, req: DataRequest) -> Result, DataError>; -} - -#[cfg(feature = "experimental")] -impl DatePatternV1Provider for P -where - M: KeyedDataMarker>, - P: DataProvider + ?Sized, -{ - fn load(&self, req: DataRequest) -> Result, DataError> { - DataProvider::::load(self, req) - } -} - /// Check if the provided value is of the form `islamic-{subcal}` fn is_islamic_subcal(value: &Value, subcal: TinyAsciiStr<8>) -> bool { if let &[first, second] = value.as_tinystr_slice() { @@ -187,7 +109,7 @@ impl CldrCalendar for NeverCalendar { type YearNamesV1Marker = NeverMarker>; type MonthNamesV1Marker = NeverMarker>; type DatePatternV1Marker = NeverMarker>; - type DateSkeletonPatternsV1Marker = NeverMarker>; + type SkeletaV1Marker = NeverMarker>; } impl CldrCalendar for Buddhist { @@ -201,7 +123,7 @@ impl CldrCalendar for Buddhist { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = BuddhistDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = BuddhistDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = BuddhistDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Chinese { @@ -215,7 +137,7 @@ impl CldrCalendar for Chinese { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = ChineseDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = ChineseDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = ChineseDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Coptic { @@ -229,7 +151,7 @@ impl CldrCalendar for Coptic { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = CopticDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = CopticDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = CopticDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Dangi { @@ -243,7 +165,7 @@ impl CldrCalendar for Dangi { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = DangiDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = DangiDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = DangiDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Ethiopian { @@ -257,7 +179,7 @@ impl CldrCalendar for Ethiopian { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = EthiopianDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = EthiopianDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = EthiopianDateNeoSkeletonPatternsV1Marker; fn is_identifier_allowed_for_calendar(value: &Value) -> bool { *value == value!("ethiopic") || *value == value!("ethioaa") } @@ -274,7 +196,7 @@ impl CldrCalendar for Gregorian { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = GregorianDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = GregorianDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = GregorianDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Hebrew { @@ -288,7 +210,7 @@ impl CldrCalendar for Hebrew { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = HebrewDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = HebrewDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = HebrewDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Indian { @@ -302,7 +224,7 @@ impl CldrCalendar for Indian { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = IndianDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = IndianDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = IndianDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for IslamicCivil { @@ -319,7 +241,7 @@ impl CldrCalendar for IslamicCivil { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = IslamicDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; fn is_identifier_allowed_for_calendar(value: &Value) -> bool { *value == value!("islamicc") || is_islamic_subcal(value, tinystr!(8, "civil")) } @@ -336,7 +258,7 @@ impl CldrCalendar for IslamicObservational { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = IslamicDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for IslamicTabular { @@ -353,7 +275,7 @@ impl CldrCalendar for IslamicTabular { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = IslamicDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; fn is_identifier_allowed_for_calendar(value: &Value) -> bool { is_islamic_subcal(value, tinystr!(8, "tbla")) } @@ -373,7 +295,7 @@ impl CldrCalendar for IslamicUmmAlQura { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = IslamicDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = IslamicDateNeoSkeletonPatternsV1Marker; fn is_identifier_allowed_for_calendar(value: &Value) -> bool { is_islamic_subcal(value, tinystr!(8, "umalqura")) } @@ -390,7 +312,7 @@ impl CldrCalendar for Japanese { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = JapaneseDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = JapaneseDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = JapaneseDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for JapaneseExtended { @@ -404,7 +326,7 @@ impl CldrCalendar for JapaneseExtended { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = JapaneseExtendedDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = JapaneseExtendedDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = JapaneseExtendedDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Persian { @@ -418,7 +340,7 @@ impl CldrCalendar for Persian { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = PersianDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = PersianDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = PersianDateNeoSkeletonPatternsV1Marker; } impl CldrCalendar for Roc { @@ -432,7 +354,7 @@ impl CldrCalendar for Roc { #[cfg(any(feature = "datagen", feature = "experimental"))] type DatePatternV1Marker = RocDatePatternV1Marker; #[cfg(any(feature = "datagen", feature = "experimental"))] - type DateSkeletonPatternsV1Marker = RocDateNeoSkeletonPatternsV1Marker; + type SkeletaV1Marker = RocDateNeoSkeletonPatternsV1Marker; } #[cfg(any(feature = "datagen", feature = "experimental"))] @@ -730,56 +652,220 @@ where Ok(payload) } +#[cfg(any(feature = "datagen", feature = "experimental"))] +mod private { + pub trait Sealed {} +} + +/// A collection of marker types associated with all calendars. +/// +/// This is used to group together the calendar-specific marker types that produce a common +/// [`DataMarker`]. For example, this trait can be implemented for [`YearNamesV1Marker`]. +/// +/// This trait serves as a building block for a cross-calendar [`BoundDataProvider`]. +#[cfg(any(feature = "datagen", feature = "experimental"))] +pub trait CalMarkers: private::Sealed +where + M: DataMarker, +{ + /// The type for a [`Buddhist`] calendar + type Buddhist: KeyedDataMarker; + /// The type for a [`Chinese`] calendar + type Chinese: KeyedDataMarker; + /// The type for a [`Coptic`] calendar + type Coptic: KeyedDataMarker; + /// The type for a [`Dangi`] calendar + type Dangi: KeyedDataMarker; + /// The type for an [`Ethiopian`] calendar, with Amete Mihret era + type Ethiopian: KeyedDataMarker; + /// The type for an [`Ethiopian`] calendar, with Amete Alem era + type EthiopianAmeteAlem: KeyedDataMarker; + /// The type for a [`Gregorian`] calendar + type Gregorian: KeyedDataMarker; + /// The type for a [`Hebrew`] calendar + type Hebrew: KeyedDataMarker; + /// The type for a [`Indian`] calendar + type Indian: KeyedDataMarker; + /// The type for an [`IslamicCivil`] calendar + type IslamicCivil: KeyedDataMarker; + /// The type for an [`IslamicObservational`] calendar + type IslamicObservational: KeyedDataMarker; + /// The type for an [`IslamicTabular`] calendar + type IslamicTabular: KeyedDataMarker; + /// The type for an [`IslamicUmmAlQura`] calendar + type IslamicUmmAlQura: KeyedDataMarker; + /// The type for a [`Japanese`] calendar + type Japanese: KeyedDataMarker; + /// The type for a [`JapaneseExtended`] calendar + type JapaneseExtended: KeyedDataMarker; + /// The type for a [`Persian`] calendar + type Persian: KeyedDataMarker; + /// The type for a [`Roc`] calendar + type Roc: KeyedDataMarker; +} + +/// Implementation of [`CalMarkers`] that includes data for all calendars. +#[derive(Debug)] +#[cfg(any(feature = "datagen", feature = "experimental"))] +#[allow(clippy::exhaustive_enums)] // empty enum +pub enum FullDataCalMarkers {} + +#[cfg(any(feature = "datagen", feature = "experimental"))] +impl private::Sealed for FullDataCalMarkers {} + +/// Implementation of [`CalMarkers`] that includes data for no calendars. +#[derive(Debug)] +#[cfg(any(feature = "datagen", feature = "experimental"))] +#[allow(clippy::exhaustive_enums)] // empty enum +pub enum NoDataCalMarkers {} + +#[cfg(any(feature = "datagen", feature = "experimental"))] +impl private::Sealed for NoDataCalMarkers {} + +#[cfg(any(feature = "datagen", feature = "experimental"))] +impl CalMarkers for NoDataCalMarkers +where + M: DataMarker, +{ + type Buddhist = NeverMarker; + type Chinese = NeverMarker; + type Coptic = NeverMarker; + type Dangi = NeverMarker; + type Ethiopian = NeverMarker; + type EthiopianAmeteAlem = NeverMarker; + type Gregorian = NeverMarker; + type Hebrew = NeverMarker; + type Indian = NeverMarker; + type IslamicCivil = NeverMarker; + type IslamicObservational = NeverMarker; + type IslamicTabular = NeverMarker; + type IslamicUmmAlQura = NeverMarker; + type Japanese = NeverMarker; + type JapaneseExtended = NeverMarker; + type Persian = NeverMarker; + type Roc = NeverMarker; +} + #[cfg(feature = "experimental")] -pub(crate) struct AnyCalendarProvider<'a, P: ?Sized> { - pub(crate) provider: &'a P, - pub(crate) kind: AnyCalendarKind, +pub(crate) struct AnyCalendarProvider { + provider: P, + kind: AnyCalendarKind, + _helper: PhantomData, } #[cfg(feature = "experimental")] +impl AnyCalendarProvider { + pub(crate) fn new(provider: P, kind: AnyCalendarKind) -> Self { + Self { + provider, + kind, + _helper: PhantomData, + } + } +} + +#[cfg(feature = "experimental")] +impl BoundDataProvider for AnyCalendarProvider +where + M: DataMarker, + H: CalMarkers, + P: Sized + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider, +{ + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + use AnyCalendarKind::*; + let p = &self.provider; + match self.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), + Dangi => H::Dangi::bind(p).load_bound(req), + Ethiopian => H::Ethiopian::bind(p).load_bound(req), + EthiopianAmeteAlem => H::EthiopianAmeteAlem::bind(p).load_bound(req), + Gregorian => H::Gregorian::bind(p).load_bound(req), + Hebrew => H::Hebrew::bind(p).load_bound(req), + Indian => H::Indian::bind(p).load_bound(req), + IslamicCivil => H::IslamicCivil::bind(p).load_bound(req), + IslamicObservational => H::IslamicObservational::bind(p).load_bound(req), + IslamicTabular => H::IslamicTabular::bind(p).load_bound(req), + IslamicUmmAlQura => H::IslamicUmmAlQura::bind(p).load_bound(req), + Japanese => H::Japanese::bind(p).load_bound(req), + JapaneseExtended => H::JapaneseExtended::bind(p).load_bound(req), + Persian => H::Persian::bind(p).load_bound(req), + Roc => H::Roc::bind(p).load_bound(req), + _ => Err( + DataError::custom("Don't know how to load data for specified calendar") + .with_debug_context(&self.kind), + ), + } + } + fn bound_key(&self) -> DataKey { + use AnyCalendarKind::*; + match self.kind { + Buddhist => H::Buddhist::KEY, + Chinese => H::Chinese::KEY, + Coptic => H::Coptic::KEY, + Dangi => H::Dangi::KEY, + Ethiopian => H::Ethiopian::KEY, + EthiopianAmeteAlem => H::EthiopianAmeteAlem::KEY, + Gregorian => H::Gregorian::KEY, + Hebrew => H::Hebrew::KEY, + Indian => H::Indian::KEY, + IslamicCivil => H::IslamicCivil::KEY, + IslamicObservational => H::IslamicObservational::KEY, + IslamicTabular => H::IslamicTabular::KEY, + IslamicUmmAlQura => H::IslamicUmmAlQura::KEY, + Japanese => H::Japanese::KEY, + JapaneseExtended => H::JapaneseExtended::KEY, + Persian => H::Persian::KEY, + Roc => H::Roc::KEY, + _ => NeverMarker::::KEY, + } + } +} + +#[cfg(any(feature = "datagen", feature = "experimental"))] macro_rules! impl_load_any_calendar { - ([$(($trait:ident, $erased:ident, $marker:ident)),+], [$($kind_cal:ident),+], [$($kind:ident => $cal:ident),+]) => { - impl_load_any_calendar!(@expand [$(($trait, $erased, $marker)),+], [$($kind_cal),+], [$($kind => $cal),+]); + ([$(($erased:ident, $marker:ident)),+], [$($kind_cal:ident),+], [$($kind:ident => $cal:ident),+]) => { + impl_load_any_calendar!(@expand [$(($erased, $marker)),+], [$($kind_cal),+], [$($kind => $cal),+]); }; - (@expand [$(($trait:ident, $erased:ident, $marker:ident)),+], $tail1:tt, $tail2:tt) => { - $(impl_load_any_calendar!(@single_impl $trait, $erased, $marker, $tail1, $tail2);)+ + (@expand [$(($erased:ident, $marker:ident)),+], $tail1:tt, $tail2:tt) => { + $(impl_load_any_calendar!(@single_impl $erased, $marker, $tail1, $tail2);)+ }; - (@single_impl $trait:ident, $erased:ident, $marker:ident, [$($kind_cal:ident),+], [$($kind:ident => $cal:ident),+]) => { - impl

$trait<$erased> for AnyCalendarProvider<'_, P> - where - P: ?Sized + $(DataProvider::<<$kind_cal as CldrCalendar>::$marker> +)+ - { - fn load( - &self, - req: DataRequest, - ) -> Result, DataError> { - match self.kind { - $( - AnyCalendarKind::$kind_cal => DataProvider - ::<<$kind_cal as CldrCalendar>::$marker> - ::load(self.provider, req) - .map(DataResponse::cast), - )+ - $( - AnyCalendarKind::$kind => DataProvider - ::<<$cal as CldrCalendar>::$marker> - ::load(self.provider, req) - .map(DataResponse::cast), - )+ - _ => Err( - DataError::custom("Don't know how to load data for specified calendar") - .with_debug_context(&self.kind)), - } - } + (@single_impl $erased:ident, $marker:ident, [$($kind_cal:ident),+], [$($kind:ident => $cal:ident),+]) => { + impl CalMarkers<$erased> for FullDataCalMarkers { + $( + type $kind_cal = <$kind_cal as CldrCalendar>::$marker; + )+ + $( + type $kind = <$cal as CldrCalendar>::$marker; + )+ } }; } -#[cfg(feature = "experimental")] +#[cfg(any(feature = "datagen", feature = "experimental"))] impl_load_any_calendar!([ - (DatePatternV1Provider, ErasedDatePatternV1Marker, DatePatternV1Marker), - (YearNamesV1Provider, ErasedYearNamesV1Marker, YearNamesV1Marker), - (MonthNamesV1Provider, ErasedMonthNamesV1Marker, MonthNamesV1Marker) + (DatePatternV1Marker, DatePatternV1Marker), + (YearNamesV1Marker, YearNamesV1Marker), + (MonthNamesV1Marker, MonthNamesV1Marker), + (SkeletaV1Marker, SkeletaV1Marker) ], [ Buddhist, Chinese, diff --git a/components/datetime/src/format/neo.rs b/components/datetime/src/format/neo.rs index bfac5e2f73b..637bf17b065 100644 --- a/components/datetime/src/format/neo.rs +++ b/components/datetime/src/format/neo.rs @@ -3,10 +3,7 @@ // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use super::datetime::{try_write_pattern, DateTimeWriteError}; -use crate::calendar::{ - CldrCalendar, DayPeriodNamesV1Provider, MonthNamesV1Provider, WeekdayNamesV1Provider, - YearNamesV1Provider, -}; +use crate::calendar::CldrCalendar; use crate::external_loaders::*; use crate::fields::{self, Field, FieldLength, FieldSymbol}; use crate::helpers::size_test; @@ -32,8 +29,9 @@ use icu_decimal::options::FixedDecimalFormatterOptions; use icu_decimal::options::GroupingStrategy; use icu_decimal::provider::DecimalSymbolsV1Marker; use icu_decimal::FixedDecimalFormatter; -use icu_provider::prelude::*; +use icu_provider::{prelude::*, NeverMarker}; use writeable::TryWriteable; +use yoke::Yokeable; /// This can be extended in the future to support multiple lengths. /// For now, this type wraps a symbols object tagged with a single length. See #4337 @@ -91,37 +89,53 @@ where } } -impl OptionalNames> +impl OptionalNames where S: Copy, - M: DataMarker, { - pub(crate) fn as_borrowed( - &self, - ) -> OptionalNames::Output> { + pub(crate) fn as_borrowed<'a, Y>(&'a self) -> OptionalNames>::Output> + where + T: MaybePayload, + Y: for<'y> Yokeable<'y>, + { match self { Self::None => OptionalNames::None, - Self::SingleLength(field_symbol, field_length, payload) => { - OptionalNames::SingleLength(*field_symbol, *field_length, payload.get()) - } + Self::SingleLength(field_symbol, field_length, payload) => match payload.maybe_get() { + Some(data) => OptionalNames::SingleLength(*field_symbol, *field_length, data), + None => OptionalNames::None, + }, } } } /// Helper for type resolution with optional DataProvider arguments -pub(crate) struct PhantomProvider { - _not_constructible: core::convert::Infallible, -} +pub(crate) struct PhantomProvider; impl DataProvider for PhantomProvider { #[inline] - fn load(&self, _req: DataRequest) -> Result, DataError> { - unreachable!() // not constructible + fn load(&self, req: DataRequest) -> Result, DataError> { + debug_assert!(false); + Err(DataErrorKind::MissingDataKey.with_req(M::KEY, req)) + } +} + +impl BoundDataProvider for PhantomProvider { + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + debug_assert!(false); + let key = BoundDataProvider::::bound_key(self); + Err(DataErrorKind::MissingDataKey + .into_error() + .with_req(key, req)) + } + #[inline] + fn bound_key(&self) -> DataKey { + NeverMarker::::KEY } } size_test!( - TypedDateTimeNames, + TypedDateTimeNames, typed_date_time_names_size, 488 ); @@ -200,21 +214,160 @@ size_test!( /// ); /// ``` #[derive(Debug)] -pub struct TypedDateTimeNames { +pub struct TypedDateTimeNames { locale: DataLocale, - inner: RawDateTimeNames, + inner: RawDateTimeNames, _calendar: PhantomData, } +pub trait DateTimeNamesMarker { + type YearNames: MaybePayload> + fmt::Debug; + type MonthNames: MaybePayload> + fmt::Debug; + type WeekdayNames: MaybePayload> + fmt::Debug; + type DayPeriodNames: MaybePayload> + fmt::Debug; +} + +pub trait MaybePayload Yokeable<'a>> { + fn maybe_from_payload(payload: DataPayload) -> Option + where + M: DataMarker, + Self: Sized; + fn load_from(provider: &P, req: DataRequest) -> Option> + where + M: DataMarker, + P: BoundDataProvider + ?Sized, + Self: Sized; + #[allow(clippy::needless_lifetimes)] // Yokeable is involved + fn maybe_get<'a>(&'a self) -> Option<&'a >::Output>; +} + +impl Yokeable<'a>> MaybePayload for DataPayload +where + M0: DataMarker, +{ + #[inline] + fn maybe_from_payload(payload: DataPayload) -> Option + where + M: DataMarker, + { + Some(payload.cast()) + } + #[inline] + fn load_from(provider: &P, req: DataRequest) -> Option> + where + M: DataMarker, + P: BoundDataProvider + ?Sized, + Self: Sized, + { + Some( + provider + .load_bound(req) + .and_then(DataResponse::take_payload) + .map(DataPayload::cast), + ) + } + #[allow(clippy::needless_lifetimes)] // Yokeable is involved + #[inline] + fn maybe_get<'a>(&'a self) -> Option<&'a >::Output> { + Some(self.get()) + } +} + +impl Yokeable<'a>> MaybePayload for () { + #[inline] + fn maybe_from_payload(_payload: DataPayload) -> Option + where + M: DataMarker, + { + None + } + #[inline] + fn load_from(_provider: &P, _req: DataRequest) -> Option> + where + M: DataMarker, + P: BoundDataProvider + ?Sized, + Self: Sized, + { + // TODO: Is it better to return DataError or SingleLoadError? + // SingleLoadError needs to be from the caller because it needs `field`. + None + // Err(DataError::custom("cannot load into this type").with_req(provider.key(), req)) + } + #[allow(clippy::needless_lifetimes)] // Yokeable is involved + #[inline] + fn maybe_get<'a>(&'a self) -> Option<&'a >::Output> { + None + } +} + +#[derive(Debug)] +pub struct DateMarker {} + +impl DateTimeNamesMarker for DateMarker { + type YearNames = DataPayload; + type MonthNames = DataPayload; + type WeekdayNames = DataPayload; + type DayPeriodNames = (); +} + #[derive(Debug)] -pub(crate) struct RawDateTimeNames { - year_symbols: OptionalNames<(), DataPayload>, - month_symbols: OptionalNames>, - weekday_symbols: OptionalNames>, - dayperiod_symbols: OptionalNames<(), DataPayload>, +pub struct TimeMarker {} + +impl DateTimeNamesMarker for TimeMarker { + type YearNames = (); + type MonthNames = (); + type WeekdayNames = (); + type DayPeriodNames = DataPayload; +} + +#[derive(Debug)] +pub struct DateTimeMarker {} + +impl DateTimeNamesMarker for DateTimeMarker { + type YearNames = DataPayload; + type MonthNames = DataPayload; + type WeekdayNames = DataPayload; + type DayPeriodNames = DataPayload; +} + +impl From> for RawDateTimeNames { + fn from(other: RawDateTimeNames) -> Self { + Self { + year_symbols: other.year_symbols, + month_symbols: other.month_symbols, + weekday_symbols: other.weekday_symbols, + dayperiod_symbols: OptionalNames::None, + fixed_decimal_formatter: other.fixed_decimal_formatter, + week_calculator: other.week_calculator, + _marker: PhantomData, + } + } +} + +impl From> for RawDateTimeNames { + fn from(other: RawDateTimeNames) -> Self { + Self { + year_symbols: OptionalNames::None, + month_symbols: OptionalNames::None, + weekday_symbols: OptionalNames::None, + dayperiod_symbols: other.dayperiod_symbols, + fixed_decimal_formatter: other.fixed_decimal_formatter, + week_calculator: other.week_calculator, + _marker: PhantomData, + } + } +} + +#[derive(Debug)] +pub(crate) struct RawDateTimeNames { + year_symbols: OptionalNames<(), R::YearNames>, + month_symbols: OptionalNames, + weekday_symbols: OptionalNames, + dayperiod_symbols: OptionalNames<(), R::DayPeriodNames>, // TODO(#4340): Make the FixedDecimalFormatter optional fixed_decimal_formatter: Option, week_calculator: Option, + _marker: PhantomData, } #[derive(Debug, Copy, Clone)] @@ -227,7 +380,7 @@ pub(crate) struct RawDateTimeNamesBorrowed<'l> { pub(crate) week_calculator: Option<&'l WeekCalculator>, } -impl TypedDateTimeNames { +impl TypedDateTimeNames { /// Constructor that takes a selected locale and creates an empty instance. /// /// For an example, see [`TypedDateTimeNames`]. @@ -271,8 +424,11 @@ impl TypedDateTimeNames { where P: DataProvider + ?Sized, { - self.inner - .load_year_names(provider, &self.locale, field_length)?; + self.inner.load_year_names( + &C::YearNamesV1Marker::bind(provider), + &self.locale, + field_length, + )?; Ok(self) } @@ -328,8 +484,12 @@ impl TypedDateTimeNames { where P: DataProvider + ?Sized, { - self.inner - .load_month_names(provider, &self.locale, field_symbol, field_length)?; + self.inner.load_month_names( + &C::MonthNamesV1Marker::bind(provider), + &self.locale, + field_symbol, + field_length, + )?; Ok(self) } @@ -395,8 +555,9 @@ impl TypedDateTimeNames { where P: DataProvider + ?Sized, { + let provider = DayPeriodNamesV1Marker::bind(provider); self.inner - .load_day_period_names(provider, &self.locale, field_length)?; + .load_day_period_names(&provider, &self.locale, field_length)?; Ok(self) } @@ -452,8 +613,12 @@ impl TypedDateTimeNames { where P: DataProvider + ?Sized, { - self.inner - .load_weekday_names(provider, &self.locale, field_symbol, field_length)?; + self.inner.load_weekday_names( + &WeekdayNamesV1Marker::bind(provider), + &self.locale, + field_symbol, + field_length, + )?; Ok(self) } @@ -597,17 +762,16 @@ impl TypedDateTimeNames { + ?Sized, { let locale = &self.locale; - self.inner - .load_for_pattern::( - Some(provider), - Some(provider), - Some(provider), - Some(provider), - Some(&ExternalLoaderUnstable(provider)), - Some(&ExternalLoaderUnstable(provider)), - locale, - pattern.iter_items(), - )?; + self.inner.load_for_pattern( + &C::YearNamesV1Marker::bind(provider), + &C::MonthNamesV1Marker::bind(provider), + &WeekdayNamesV1Marker::bind(provider), + &DayPeriodNamesV1Marker::bind(provider), + Some(&ExternalLoaderUnstable(provider)), + Some(&ExternalLoaderUnstable(provider)), + locale, + pattern.iter_items(), + )?; Ok(DateTimePatternFormatter { inner: self.inner.with_pattern(pattern.as_borrowed()), _calendar: PhantomData, @@ -660,17 +824,16 @@ impl TypedDateTimeNames { + DataProvider, { let locale = &self.locale; - self.inner - .load_for_pattern::( - Some(&crate::provider::Baked), - Some(&crate::provider::Baked), - Some(&crate::provider::Baked), - Some(&crate::provider::Baked), - Some(&ExternalLoaderCompiledData), - Some(&ExternalLoaderCompiledData), - locale, - pattern.iter_items(), - )?; + self.inner.load_for_pattern( + &C::YearNamesV1Marker::bind(&crate::provider::Baked), + &C::MonthNamesV1Marker::bind(&crate::provider::Baked), + &WeekdayNamesV1Marker::bind(&crate::provider::Baked), + &DayPeriodNamesV1Marker::bind(&crate::provider::Baked), + Some(&ExternalLoaderCompiledData), + Some(&ExternalLoaderCompiledData), + locale, + pattern.iter_items(), + )?; Ok(DateTimePatternFormatter { inner: self.inner.with_pattern(pattern.as_borrowed()), _calendar: PhantomData, @@ -684,9 +847,11 @@ impl TypedDateTimeNames { pub enum SingleLoadError { /// Duplicate field in pattern DuplicateField(Field), - /// UnsupportedField + /// ICU4X does not support this field UnsupportedField(Field), - /// Data + /// The specific type does not support this field + TypeTooNarrow(Field), + /// An error arising from the [`DataProvider`] Data(DataError), } @@ -696,9 +861,11 @@ pub enum SingleLoadError { pub enum LoadError { /// DuplicateField DuplicateField(Field), - /// UnsupportedField + /// ICU4X does not support this field UnsupportedField(Field), - /// Data + /// The specific type does not support this field + TypeTooNarrow(Field), + /// An error arising from the [`DataProvider`] Data(DataError), /// MissingNames MissingNames(Field), @@ -709,12 +876,13 @@ impl From for LoadError { match value { SingleLoadError::Data(e) => LoadError::Data(e), SingleLoadError::UnsupportedField(f) => LoadError::UnsupportedField(f), + SingleLoadError::TypeTooNarrow(f) => LoadError::TypeTooNarrow(f), SingleLoadError::DuplicateField(f) => LoadError::DuplicateField(f), } } } -impl RawDateTimeNames { +impl RawDateTimeNames { pub(crate) fn new_without_fixed_decimal_formatter() -> Self { Self { year_symbols: OptionalNames::None, @@ -723,6 +891,7 @@ impl RawDateTimeNames { dayperiod_symbols: OptionalNames::None, fixed_decimal_formatter: None, week_calculator: None, + _marker: PhantomData, } } @@ -737,15 +906,14 @@ impl RawDateTimeNames { } } - pub(crate) fn load_year_names( + pub(crate) fn load_year_names

( &mut self, provider: &P, locale: &DataLocale, field_length: FieldLength, ) -> Result<(), SingleLoadError> where - P: YearNamesV1Provider + ?Sized, - M: DataMarker>, + P: BoundDataProvider + ?Sized, { let field = fields::Field { symbol: FieldSymbol::Era, @@ -769,18 +937,22 @@ impl RawDateTimeNames { }, ))); let payload = provider - .load(DataRequest { + .load_bound(DataRequest { locale: &locale, metadata: Default::default(), }) .and_then(DataResponse::take_payload) - .map_err(SingleLoadError::Data)? - .cast(); - self.year_symbols = OptionalNames::SingleLength((), field_length, payload); + .map_err(SingleLoadError::Data)?; + self.year_symbols = OptionalNames::SingleLength( + (), + field_length, + R::YearNames::maybe_from_payload(payload) + .ok_or(SingleLoadError::TypeTooNarrow(field))?, + ); Ok(()) } - pub(crate) fn load_month_names( + pub(crate) fn load_month_names

( &mut self, provider: &P, locale: &DataLocale, @@ -788,8 +960,7 @@ impl RawDateTimeNames { field_length: FieldLength, ) -> Result<(), SingleLoadError> where - P: MonthNamesV1Provider + ?Sized, - M: DataMarker>, + P: BoundDataProvider + ?Sized, { let field = fields::Field { symbol: FieldSymbol::Month(field_symbol), @@ -818,26 +989,29 @@ impl RawDateTimeNames { }, ))); let payload = provider - .load(DataRequest { + .load_bound(DataRequest { locale: &locale, metadata: Default::default(), }) .and_then(DataResponse::take_payload) - .map_err(SingleLoadError::Data)? - .cast(); - self.month_symbols = OptionalNames::SingleLength(field_symbol, field_length, payload); + .map_err(SingleLoadError::Data)?; + self.month_symbols = OptionalNames::SingleLength( + field_symbol, + field_length, + R::MonthNames::maybe_from_payload(payload) + .ok_or(SingleLoadError::TypeTooNarrow(field))?, + ); Ok(()) } - pub(crate) fn load_day_period_names( + pub(crate) fn load_day_period_names

( &mut self, provider: &P, locale: &DataLocale, field_length: FieldLength, ) -> Result<(), SingleLoadError> where - P: DayPeriodNamesV1Provider + ?Sized, - M: DataMarker>, + P: BoundDataProvider + ?Sized, { let field = fields::Field { // Names for 'a' and 'b' are stored in the same data key @@ -861,19 +1035,20 @@ impl RawDateTimeNames { _ => return Err(SingleLoadError::UnsupportedField(field)), }, ))); - let payload = provider - .load(DataRequest { + let payload = R::DayPeriodNames::load_from( + provider, + DataRequest { locale: &locale, metadata: Default::default(), - }) - .and_then(DataResponse::take_payload) - .map_err(SingleLoadError::Data)? - .cast(); + }, + ) + .ok_or(SingleLoadError::TypeTooNarrow(field))? + .map_err(SingleLoadError::Data)?; self.dayperiod_symbols = OptionalNames::SingleLength((), field_length, payload); Ok(()) } - pub(crate) fn load_weekday_names( + pub(crate) fn load_weekday_names

( &mut self, provider: &P, locale: &DataLocale, @@ -881,8 +1056,7 @@ impl RawDateTimeNames { field_length: FieldLength, ) -> Result<(), SingleLoadError> where - P: WeekdayNamesV1Provider + ?Sized, - M: DataMarker>, + P: BoundDataProvider + ?Sized, { let field = fields::Field { symbol: FieldSymbol::Weekday(field_symbol), @@ -919,14 +1093,18 @@ impl RawDateTimeNames { }, ))); let payload = provider - .load(DataRequest { + .load_bound(DataRequest { locale: &locale, metadata: Default::default(), }) .and_then(DataResponse::take_payload) - .map_err(SingleLoadError::Data)? - .cast(); - self.weekday_symbols = OptionalNames::SingleLength(field_symbol, field_length, payload); + .map_err(SingleLoadError::Data)?; + self.weekday_symbols = OptionalNames::SingleLength( + field_symbol, + field_length, + R::WeekdayNames::maybe_from_payload(payload) + .ok_or(SingleLoadError::TypeTooNarrow(field))?, + ); Ok(()) } @@ -965,23 +1143,17 @@ impl RawDateTimeNames { /// This function has a lot of arguments because many of the arguments are generic, /// and pulling them out to an options struct would be cumbersome. #[allow(clippy::too_many_arguments)] - pub(crate) fn load_for_pattern( + pub(crate) fn load_for_pattern( &mut self, - year_provider: Option<&(impl YearNamesV1Provider + ?Sized)>, - month_provider: Option<&(impl MonthNamesV1Provider + ?Sized)>, - weekday_provider: Option<&(impl WeekdayNamesV1Provider + ?Sized)>, - dayperiod_provider: Option<&(impl DayPeriodNamesV1Provider + ?Sized)>, + year_provider: &(impl BoundDataProvider + ?Sized), + month_provider: &(impl BoundDataProvider + ?Sized), + weekday_provider: &(impl BoundDataProvider + ?Sized), + dayperiod_provider: &(impl BoundDataProvider + ?Sized), fixed_decimal_formatter_loader: Option<&impl FixedDecimalFormatterLoader>, week_calculator_loader: Option<&impl WeekCalculatorLoader>, locale: &DataLocale, pattern_items: impl Iterator, - ) -> Result<(), LoadError> - where - YearMarker: DataMarker>, - MonthMarker: DataMarker>, - WeekdayMarker: DataMarker>, - DayPeriodMarker: DataMarker>, - { + ) -> Result<(), LoadError> { let fields = pattern_items.filter_map(|p| match p { PatternItem::Field(field) => Some(field), _ => None, @@ -993,22 +1165,13 @@ impl RawDateTimeNames { match field.symbol { ///// Textual symbols ///// FieldSymbol::Era => { - self.load_year_names( - year_provider.ok_or(LoadError::MissingNames(field))?, - locale, - field.length, - )?; + self.load_year_names(year_provider, locale, field.length)?; } FieldSymbol::Month(symbol) => match field.length { FieldLength::One => numeric_field = Some(field), FieldLength::TwoDigit => numeric_field = Some(field), _ => { - self.load_month_names( - month_provider.ok_or(LoadError::MissingNames(field))?, - locale, - symbol, - field.length, - )?; + self.load_month_names(month_provider, locale, symbol, field.length)?; } }, // 'E' is always text @@ -1020,20 +1183,11 @@ impl RawDateTimeNames { numeric_field = Some(field) } _ => { - self.load_weekday_names( - weekday_provider.ok_or(LoadError::MissingNames(field))?, - locale, - symbol, - field.length, - )?; + self.load_weekday_names(weekday_provider, locale, symbol, field.length)?; } }, FieldSymbol::DayPeriod(_) => { - self.load_day_period_names( - dayperiod_provider.ok_or(LoadError::MissingNames(field))?, - locale, - field.length, - )?; + self.load_day_period_names(dayperiod_provider, locale, field.length)?; } ///// Numeric symbols ///// diff --git a/components/datetime/src/lib.rs b/components/datetime/src/lib.rs index f302cad584c..e10c48b415f 100644 --- a/components/datetime/src/lib.rs +++ b/components/datetime/src/lib.rs @@ -134,6 +134,7 @@ mod any; mod calendar; mod datetime; mod error; +#[cfg(feature = "experimental")] mod external_loaders; pub mod fields; mod format; diff --git a/components/datetime/src/neo.rs b/components/datetime/src/neo.rs index 4c670314f37..4c779a5a606 100644 --- a/components/datetime/src/neo.rs +++ b/components/datetime/src/neo.rs @@ -13,7 +13,8 @@ use crate::input::ExtractedDateTimeInput; use crate::input::{DateInput, DateTimeInput, IsoTimeInput}; use crate::neo_pattern::DateTimePattern; use crate::neo_skeleton::{ - NeoSkeletonLength, TypedNeoDateSkeletonComponents, TypedNeoTimeSkeletonComponents, + NeoComponents, NeoDateComponents, NeoDayComponents, NeoSkeletonCommonData, + NeoSkeletonComponents, NeoSkeletonLength, TypedNeoSkeletonData, }; use crate::options::length; use crate::provider::neo::*; @@ -31,6 +32,16 @@ use icu_decimal::provider::DecimalSymbolsV1Marker; use icu_provider::{prelude::*, NeverMarker}; use writeable::TryWriteable; +#[doc(hidden)] // internal +pub mod _internal { + pub use crate::calendar::CalMarkers; + pub use crate::calendar::FullDataCalMarkers; + pub use crate::calendar::NoDataCalMarkers; +} + +use _internal::CalMarkers; +use _internal::FullDataCalMarkers as FullData; + /// Helper macro for generating any/buffer constructors in this file. macro_rules! gen_any_buffer_constructors_with_external_loader { ($compiled_fn:ident, $any_fn:ident, $buffer_fn:ident, $internal_fn:ident, $($arg:ident: $ty:path),+) => { @@ -68,7 +79,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { ) } }; - (S: $skel:path, $compiled_fn:ident, $any_fn:ident, $buffer_fn:ident, $internal_fn:ident, $($arg:ident: $ty:path),+) => { + (S: $skel:path | $compts:path, $compiled_fn:ident, $any_fn:ident, $buffer_fn:ident, $internal_fn:ident, $($arg:ident: $ty:path),+) => { #[doc = icu_provider::gen_any_buffer_unstable_docs!(ANY, Self::$compiled_fn)] pub fn $any_fn( provider: &P, @@ -76,7 +87,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { $($arg: $ty),+ ) -> Result where - S: ?Sized + $skel, + S: ?Sized + $skel + $compts, P: AnyProvider + ?Sized, { Self::$internal_fn::( @@ -94,7 +105,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { $($arg: $ty),+ ) -> Result where - S: ?Sized + $skel, + S: ?Sized + $skel + $compts, P: BufferProvider + ?Sized, { Self::$internal_fn::( @@ -110,7 +121,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { size_test!( TypedNeoDateFormatter, typed_neo_date_formatter_size, - 496 + 456 ); /// [`TypedNeoDateFormatter`] can format dates from a calendar selected at compile time. @@ -129,7 +140,7 @@ size_test!( #[derive(Debug)] pub struct TypedNeoDateFormatter { selection: DatePatternSelectionData, - names: RawDateTimeNames, + names: RawDateTimeNames, _calendar: PhantomData, } @@ -223,22 +234,23 @@ impl TypedNeoDateFormatter { + DataProvider, L: FixedDecimalFormatterLoader + WeekCalculatorLoader, { - let selection = DatePatternSelectionData::try_new_with_length::( - provider, locale, length, + let selection = DatePatternSelectionData::try_new_with_length( + &C::DatePatternV1Marker::bind(provider), + locale, + length, ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names - .load_for_pattern::>>( - Some(provider), // year - Some(provider), // month - Some(provider), // weekday - None::<&PhantomProvider>, // day period - Some(loader), // fixed decimal formatter - Some(loader), // week calculator - locale, - selection.pattern_items_for_data_loading(), - )?; + names.load_for_pattern( + &C::YearNamesV1Marker::bind(provider), // year + &C::MonthNamesV1Marker::bind(provider), // month + &WeekdayNamesV1Marker::bind(provider), // weekday + &PhantomProvider, // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator + locale, + selection.pattern_items_for_data_loading(), + )?; Ok(Self { selection, names, @@ -275,7 +287,7 @@ impl TypedNeoDateFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoDateSkeletonComponents, + S: ?Sized + TypedNeoSkeletonData + NeoSkeletonComponents, crate::provider::Baked: Sized // Date formatting keys + DataProvider @@ -292,7 +304,7 @@ impl TypedNeoDateFormatter { } gen_any_buffer_constructors_with_external_loader!( - S: TypedNeoDateSkeletonComponents, + S: TypedNeoSkeletonData | NeoSkeletonComponents, try_new_with_skeleton, try_new_with_skeleton_with_any_provider, try_new_with_skeleton_with_buffer_provider, @@ -307,7 +319,7 @@ impl TypedNeoDateFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoDateSkeletonComponents, + S: ?Sized + TypedNeoSkeletonData + NeoSkeletonComponents, P: ?Sized // Date formatting keys + DataProvider @@ -334,7 +346,7 @@ impl TypedNeoDateFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoDateSkeletonComponents, + S: ?Sized + TypedNeoSkeletonData + NeoSkeletonComponents, P: ?Sized // Date formatting keys + DataProvider @@ -343,18 +355,280 @@ impl TypedNeoDateFormatter { + DataProvider, L: FixedDecimalFormatterLoader + WeekCalculatorLoader, { - let selection = DatePatternSelectionData::try_new_with_skeleton::< - S::DateSkeletonPatternsV1Marker, - >(provider, locale, length, S::COMPONENTS) + let selection = DatePatternSelectionData::try_new_with_skeleton( + &S::DateSkeletonPatternsV1Marker::bind(provider), + locale, + length, + S::COMPONENTS, + ) + .map_err(LoadError::Data)?; + let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); + names.load_for_pattern( + &S::YearNamesV1Marker::bind(provider), // year + &S::MonthNamesV1Marker::bind(provider), // month + &S::WeekdayNamesV1Marker::bind(provider), // weekday + &PhantomProvider, // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator + locale, + selection.pattern_items_for_data_loading(), + )?; + Ok(Self { + selection, + names, + _calendar: PhantomData, + }) + } + + /// Formats a date. + /// + /// For an example, see [`TypedNeoDateFormatter`]. + pub fn format(&self, date: &T) -> FormattedNeoDate + where + T: DateInput, + { + let datetime = ExtractedDateTimeInput::extract_from_date(date); + FormattedNeoDate { + pattern: self.selection.select(&datetime), + datetime, + names: self.names.as_borrowed(), + } + } +} + +mod private { + pub trait Sealed {} +} + +/// A collection of types and constants for specific variants of [`TypedNeoFormatter`]. +/// +/// Individual fields can be [`NeverMarker`] if they are not needed for the specific variant. +pub trait TypedNeoFormatterMarker: private::Sealed { + /// Components in the neo skeleton. + const COMPONENTS: NeoComponents; + /// Fields for [`TypedDateTimeNames`]. + type DateTimeNamesMarker: DateTimeNamesMarker; + /// Marker for loading year names. + type YearNamesV1Marker: KeyedDataMarker>; + /// Marker for loading month names. + type MonthNamesV1Marker: KeyedDataMarker>; + /// Marker for loading date skeleton patterns. + type DateSkeletonPatternsV1Marker: KeyedDataMarker>; + /// Marker for loading weekday names. + type WeekdayNamesV1Marker: KeyedDataMarker>; + /// Marker for loading day period names. + type DayPeriodNamesV1Marker: KeyedDataMarker>; + /// Marker for loading time skeleton patterns. + type TimeSkeletonPatternsV1Marker: KeyedDataMarker>; + /// Marker for loading the date/time glue pattern. + type DateTimePatternV1Marker: KeyedDataMarker>; + // TODO: Add WeekCalculator and FixedDecimalFormatter optional bindings here +} + +/// A collection of types and constants for specific variants of [`NeoFormatter`]. +/// +/// Individual fields can be [`NeverMarker`] if they are not needed for the specific variant. +/// +/// The cross-calendar fields should be either [`FullDataCalMarkers`] or [`NoDataCalMarkers`]. +/// +/// [`FullDataCalMarkers`]: _internal::FullDataCalMarkers +/// [`NoDataCalMarkers`]: _internal::NoDataCalMarkers +pub trait NeoFormatterMarker { + /// Components in the neo skeleton. + const COMPONENTS: NeoComponents; + /// Fields for [`TypedDateTimeNames`]. + type DateTimeNamesMarker: DateTimeNamesMarker; + /// Cross-calendar data markers for year names. + type Year: CalMarkers; + /// Cross-calendar data markers for month names. + type Month: CalMarkers; + /// Cross-calendar data markers for date skeleta. + type Skel: CalMarkers; + /// Marker for loading weekday names. + type WeekdayNamesV1Marker: KeyedDataMarker>; + /// Marker for loading day period names. + type DayPeriodNamesV1Marker: KeyedDataMarker>; + /// Marker for loading time skeleton patterns. + type TimeSkeletonPatternsV1Marker: KeyedDataMarker>; + /// Marker for loading the date/time glue pattern. + type DateTimePatternV1Marker: KeyedDataMarker>; + // TODO: Add WeekCalculator, FixedDecimalFormatter, and AnyCalendar optional bindings here +} + +/// Marker for a Year/Month/Day format, like "January 1, 2000" +#[derive(Debug)] +#[allow(clippy::exhaustive_enums)] // empty enum +pub enum NeoYearMonthDayMarker {} + +impl private::Sealed for NeoYearMonthDayMarker {} + +impl TypedNeoFormatterMarker for NeoYearMonthDayMarker { + const COMPONENTS: NeoComponents = + NeoComponents::Date(NeoDateComponents::Day(NeoDayComponents::YearMonthDay)); + type DateTimeNamesMarker = DateMarker; + + // Data to include + type YearNamesV1Marker = C::YearNamesV1Marker; + type MonthNamesV1Marker = C::MonthNamesV1Marker; + type DateSkeletonPatternsV1Marker = C::SkeletaV1Marker; + type WeekdayNamesV1Marker = WeekdayNamesV1Marker; + + // Data to exclude + type DayPeriodNamesV1Marker = NeverMarker>; + type TimeSkeletonPatternsV1Marker = NeverMarker>; + type DateTimePatternV1Marker = NeverMarker>; +} + +impl NeoFormatterMarker for NeoYearMonthDayMarker { + const COMPONENTS: NeoComponents = + NeoComponents::Date(NeoDateComponents::Day(NeoDayComponents::YearMonthDay)); + type DateTimeNamesMarker = DateMarker; + + // Data to include + type WeekdayNamesV1Marker = WeekdayNamesV1Marker; + type Year = FullData; + type Month = FullData; + type Skel = FullData; + + // Data to exclude + type DayPeriodNamesV1Marker = NeverMarker>; + type TimeSkeletonPatternsV1Marker = NeverMarker>; + type DateTimePatternV1Marker = NeverMarker>; +} + +size_test!(TypedNeoFormatter, typed_neo_year_month_day_formatter_size, 456); + +/// [`TypedNeoFormatter`] is a formatter capable of formatting dates and/or times from +/// a calendar selected at compile time. +/// +/// For more details, please read the [crate root docs][crate]. +/// +#[doc = typed_neo_year_month_day_formatter_size!()] +/// +///

+/// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, +/// including in SemVer minor releases. It can be enabled with the "experimental" Cargo feature +/// of the icu meta-crate. Use with caution. +/// #3347 +///
+#[derive(Debug)] +pub struct TypedNeoFormatter> { + selection: DatePatternSelectionData, + names: RawDateTimeNames, + _calendar: PhantomData, +} + +impl> TypedNeoFormatter { + /// Creates a [`TypedNeoFormatter`] for a date skeleton. + /// + /// # Examples + /// + /// ``` + /// use icu::calendar::Date; + /// use icu::calendar::Gregorian; + /// use icu::datetime::neo::TypedNeoFormatter; + /// use icu::datetime::neo::NeoYearMonthDayMarker; + /// use icu::datetime::neo_skeleton::NeoSkeletonLength; + /// use icu::locid::locale; + /// use writeable::assert_try_writeable_eq; + /// + /// let formatter = TypedNeoFormatter::::try_new( + /// &locale!("es-MX").into(), + /// NeoSkeletonLength::Long + /// ) + /// .unwrap(); + /// + /// assert_try_writeable_eq!( + /// formatter.format(&Date::try_new_gregorian_date(2023, 12, 20).unwrap()), + /// "20 de diciembre de 2023" + /// ); + /// ``` + #[cfg(feature = "compiled_data")] + pub fn try_new(locale: &DataLocale, length: NeoSkeletonLength) -> Result + where + crate::provider::Baked: Sized + // Date formatting keys + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + { + Self::try_new_internal( + &crate::provider::Baked, + &ExternalLoaderCompiledData, + locale, + length, + ) + } + + gen_any_buffer_constructors_with_external_loader!( + try_new, + try_new_with_any_provider, + try_new_with_buffer_provider, + try_new_internal, + length: NeoSkeletonLength + ); + + #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)] + pub fn try_new_with_length_unstable

( + provider: &P, + locale: &DataLocale, + length: NeoSkeletonLength, + ) -> Result + where + P: ?Sized + // Date formatting keys + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + // FixedDecimalFormatter keys + + DataProvider + // WeekCalculator keys + + DataProvider, + { + Self::try_new_internal(provider, &ExternalLoaderUnstable(provider), locale, length) + } + + fn try_new_internal( + provider: &P, + loader: &L, + locale: &DataLocale, + length: NeoSkeletonLength, + ) -> Result + where + P: ?Sized + // Date formatting keys + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + L: FixedDecimalFormatterLoader + WeekCalculatorLoader, + { + let selection = DatePatternSelectionData::try_new_with_skeleton( + &R::DateSkeletonPatternsV1Marker::bind(provider), + locale, + length, + R::COMPONENTS, + ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names.load_for_pattern::>>( - Some(provider), // year - Some(provider), // month - Some(provider), // weekday - None::<&PhantomProvider>, // day period - Some(loader), // fixed decimal formatter - Some(loader), // week calculator + names.load_for_pattern( + &R::YearNamesV1Marker::bind(provider), // year + &R::MonthNamesV1Marker::bind(provider), // month + &R::WeekdayNamesV1Marker::bind(provider), // weekday + &R::DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator locale, selection.pattern_items_for_data_loading(), )?; @@ -381,7 +655,345 @@ impl TypedNeoDateFormatter { } } -size_test!(NeoDateFormatter, neo_date_formatter_size, 552); +size_test!( + NeoFormatter, + neo_year_month_day_formatter_size, + 512 +); + +/// [`NeoFormatter`] is a formatter capable of formatting dates and/or times from +/// a calendar selected at runtime. +/// +/// For more details, please read the [crate root docs][crate]. +/// +#[doc = neo_year_month_day_formatter_size!()] +/// +///

+/// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, +/// including in SemVer minor releases. It can be enabled with the "experimental" Cargo feature +/// of the icu meta-crate. Use with caution. +/// #3347 +///
+#[derive(Debug)] +pub struct NeoFormatter { + selection: DatePatternSelectionData, + names: RawDateTimeNames, + calendar: AnyCalendar, +} + +impl NeoFormatter { + /// Construct a new [`NeoFormatter`] from compiled data. + /// + /// This method will pick the calendar off of the locale; and if unspecified or unknown will fall back to the default + /// calendar for the locale. See [`AnyCalendarKind`] for a list of supported calendars. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + /// + /// # Examples + /// + /// ``` + /// use icu::calendar::{any_calendar::AnyCalendar, Date}; + /// use icu::datetime::neo::NeoFormatter; + /// use icu::datetime::neo_skeleton::NeoSkeletonLength; + /// use icu_datetime::neo::NeoYearMonthDayMarker; + /// use icu::locid::locale; + /// use std::str::FromStr; + /// use writeable::assert_try_writeable_eq; + /// + /// let length = NeoSkeletonLength::Medium; + /// let locale = locale!("en-u-ca-hebrew"); + /// + /// let df = NeoFormatter::::try_new(&locale.into(), length) + /// .expect("Failed to create TypedDateFormatter instance."); + /// + /// let datetime = + /// Date::try_new_iso_date(2024, 5, 8).expect("Failed to construct Date."); + /// let any_datetime = datetime.to_any(); + /// + /// assert_try_writeable_eq!( + /// df.format(&any_datetime).expect("Calendars should match"), + /// "Nisan 30, 5784" + /// ); + /// ``` + /// + /// [`AnyCalendarKind`]: icu_calendar::AnyCalendarKind + #[inline(never)] + #[cfg(feature = "compiled_data")] + pub fn try_new(locale: &DataLocale, length: NeoSkeletonLength) -> Result + where + crate::provider::Baked: Sized + // Date formatting keys + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + { + Self::try_new_internal( + &crate::provider::Baked, + &ExternalLoaderCompiledData, + locale, + length, + ) + } + + gen_any_buffer_constructors_with_external_loader!( + try_new, + try_new_with_any_provider, + try_new_with_buffer_provider, + try_new_internal, + length: NeoSkeletonLength + ); + + #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)] + pub fn try_new_unstable

( + provider: &P, + locale: &DataLocale, + length: NeoSkeletonLength, + ) -> Result + where + P: ?Sized + // Date formatting keys + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider + + DataProvider + + DataProvider + + DataProvider + // AnyCalendar constructor keys + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider + // FixedDecimalFormatter keys + + DataProvider + // WeekCalculator keys + + DataProvider, + { + Self::try_new_internal(provider, &ExternalLoaderUnstable(provider), locale, length) + } + + fn try_new_internal( + provider: &P, + loader: &L, + locale: &DataLocale, + length: NeoSkeletonLength, + ) -> Result + where + P: ?Sized + // Date formatting keys + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + L: FixedDecimalFormatterLoader + WeekCalculatorLoader + AnyCalendarLoader, + { + let calendar = AnyCalendarLoader::load(loader, locale).map_err(LoadError::Data)?; + let kind = calendar.kind(); + let selection = DatePatternSelectionData::try_new_with_skeleton( + &AnyCalendarProvider::::new(provider, kind), + locale, + length, + R::COMPONENTS, + ) + .map_err(LoadError::Data)?; + let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); + names.load_for_pattern( + &AnyCalendarProvider::::new(provider, kind), // year + &AnyCalendarProvider::::new(provider, kind), // month + &R::WeekdayNamesV1Marker::bind(provider), // weekday + &R::DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator + locale, + selection.pattern_items_for_data_loading(), + )?; + Ok(Self { + selection, + names, + calendar, + }) + } + + /// Formats a date. + /// + /// If the date is in neither ISO-8601 nor the same calendar system as the formatter, + /// an error is returned. + /// + /// For an example, see [`NeoDateFormatter`]. + pub fn format(&self, date: &T) -> Result + where + T: DateInput, + { + let datetime = + if let Some(converted) = crate::calendar::convert_if_necessary(&self.calendar, date)? { + ExtractedDateTimeInput::extract_from_date(&converted) + } else { + ExtractedDateTimeInput::extract_from_date(date) + }; + Ok(FormattedNeoDate { + pattern: self.selection.select(&datetime), + datetime, + names: self.names.as_borrowed(), + }) + } +} + +size_test!(NeoDateFormatter, neo_date_formatter_size, 512); /// [`NeoDateFormatter`] is a formatter capable of formatting dates from any calendar, selected /// at runtime. For the difference between this and [`TypedNeoDateFormatter`], please read the @@ -398,7 +1010,7 @@ size_test!(NeoDateFormatter, neo_date_formatter_size, 552); #[derive(Debug)] pub struct NeoDateFormatter { selection: DatePatternSelectionData, - names: RawDateTimeNames, + names: RawDateTimeNames, calendar: AnyCalendar, } @@ -470,48 +1082,57 @@ impl NeoDateFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider // AnyCalendar constructor keys @@ -543,69 +1164,77 @@ impl NeoDateFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider, L: FixedDecimalFormatterLoader + WeekCalculatorLoader + AnyCalendarLoader, { let calendar = AnyCalendarLoader::load(loader, locale).map_err(LoadError::Data)?; let kind = calendar.kind(); - let any_calendar_provider = AnyCalendarProvider { provider, kind }; - let selection = DatePatternSelectionData::try_new_with_length::( - &any_calendar_provider, + let selection = DatePatternSelectionData::try_new_with_length( + &AnyCalendarProvider::::new(provider, kind), locale, length, ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names.load_for_pattern::>>( - Some(&any_calendar_provider), // year - Some(&any_calendar_provider), // month - Some(provider), // weekday - None::<&PhantomProvider>, // day period - Some(loader), // fixed decimal formatter - Some(loader), // week calculator + names.load_for_pattern( + &AnyCalendarProvider::::new(provider, kind), // year + &AnyCalendarProvider::::new(provider, kind), // month + &WeekdayNamesV1Marker::bind(provider), // weekday + &PhantomProvider, // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator locale, selection.pattern_items_for_data_loading(), )?; @@ -681,7 +1310,7 @@ impl<'a> FormattedNeoDate<'a> { } } -size_test!(NeoTimeFormatter, neo_time_formatter_size, 456); +size_test!(NeoTimeFormatter, neo_time_formatter_size, 312); /// [`NeoTimeFormatter`] can format times of day. /// It supports both 12-hour and 24-hour formats. @@ -697,7 +1326,7 @@ size_test!(NeoTimeFormatter, neo_time_formatter_size, 456); #[derive(Debug)] pub struct NeoTimeFormatter { selection: TimePatternSelectionData, - names: RawDateTimeNames, + names: RawDateTimeNames, } impl NeoTimeFormatter { @@ -782,13 +1411,13 @@ impl NeoTimeFormatter { let selection = TimePatternSelectionData::try_new_with_length(provider, locale, length) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names.load_for_pattern::, NeverMarker, NeverMarker, DayPeriodNamesV1Marker>( - None::<&PhantomProvider>, // year - None::<&PhantomProvider>, // month - None::<&PhantomProvider>, // weekday - Some(provider), // day period - Some(loader), // fixed decimal formatter - None::<&PhantomLoader>, // week calculator + names.load_for_pattern( + &PhantomProvider, // year + &PhantomProvider, // month + &PhantomProvider, // weekday + &DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + None::<&PhantomLoader>, // week calculator locale, selection.pattern_items_for_data_loading(), )?; @@ -823,7 +1452,7 @@ impl NeoTimeFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoTimeSkeletonComponents, + S: ?Sized + NeoSkeletonCommonData + NeoSkeletonComponents, crate::provider::Baked: Sized // Time formatting keys + DataProvider @@ -838,7 +1467,7 @@ impl NeoTimeFormatter { } gen_any_buffer_constructors_with_external_loader!( - S: TypedNeoTimeSkeletonComponents, + S: NeoSkeletonCommonData | NeoSkeletonComponents, try_new_with_skeleton, try_new_with_skeleton_with_any_provider, try_new_with_skeleton_with_buffer_provider, @@ -853,7 +1482,7 @@ impl NeoTimeFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoTimeSkeletonComponents, + S: ?Sized + NeoSkeletonCommonData + NeoSkeletonComponents, P: ?Sized // Time formatting keys + DataProvider @@ -876,25 +1505,29 @@ impl NeoTimeFormatter { length: NeoSkeletonLength, ) -> Result where - S: ?Sized + TypedNeoTimeSkeletonComponents, + S: ?Sized + NeoSkeletonCommonData + NeoSkeletonComponents, P: ?Sized // Date formatting keys + DataProvider + DataProvider, L: FixedDecimalFormatterLoader, { - let selection = TimePatternSelectionData::try_new_with_skeleton::< - S::TimeSkeletonPatternsV1Marker, - >(provider, locale, length, S::COMPONENTS) - .map_err(LoadError::Data)?; + let selection = + TimePatternSelectionData::try_new_with_skeleton::( + provider, + locale, + length, + ::COMPONENTS, + ) + .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names.load_for_pattern::( - None::<&PhantomProvider>, // year - None::<&PhantomProvider>, // month - None::<&PhantomProvider>, // weekday - Some(provider), // day period - Some(loader), // fixed decimal formatter - None::<&PhantomLoader>, // week calculator + names.load_for_pattern( + &PhantomProvider, // year + &PhantomProvider, // month + &PhantomProvider, // weekday + &S::DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + None::<&PhantomLoader>, // week calculator locale, selection.pattern_items_for_data_loading(), )?; @@ -981,7 +1614,7 @@ size_test!( #[derive(Debug)] pub struct TypedNeoDateTimeFormatter { selection: DateTimePatternSelectionData, - names: RawDateTimeNames, + names: RawDateTimeNames, _calendar: PhantomData, } @@ -1087,7 +1720,7 @@ impl TypedNeoDateTimeFormatter { )?; Ok(Self { selection: DateTimePatternSelectionData::Date(date_formatter.selection), - names: date_formatter.names, + names: date_formatter.names.into(), _calendar: PhantomData, }) } @@ -1179,7 +1812,7 @@ impl TypedNeoDateTimeFormatter { NeoTimeFormatter::try_new_with_length_internal(provider, loader, locale, length)?; Ok(Self { selection: DateTimePatternSelectionData::Time(time_formatter.selection), - names: time_formatter.names, + names: time_formatter.names.into(), _calendar: PhantomData, }) } @@ -1297,23 +1930,25 @@ impl TypedNeoDateTimeFormatter { + DataProvider, L: FixedDecimalFormatterLoader + WeekCalculatorLoader, { - let selection = DateTimeGluePatternSelectionData::try_new_with_lengths::< - C::DatePatternV1Marker, - _, - >(provider, provider, locale, date_length, time_length) + let selection = DateTimeGluePatternSelectionData::try_new_with_lengths( + &C::DatePatternV1Marker::bind(provider), + provider, + locale, + date_length, + time_length, + ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names - .load_for_pattern::( - Some(provider), // year - Some(provider), // month - Some(provider), // weekday - Some(provider), // day period - Some(loader), // fixed decimal formatter - Some(loader), // week calculator - locale, - selection.pattern_items_for_data_loading(), - )?; + names.load_for_pattern( + &C::YearNamesV1Marker::bind(provider), // year + &C::MonthNamesV1Marker::bind(provider), // month + &WeekdayNamesV1Marker::bind(provider), // weekday + &DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator + locale, + selection.pattern_items_for_data_loading(), + )?; Ok(Self { selection: DateTimePatternSelectionData::DateTimeGlue(selection), names, @@ -1396,7 +2031,7 @@ size_test!(NeoDateTimeFormatter, neo_date_time_formatter_size, 632); #[derive(Debug)] pub struct NeoDateTimeFormatter { selection: DateTimePatternSelectionData, - names: RawDateTimeNames, + names: RawDateTimeNames, calendar: AnyCalendar, } @@ -1469,55 +2104,64 @@ impl NeoDateTimeFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider // AnyCalendar constructor keys + DataProvider + DataProvider + + DataProvider + + DataProvider + DataProvider + DataProvider // FixedDecimalFormatter keys @@ -1542,48 +2186,57 @@ impl NeoDateTimeFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider, L: FixedDecimalFormatterLoader + WeekCalculatorLoader + AnyCalendarLoader, @@ -1592,7 +2245,7 @@ impl NeoDateTimeFormatter { NeoDateFormatter::try_new_with_length_internal(provider, loader, locale, length)?; Ok(Self { selection: DateTimePatternSelectionData::Date(date_formatter.selection), - names: date_formatter.names, + names: date_formatter.names.into(), calendar: date_formatter.calendar, }) } @@ -1704,7 +2357,7 @@ impl NeoDateTimeFormatter { NeoTimeFormatter::try_new_with_length_internal(provider, loader, locale, length)?; Ok(Self { selection: DateTimePatternSelectionData::Time(time_formatter.selection), - names: time_formatter.names, + names: time_formatter.names.into(), calendar, }) } @@ -1772,50 +2425,57 @@ impl NeoDateTimeFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider // Time formatting keys @@ -1826,6 +2486,8 @@ impl NeoDateTimeFormatter { // AnyCalendar constructor keys + DataProvider + DataProvider + + DataProvider + + DataProvider + DataProvider + DataProvider // FixedDecimalFormatter key @@ -1852,48 +2514,57 @@ impl NeoDateTimeFormatter { where P: ?Sized // DatePattern, YearNames, and MonthNames keys - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider - + DataProvider + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> + + DataProvider<>::Buddhist> + + DataProvider<>::Chinese> + + DataProvider<>::Coptic> + + DataProvider<>::Dangi> + + DataProvider<>::Ethiopian> + + DataProvider<>::EthiopianAmeteAlem> + + DataProvider<>::Gregorian> + + DataProvider<>::Hebrew> + + DataProvider<>::Indian> + + DataProvider<>::IslamicCivil> + + DataProvider<>::IslamicObservational> + + DataProvider<>::IslamicTabular> + + DataProvider<>::IslamicUmmAlQura> + + DataProvider<>::Japanese> + + DataProvider<>::JapaneseExtended> + + DataProvider<>::Persian> + + DataProvider<>::Roc> // Other date formatting keys + DataProvider // Time formatting keys @@ -1905,24 +2576,22 @@ impl NeoDateTimeFormatter { { let calendar = AnyCalendarLoader::load(loader, locale).map_err(LoadError::Data)?; let kind = calendar.kind(); - let any_calendar_provider = AnyCalendarProvider { provider, kind }; - let selection = - DateTimeGluePatternSelectionData::try_new_with_lengths::( - &any_calendar_provider, - provider, - locale, - date_length, - time_length, - ) - .map_err(LoadError::Data)?; + let selection = DateTimeGluePatternSelectionData::try_new_with_lengths( + &AnyCalendarProvider::::new(provider, kind), + provider, + locale, + date_length, + time_length, + ) + .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_fixed_decimal_formatter(); - names.load_for_pattern::( - Some(&any_calendar_provider), // year - Some(&any_calendar_provider), // month - Some(provider), // weekday - Some(provider), // day period - Some(loader), // fixed decimal formatter - Some(loader), // week calculator + names.load_for_pattern( + &AnyCalendarProvider::::new(provider, kind), // year + &AnyCalendarProvider::::new(provider, kind), // month + &WeekdayNamesV1Marker::bind(provider), // weekday + &DayPeriodNamesV1Marker::bind(provider), // day period + Some(loader), // fixed decimal formatter + Some(loader), // week calculator locale, selection.pattern_items_for_data_loading(), )?; diff --git a/components/datetime/src/neo_skeleton.rs b/components/datetime/src/neo_skeleton.rs index 326413fa19c..9d3abbfa833 100644 --- a/components/datetime/src/neo_skeleton.rs +++ b/components/datetime/src/neo_skeleton.rs @@ -4,7 +4,10 @@ //! Temporary module for neo datetime skeletons (Semantic Skeleta) +use crate::calendar::CalMarkers; +use crate::calendar::FullDataCalMarkers; use crate::calendar::NeverCalendar; +use crate::calendar::NoDataCalMarkers; use crate::options::components; use crate::provider::neo::*; use crate::CldrCalendar; @@ -14,56 +17,49 @@ use tinystr::tinystr; use tinystr::TinyAsciiStr; /// Sealed trait implemented by neo skeleton marker types. -pub trait TypedNeoSkeletonData -where - C: CldrCalendar + ?Sized, -{ - /// Marker for loading year names. - /// Can be [`NeverMarker`] if not needed for this skeleton. - type YearNamesV1Marker: KeyedDataMarker>; - /// Marker for loading month names. - /// Can be [`NeverMarker`] if not needed for this skeleton. - type MonthNamesV1Marker: KeyedDataMarker>; +pub trait NeoSkeletonCommonData { /// Marker for loading weekday names. /// Can be [`NeverMarker`] if not needed for this skeleton. type WeekdayNamesV1Marker: KeyedDataMarker>; /// Marker for loading day period names. /// Can be [`NeverMarker`] if not needed for this skeleton. type DayPeriodNamesV1Marker: KeyedDataMarker>; - /// Marker for loading date skeleton patterns. - /// Can be [`NeverMarker`] if not needed for this skeleton. - type DateSkeletonPatternsV1Marker: KeyedDataMarker>; /// Marker for loading time skeleton patterns. /// Can be [`NeverMarker`] if not needed for this skeleton. type TimeSkeletonPatternsV1Marker: KeyedDataMarker>; /// Marker for loading the date/time glue pattern. /// Can be [`NeverMarker`] if not needed for this skeleton. type DateTimePatternV1Marker: KeyedDataMarker>; - /// Marker for loading date/time combined patterns. - /// Can be [`NeverMarker`] if not needed for this skeleton. - type DateTimeSkeletonPatternsV1Marker: KeyedDataMarker>; } /// Sealed trait implemented by neo skeleton marker types. -pub trait TypedNeoDateSkeletonComponents: TypedNeoSkeletonData +pub trait TypedNeoSkeletonData: NeoSkeletonCommonData where C: CldrCalendar + ?Sized, { - /// Components in the neo skeleton. - const COMPONENTS: NeoDateComponents; + /// Marker for loading year names. + /// Can be [`NeverMarker`] if not needed for this skeleton. + type YearNamesV1Marker: KeyedDataMarker>; + /// Marker for loading month names. + /// Can be [`NeverMarker`] if not needed for this skeleton. + type MonthNamesV1Marker: KeyedDataMarker>; + /// Marker for loading date skeleton patterns. + /// Can be [`NeverMarker`] if not needed for this skeleton. + type DateSkeletonPatternsV1Marker: KeyedDataMarker>; } /// Sealed trait implemented by neo skeleton marker types. -pub trait TypedNeoTimeSkeletonComponents: TypedNeoSkeletonData { - /// Components in the neo skeleton. - const COMPONENTS: NeoTimeComponents; +pub trait NeoSkeletonData: NeoSkeletonCommonData { + /// Cross-calendar data markers for year names. + type Year: CalMarkers; + /// Cross-calendar data markers for month names. + type Month: CalMarkers; + /// Cross-calendar data markers for date skeleta. + type Skel: CalMarkers; } /// Sealed trait implemented by neo skeleton marker types. -pub trait TypedNeoSkeletonComponents: TypedNeoSkeletonData -where - C: CldrCalendar + ?Sized, -{ +pub trait NeoSkeletonComponents { /// Components in the neo skeleton. const COMPONENTS: NeoComponents; } @@ -133,6 +129,14 @@ impl NeoSkeletonLength { #[allow(clippy::exhaustive_enums)] // empty enum pub enum YearMonthMarker {} +impl NeoSkeletonCommonData for YearMonthMarker { + // Data to exclude + type WeekdayNamesV1Marker = NeverMarker>; + type DayPeriodNamesV1Marker = NeverMarker>; + type TimeSkeletonPatternsV1Marker = NeverMarker>; + type DateTimePatternV1Marker = NeverMarker>; +} + impl TypedNeoSkeletonData for YearMonthMarker where C: CldrCalendar, @@ -140,21 +144,56 @@ where // Data to include type YearNamesV1Marker = C::YearNamesV1Marker; type MonthNamesV1Marker = C::MonthNamesV1Marker; - type DateSkeletonPatternsV1Marker = C::DateSkeletonPatternsV1Marker; + type DateSkeletonPatternsV1Marker = C::SkeletaV1Marker; +} + +impl NeoSkeletonData for YearMonthMarker { + // Data to include + type Year = FullDataCalMarkers; + type Month = FullDataCalMarkers; + type Skel = FullDataCalMarkers; +} + +impl NeoSkeletonComponents for YearMonthMarker { + const COMPONENTS: NeoComponents = NeoComponents::Date(NeoDateComponents::YearMonth); +} + +/// Marker for a day, month, and year, as in +/// “January 1, 2000”. +#[derive(Debug)] +#[allow(clippy::exhaustive_enums)] // empty enum +pub enum YearMonthDayMarker {} + +impl NeoSkeletonCommonData for YearMonthDayMarker { + // Data to include + type WeekdayNamesV1Marker = WeekdayNamesV1Marker; // Data to exclude - type WeekdayNamesV1Marker = NeverMarker>; type DayPeriodNamesV1Marker = NeverMarker>; type TimeSkeletonPatternsV1Marker = NeverMarker>; type DateTimePatternV1Marker = NeverMarker>; - type DateTimeSkeletonPatternsV1Marker = NeverMarker>; } -impl TypedNeoDateSkeletonComponents for YearMonthMarker +impl TypedNeoSkeletonData for YearMonthDayMarker where C: CldrCalendar, { - const COMPONENTS: NeoDateComponents = NeoDateComponents::YearMonth; + // Data to include + type YearNamesV1Marker = C::YearNamesV1Marker; + type MonthNamesV1Marker = C::MonthNamesV1Marker; + type DateSkeletonPatternsV1Marker = C::SkeletaV1Marker; +} + +impl NeoSkeletonData for YearMonthDayMarker { + // Data to include + type Year = FullDataCalMarkers; + type Month = FullDataCalMarkers; + type Skel = FullDataCalMarkers; +} + +impl NeoSkeletonComponents for YearMonthDayMarker { + const COMPONENTS: NeoComponents = + NeoComponents::Date(NeoDateComponents::Day(NeoDayComponents::YearMonthDay)); } /// Marker for an hour and minute (12-hour or 24-hour chosen by locale), as in @@ -163,22 +202,32 @@ where #[allow(clippy::exhaustive_enums)] // empty enum pub enum HourMinuteMarker {} -impl TypedNeoSkeletonData for HourMinuteMarker { +impl NeoSkeletonCommonData for HourMinuteMarker { // Data to include type DayPeriodNamesV1Marker = DayPeriodNamesV1Marker; type TimeSkeletonPatternsV1Marker = TimeNeoSkeletonPatternsV1Marker; // Data to exclude - type YearNamesV1Marker = NeverMarker>; - type MonthNamesV1Marker = NeverMarker>; type WeekdayNamesV1Marker = NeverMarker>; type DateTimePatternV1Marker = NeverMarker>; +} + +impl TypedNeoSkeletonData for HourMinuteMarker { + // Data to exclude + type YearNamesV1Marker = NeverMarker>; + type MonthNamesV1Marker = NeverMarker>; type DateSkeletonPatternsV1Marker = NeverMarker>; - type DateTimeSkeletonPatternsV1Marker = NeverMarker>; } -impl TypedNeoTimeSkeletonComponents for HourMinuteMarker { - const COMPONENTS: NeoTimeComponents = NeoTimeComponents::HourMinute; +impl NeoSkeletonData for HourMinuteMarker { + // Data to exclude + type Year = NoDataCalMarkers; + type Month = NoDataCalMarkers; + type Skel = NoDataCalMarkers; +} + +impl NeoSkeletonComponents for HourMinuteMarker { + const COMPONENTS: NeoComponents = NeoComponents::Time(NeoTimeComponents::HourMinute); } // TODO: Add more of these TypedNeoSkeletonData marker types. diff --git a/components/datetime/src/provider/mod.rs b/components/datetime/src/provider/mod.rs index 803361f32e7..4ed7a48dbcb 100644 --- a/components/datetime/src/provider/mod.rs +++ b/components/datetime/src/provider/mod.rs @@ -100,8 +100,32 @@ const _: () = { #[cfg(feature = "experimental")] icu_datetime_data::impl_datetime_patterns_time_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_buddhist_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_chinese_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_coptic_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_dangi_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_ethiopic_skeleton_v1!(Baked); #[cfg(feature = "experimental")] icu_datetime_data::impl_datetime_patterns_gregory_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_hebrew_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_indian_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_islamic_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_japanese_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_japanext_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_persian_skeleton_v1!(Baked); + #[cfg(feature = "experimental")] + icu_datetime_data::impl_datetime_patterns_roc_skeleton_v1!(Baked); #[cfg(feature = "experimental")] icu_datetime_data::impl_datetime_symbols_buddhist_months_v1!(Baked); diff --git a/components/datetime/src/provider/neo.rs b/components/datetime/src/provider/neo.rs index 03f72fb50ec..1ed1b128e04 100644 --- a/components/datetime/src/provider/neo.rs +++ b/components/datetime/src/provider/neo.rs @@ -693,22 +693,30 @@ pub struct DateTimeSkeletonsV1<'data> { pub map: ZeroMap<'data, str, PatternULE>, } -pub(crate) struct ErasedYearNamesV1Marker; -impl DataMarker for ErasedYearNamesV1Marker { +/// Calendar-agnostic year name data marker +#[derive(Debug)] +pub struct YearNamesV1Marker; +impl DataMarker for YearNamesV1Marker { type Yokeable = YearNamesV1<'static>; } -pub(crate) struct ErasedMonthNamesV1Marker; -impl DataMarker for ErasedMonthNamesV1Marker { +/// Calendar-agnostic month name data marker +#[derive(Debug)] +pub struct MonthNamesV1Marker; +impl DataMarker for MonthNamesV1Marker { type Yokeable = MonthNamesV1<'static>; } -pub(crate) struct ErasedDatePatternV1Marker; -impl DataMarker for ErasedDatePatternV1Marker { +/// Calendar-agnostic date pattern data marker +#[derive(Debug)] +pub struct DatePatternV1Marker; +impl DataMarker for DatePatternV1Marker { type Yokeable = DatePatternV1<'static>; } -pub(crate) struct ErasedPackedSkeletonDataV1Marker; -impl DataMarker for ErasedPackedSkeletonDataV1Marker { +/// Calendar-agnostic date skeleta data marker +#[derive(Debug)] +pub struct SkeletaV1Marker; +impl DataMarker for SkeletaV1Marker { type Yokeable = PackedSkeletonDataV1<'static>; } diff --git a/components/datetime/src/raw/neo.rs b/components/datetime/src/raw/neo.rs index c8c10e53f8e..d1ee47259b5 100644 --- a/components/datetime/src/raw/neo.rs +++ b/components/datetime/src/raw/neo.rs @@ -2,12 +2,9 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). -use crate::calendar::DatePatternV1Provider; use crate::input::ExtractedDateTimeInput; use crate::neo_pattern::DateTimePattern; -use crate::neo_skeleton::{ - NeoDateComponents, NeoDateSkeleton, NeoSkeletonLength, NeoTimeComponents, NeoTimeSkeleton, -}; +use crate::neo_skeleton::{NeoComponents, NeoDateSkeleton, NeoSkeletonLength, NeoTimeSkeleton}; use crate::options::length; use crate::pattern::runtime::{PatternBorrowed, PatternMetadata}; use crate::pattern::{runtime, PatternItem}; @@ -19,15 +16,15 @@ use zerovec::ZeroSlice; #[derive(Debug)] pub(crate) enum DatePatternSelectionData { - SingleDate(DataPayload), + SingleDate(DataPayload), SkeletonDate { skeleton: NeoDateSkeleton, - payload: DataPayload, + payload: DataPayload, }, #[allow(dead_code)] // TODO(#4478) OptionalEra { - with_era: DataPayload, - without_era: DataPayload, + with_era: DataPayload, + without_era: DataPayload, }, } @@ -42,7 +39,7 @@ pub(crate) enum TimePatternSelectionData { #[allow(dead_code)] // TODO SkeletonTime { skeleton: NeoTimeSkeleton, - payload: DataPayload, + payload: DataPayload, }, } @@ -77,14 +74,11 @@ pub(crate) enum DateTimePatternDataBorrowed<'a> { } impl DatePatternSelectionData { - pub(crate) fn try_new_with_length( - provider: &(impl DatePatternV1Provider + ?Sized), + pub(crate) fn try_new_with_length( + provider: &(impl BoundDataProvider + ?Sized), locale: &DataLocale, length: length::Date, - ) -> Result - where - M: DataMarker>, - { + ) -> Result { let mut locale = locale.clone(); locale.set_aux(AuxiliaryKeys::from_subtag(aux::pattern_subtag_for( match length { @@ -96,7 +90,7 @@ impl DatePatternSelectionData { None, // no hour cycle for date patterns ))); let payload = provider - .load(DataRequest { + .load_bound(DataRequest { locale: &locale, metadata: Default::default(), })? @@ -105,15 +99,16 @@ impl DatePatternSelectionData { Ok(Self::SingleDate(payload)) } - pub(crate) fn try_new_with_skeleton( - provider: &(impl DataProvider + ?Sized), + pub(crate) fn try_new_with_skeleton( + provider: &(impl BoundDataProvider + ?Sized), locale: &DataLocale, length: NeoSkeletonLength, - components: NeoDateComponents, - ) -> Result - where - M: KeyedDataMarker>, - { + components: NeoComponents, + ) -> Result { + let NeoComponents::Date(components) = components else { + // TODO: Refactor to eliminate the unreachable + unreachable!() + }; let mut locale = locale.clone(); let subtag = match Subtag::try_from_raw(*components.id_str().all_bytes()) { Ok(subtag) => subtag, @@ -127,7 +122,7 @@ impl DatePatternSelectionData { }; locale.set_aux(AuxiliaryKeys::from_subtag(subtag)); let payload = provider - .load(DataRequest { + .load_bound(DataRequest { locale: &locale, metadata: Default::default(), })? @@ -214,11 +209,15 @@ impl TimePatternSelectionData { provider: &(impl DataProvider + ?Sized), locale: &DataLocale, length: NeoSkeletonLength, - components: NeoTimeComponents, + components: NeoComponents, ) -> Result where M: KeyedDataMarker>, { + let NeoComponents::Time(components) = components else { + // TODO: Refactor to eliminate the unreachable + unreachable!() + }; let mut locale = locale.clone(); let subtag = match Subtag::try_from_raw(*components.id_str().all_bytes()) { Ok(subtag) => subtag, @@ -284,8 +283,8 @@ impl<'a> TimePatternDataBorrowed<'a> { } impl DateTimeGluePatternSelectionData { - pub(crate) fn try_new_with_lengths( - date_pattern_provider: &(impl DatePatternV1Provider + ?Sized), + pub(crate) fn try_new_with_lengths

( + date_pattern_provider: &(impl BoundDataProvider + ?Sized), provider: &P, locale: &DataLocale, date_length: length::Date, @@ -293,9 +292,8 @@ impl DateTimeGluePatternSelectionData { ) -> Result where P: DataProvider + DataProvider + ?Sized, - M: DataMarker>, { - let date = DatePatternSelectionData::try_new_with_length::( + let date = DatePatternSelectionData::try_new_with_length( date_pattern_provider, locale, date_length, diff --git a/provider/core/src/data_provider.rs b/provider/core/src/data_provider.rs index d8a3684ce09..3e41b52a633 100644 --- a/provider/core/src/data_provider.rs +++ b/provider/core/src/data_provider.rs @@ -2,6 +2,9 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +use core::marker::PhantomData; +use yoke::Yokeable; + use crate::error::DataError; use crate::key::DataKey; use crate::marker::{DataMarker, KeyedDataMarker}; @@ -127,6 +130,130 @@ where } } +/// A data provider that loads data for a specific data type. +/// +/// Unlike [`DataProvider`], the provider is bound to a specific key ahead of time. +/// +/// This crate provides [`DataProviderWithKey`] which implements this trait on a single provider +/// with a single key. However, this trait can also be implemented on providers that fork between +/// multiple keys that all return the same data type. For example, it can abstract over many +/// calendar systems in the datetime formatter. +/// +/// [`AnyMarker`]: crate::any::AnyMarker +pub trait BoundDataProvider +where + M: DataMarker, +{ + /// Query the provider for data, returning the result. + /// + /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an + /// Error with more information. + fn load_bound(&self, req: DataRequest) -> Result, DataError>; + /// Returns the [`DataKey`] that this provider uses for loading data. + fn bound_key(&self) -> DataKey; +} + +impl<'a, M, P> BoundDataProvider for &'a P +where + M: DataMarker, + P: BoundDataProvider + ?Sized, +{ + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + (*self).load_bound(req) + } + #[inline] + fn bound_key(&self) -> DataKey { + (*self).bound_key() + } +} + +impl BoundDataProvider for alloc::boxed::Box

+where + M: DataMarker, + P: BoundDataProvider + ?Sized, +{ + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + (**self).load_bound(req) + } + #[inline] + fn bound_key(&self) -> DataKey { + (**self).bound_key() + } +} + +impl BoundDataProvider for alloc::rc::Rc

+where + M: DataMarker, + P: BoundDataProvider + ?Sized, +{ + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + (**self).load_bound(req) + } + #[inline] + fn bound_key(&self) -> DataKey { + (**self).bound_key() + } +} + +#[cfg(target_has_atomic = "ptr")] +impl BoundDataProvider for alloc::sync::Arc

+where + M: DataMarker, + P: BoundDataProvider + ?Sized, +{ + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + (**self).load_bound(req) + } + #[inline] + fn bound_key(&self) -> DataKey { + (**self).bound_key() + } +} + +/// A [`DataProvider`] associated with a specific key. +/// +/// Implements [`BoundDataProvider`]. +#[derive(Debug)] +pub struct DataProviderWithKey { + inner: P, + _marker: PhantomData, +} + +impl DataProviderWithKey +where + M: KeyedDataMarker, + P: DataProvider, +{ + /// Creates a [`DataProviderWithKey`] from a [`DataProvider`] with a [`KeyedDataMarker`]. + pub const fn new(inner: P) -> Self { + Self { + inner, + _marker: PhantomData, + } + } +} + +impl BoundDataProvider for DataProviderWithKey +where + M: KeyedDataMarker, + M0: DataMarker, + Y: for<'a> Yokeable<'a>, + P: DataProvider, +{ + #[inline] + fn load_bound(&self, req: DataRequest) -> Result, DataError> { + self.inner.load(req).map(DataResponse::cast) + } + #[inline] + fn bound_key(&self) -> DataKey { + M::KEY + } +} + #[cfg(test)] mod test { diff --git a/provider/core/src/lib.rs b/provider/core/src/lib.rs index e90fa483e5a..0ce44cb8911 100644 --- a/provider/core/src/lib.rs +++ b/provider/core/src/lib.rs @@ -153,7 +153,9 @@ pub mod marker; pub mod serde; // Types from private modules +pub use crate::data_provider::BoundDataProvider; pub use crate::data_provider::DataProvider; +pub use crate::data_provider::DataProviderWithKey; pub use crate::data_provider::DynamicDataProvider; pub use crate::error::DataError; pub use crate::error::DataErrorKind; @@ -213,6 +215,8 @@ pub mod prelude { #[cfg(feature = "experimental")] pub use crate::AuxiliaryKeys; #[doc(no_inline)] + pub use crate::BoundDataProvider; + #[doc(no_inline)] pub use crate::BufferMarker; #[doc(no_inline)] pub use crate::BufferProvider; diff --git a/provider/core/src/marker.rs b/provider/core/src/marker.rs index 83aa2c524ec..782dad1b217 100644 --- a/provider/core/src/marker.rs +++ b/provider/core/src/marker.rs @@ -6,7 +6,7 @@ use core::marker::PhantomData; -use crate::{data_key, key::DataKey}; +use crate::{data_key, DataKey, DataProvider, DataProviderWithKey}; use yoke::Yokeable; /// Trait marker for data structs. All types delivered by the data provider must be associated with @@ -84,6 +84,15 @@ pub trait DataMarker: 'static { pub trait KeyedDataMarker: DataMarker { /// The single [`DataKey`] associated with this marker. const KEY: DataKey; + + /// Binds this [`KeyedDataMarker`] to a provider supporting it. + fn bind

(provider: P) -> DataProviderWithKey + where + P: DataProvider, + Self: Sized, + { + DataProviderWithKey::new(provider) + } } /// A [`DataMarker`] that never returns data.