From 7e05025a564bc68fd10594e1413937e67f419ea9 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 31 Oct 2024 18:09:04 -0700 Subject: [PATCH] Checkpoint: one model for dynamic datetime skeletons --- components/datetime/src/combo.rs | 39 +- components/datetime/src/dynamic.rs | 470 ++++++++++++++++++ components/datetime/src/fieldset.rs | 39 +- components/datetime/src/lib.rs | 1 + components/datetime/src/neo.rs | 132 +++-- components/datetime/src/raw/neo.rs | 96 ++-- .../datetime/src/scaffold/dynamic_impls.rs | 83 ++++ components/datetime/src/scaffold/mod.rs | 1 + 8 files changed, 755 insertions(+), 106 deletions(-) create mode 100644 components/datetime/src/dynamic.rs create mode 100644 components/datetime/src/scaffold/dynamic_impls.rs diff --git a/components/datetime/src/combo.rs b/components/datetime/src/combo.rs index 5b60efb8328..58dff795eaa 100644 --- a/components/datetime/src/combo.rs +++ b/components/datetime/src/combo.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; -use crate::{format::neo::*, neo_skeleton::*, provider::neo::*, scaffold::*}; +use crate::{dynamic::*, format::neo::*, neo_skeleton::*, provider::neo::*, scaffold::*}; use icu_provider::marker::NeverMarker; /// Struct for combining date, time, and zone fields. @@ -140,6 +140,15 @@ where const COMPONENTS: NeoComponents = NeoComponents::Date(D::COMPONENTS); } +impl GetField for Combo +where + D: HasConstDateComponents, +{ + fn get_field(&self) -> CompositeFieldSet { + todo!() + } +} + impl DateTimeMarkers for Combo where D: DateTimeMarkers, @@ -178,6 +187,15 @@ where const COMPONENTS: NeoComponents = NeoComponents::Time(T::COMPONENTS); } +impl GetField for Combo +where + T: HasConstTimeComponents, +{ + fn get_field(&self) -> CompositeFieldSet { + todo!() + } +} + impl DateTimeMarkers for Combo where T: DateTimeMarkers, @@ -216,6 +234,15 @@ where const COMPONENTS: NeoComponents = NeoComponents::Zone(Z::COMPONENT); } +impl GetField for Combo +where + Z: HasConstZoneComponent, +{ + fn get_field(&self) -> CompositeFieldSet { + todo!() + } +} + impl DateTimeMarkers for Combo where Z: DateTimeMarkers, @@ -256,6 +283,16 @@ where const COMPONENTS: NeoComponents = NeoComponents::DateTime(D::COMPONENTS, T::COMPONENTS); } +impl GetField for Combo +where + D: HasConstDateComponents, + T: HasConstTimeComponents, +{ + fn get_field(&self) -> CompositeFieldSet { + todo!() + } +} + impl DateTimeMarkers for Combo where D: DateTimeMarkers, diff --git a/components/datetime/src/dynamic.rs b/components/datetime/src/dynamic.rs new file mode 100644 index 00000000000..698b8735207 --- /dev/null +++ b/components/datetime/src/dynamic.rs @@ -0,0 +1,470 @@ +// This file is part of ICU4X. For terms of use, please see the file +// 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 crate::options::preferences::HourCycle; +use crate::{fieldset, NeoSkeletonLength}; +use crate::neo_skeleton::{Alignment, FractionalSecondDigits, NeoTimeZoneStyle}; +use crate::scaffold::DateTimeMarkers; +use icu_provider::prelude::*; +use crate::raw::neo::RawNeoOptions; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum DateFieldSet { + /// The day of the month, as in + /// “on the 1st”. + D(fieldset::D), + /// The month and day of the month, as in + /// “January 1st”. + MD(fieldset::MD), + /// The year, month, and day of the month, as in + /// “January 1st, 2000”. + YMD(fieldset::YMD), + /// The day of the month and day of the week, as in + /// “Saturday 1st”. + DE(fieldset::DE), + /// The month, day of the month, and day of the week, as in + /// “Saturday, January 1st”. + MDE(fieldset::MDE), + /// The year, month, day of the month, and day of the week, as in + /// “Saturday, January 1st, 2000”. + YMDE(fieldset::YMDE), + /// The day of the week alone, as in + /// “Saturday”. + E(fieldset::E), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum CalendarPeriodFieldSet { + /// A standalone month, as in + /// “January”. + M(fieldset::M), + /// A month and year, as in + /// “January 2000”. + YM(fieldset::YM), + /// A year, as in + /// “2000”. + Y(fieldset::Y), + // TODO: Add support for week-of-year + // /// The year and week of the year, as in + // /// “52nd week of 1999”. + // YW(fieldset::YW), + // TODO(#501): Consider adding support for Quarter and YearQuarter. +} + +/// Field set for a standalone time of day. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum TimeFieldSet { + /// An hour (12-hour or 24-hour chosen by locale), as in + /// "4 pm" or "16h" + H(fieldset::H), + /// An hour and minute (12-hour or 24-hour chosen by locale), as in + /// "4:03 pm" or "16:03" + HM(fieldset::HM), + /// An hour, minute, and second (12-hour or 24-hour chosen by locale), as in + /// "4:03:51 pm" or "16:03:51" + HMS(fieldset::HMS), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub struct TimeZoneStyleWithLength { + pub style: NeoTimeZoneStyle, + pub length: NeoSkeletonLength, +} + +/// Field set for a time when used in combination with a date. +/// +/// This is separate from [`TimeFieldSet`] in order to avoid duplication of options. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum TimeOfDateFieldSet { + /// An hour (12-hour or 24-hour chosen by locale), as in + /// "4 pm" or "16h" + #[non_exhaustive] + H {}, + /// An hour and minute (12-hour or 24-hour chosen by locale), as in + /// "4:03 pm" or "16:03" + #[non_exhaustive] + HM {}, + /// An hour, minute, and second (12-hour or 24-hour chosen by locale), as in + /// "4:03:51 pm" or "16:03:51" + #[non_exhaustive] + HMS { + fractional_second_digits: Option + } +} + +impl TimeOfDateFieldSet { + /// Construct a [`TimeOfDateFieldSet::H`]. + pub const fn h() -> Self { + Self::H {} + } + /// Construct a [`TimeOfDateFieldSet::HM`]. + pub const fn hm() -> Self { + Self::HM {} + } + /// Construct a [`TimeOfDateFieldSet::HMS`]. + pub const fn hms() -> Self { + Self::HMS { + fractional_second_digits: None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum DateAndTimeFieldSet { + /// The day of the month with time of day, as in + /// “on the 1st at 10:31 AM”. + #[non_exhaustive] + D { + date: fieldset::D, + time: TimeOfDateFieldSet, + }, + /// The month and day of the month with time of day, as in + /// “January 1st at 10:31 AM”. + #[non_exhaustive] + MD { + date: fieldset::MD, + time: TimeOfDateFieldSet, + }, + /// The year, month, and day of the month with time of day, as in + /// “January 1st, 2000 at 10:31 AM”. + #[non_exhaustive] + YMD { + date: fieldset::YMD, + time: TimeOfDateFieldSet, + }, + /// The day of the month and day of the week with time of day, as in + /// “Saturday 1st at 10:31 AM”. + #[non_exhaustive] + DE { + date: fieldset::DE, + time: TimeOfDateFieldSet, + }, + /// The month, day of the month, and day of the week with time of day, as in + /// “Saturday, January 1st at 10:31 AM”. + #[non_exhaustive] + MDE { + date: fieldset::MDE, + time: TimeOfDateFieldSet, + }, + /// The year, month, day of the month, and day of the week with time of day, as in + /// “Saturday, January 1st, 2000 at 10:31 AM”. + #[non_exhaustive] + YMDE { + date: fieldset::YMDE, + time: TimeOfDateFieldSet, + }, + /// The day of the week alone with time of day, as in + /// “Saturday at 10:31 AM”. + #[non_exhaustive] + E { + date: fieldset::E, + time: TimeOfDateFieldSet, + alignment: Option, + } +} + +impl DateAndTimeFieldSet { + /// Construct a [`DateAndTimeFieldSet::D`]. + pub const fn d(date: fieldset::D, time: TimeOfDateFieldSet) -> Self { + Self::D { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::MD`]. + pub const fn md(date: fieldset::MD, time: TimeOfDateFieldSet) -> Self { + Self::MD { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::YMD`]. + pub const fn ymd(date: fieldset::YMD, time: TimeOfDateFieldSet) -> Self { + Self::YMD { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::DE`]. + pub const fn de(date: fieldset::DE, time: TimeOfDateFieldSet) -> Self { + Self::DE { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::MDE`]. + pub const fn mde(date: fieldset::MDE, time: TimeOfDateFieldSet) -> Self { + Self::MDE { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::YMDE`]. + pub const fn ymde(date: fieldset::YMDE, time: TimeOfDateFieldSet) -> Self { + Self::YMDE { + date, + time, + } + } + /// Construct a [`DateAndTimeFieldSet::E`]. + pub const fn e(date: fieldset::E, time: TimeOfDateFieldSet) -> Self { + Self::E { + date, + time, + alignment: None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum CompositeFieldSet { + /// Field set for a date. + Date(DateFieldSet), + /// Field set for a calendar period. + CalendarPeriod(CalendarPeriodFieldSet), + /// Field set for a time. + Time(TimeFieldSet), + /// Field set for a time zone. + Zone(TimeZoneStyleWithLength), + /// Field set for a date and a time together. + DateTime(DateAndTimeFieldSet), + /// Field set for a date and a time zone together. + DateZone(DateFieldSet, NeoTimeZoneStyle), + /// Field set for a time and a time zone together. + TimeZone(TimeFieldSet, NeoTimeZoneStyle), + /// Field set for a date, a time, and a time zone together. + DateTimeZone(DateAndTimeFieldSet, NeoTimeZoneStyle), +} + +macro_rules! impl_attrs { + (@skip_fns, $type:path, [$(($attr_var:ident, $str_var:ident, $value:literal)),+,]) => { + impl $type { + $( + const $attr_var: &'static DataMarkerAttributes = DataMarkerAttributes::from_str_or_panic($value); + )+ + $( + const $str_var: &'static str = $value; + )+ + /// All attributes associated with this enum. + pub const ALL_DATA_MARKER_ATTRIBUTES: &[&DataMarkerAttributes] = &[ + $( + Self::$attr_var, + )+ + ]; + } + }; + ($type:path, [$(($variant:ident, $attr_var:ident, $str_var:ident, $value:literal)),+,]) => { + impl_attrs! { @skip_fns, $type, [$(($attr_var, $str_var, $value)),+,] } + impl $type { + /// Returns a stable string identifying this set of fields. + pub(crate) const fn id_str(self) -> &'static DataMarkerAttributes { + match self { + $( + Self::$variant(_) => Self::$attr_var, + )+ + } + } + pub(crate) fn to_raw_options(self) -> RawNeoOptions { + match self { + $( + Self::$variant(variant) => variant.to_raw_options(), + )+ + } + } + } + }; +} + +impl_attrs! { + DateFieldSet, + [ + (D, ATTR_D, STR_D, "d"), + (MD, ATTR_MD, STR_MD, "m0d"), + (YMD, ATTR_YMD, STR_YMD, "ym0d"), + (DE, ATTR_DE, STR_DE, "de"), + (MDE, ATTR_MDE, STR_MDE, "m0de"), + (YMDE, ATTR_YMDE, STR_YMDE, "ym0de"), + (E, ATTR_E, STR_E, "e"), + ] +} + +impl_attrs! { + CalendarPeriodFieldSet, + [ + (M, ATTR_M, STR_M, "m0"), + (YM, ATTR_YM, STR_YM, "ym0"), + (Y, ATTR_Y, STR_Y, "y"), + ] +} + +impl_attrs! { + @skip_fns, + TimeFieldSet, + [ + (ATTR_H, STR_H, "j"), + (ATTR_HM, STR_HM, "jm"), + (ATTR_HMS, STR_HMS, "jms"), + (ATTR_H12, STR_H12, "h"), + (ATTR_H12M, STR_H12M, "hm"), + (ATTR_H12MS, STR_H12MS, "hms"), + (ATTR_H24, STR_H24, "h0"), + (ATTR_H24M, STR_H24M, "h0m"), + (ATTR_H24MS, STR_H24MS, "h0ms"), + ] +} + +impl TimeFieldSet { + pub(crate) const fn id_str_for_hour_cycle(self, hour_cycle: Option) -> &'static DataMarkerAttributes { + use HourCycle::*; + match (self, hour_cycle) { + (TimeFieldSet::H(_), None) => Self::ATTR_H, + (TimeFieldSet::H(_), Some(H11 | H12)) => Self::ATTR_H12, + (TimeFieldSet::H(_), Some(H23 | H24)) => Self::ATTR_H24, + (TimeFieldSet::HM(_), None) => Self::ATTR_HM, + (TimeFieldSet::HM(_), Some(H11 | H12)) => Self::ATTR_H12M, + (TimeFieldSet::HM(_), Some(H23 | H24)) => Self::ATTR_H24M, + (TimeFieldSet::HMS(_), None) => Self::ATTR_HMS, + (TimeFieldSet::HMS(_), Some(H11 | H12)) => Self::ATTR_H12MS, + (TimeFieldSet::HMS(_), Some(H23 | H24)) => Self::ATTR_H24MS, + } + } + pub(crate) fn to_raw_options(self) -> RawNeoOptions { + match self { + TimeFieldSet::H(variant) => variant.to_raw_options(), + TimeFieldSet::HM(variant) => variant.to_raw_options(), + TimeFieldSet::HMS(variant) => variant.to_raw_options(), + } + } +} + +impl TimeZoneStyleWithLength { + pub(crate) fn to_raw_options(self) -> RawNeoOptions { + RawNeoOptions { + length: self.length, + alignment: None, + year_style: None, + fractional_second_digits: None, + } + } +} + +impl TimeOfDateFieldSet { + pub(crate) fn set_on_raw_options(self, options: &mut RawNeoOptions) { + match self { + TimeOfDateFieldSet::H { } => (), + TimeOfDateFieldSet::HM { } => (), + TimeOfDateFieldSet::HMS { fractional_second_digits } => { + options.fractional_second_digits = fractional_second_digits; + }, + } + } + pub(crate) fn to_time_field_set_with_length_and_alignment(self, length: NeoSkeletonLength, alignment: Option) -> TimeFieldSet { + match self { + TimeOfDateFieldSet::H { } => TimeFieldSet::H(fieldset::H { + length, + alignment, + }), + TimeOfDateFieldSet::HM { } => TimeFieldSet::HM(fieldset::HM { + length, + alignment, + }), + TimeOfDateFieldSet::HMS { fractional_second_digits } => TimeFieldSet::HMS(fieldset::HMS { + length, + alignment, + fractional_second_digits, + }), + } + } +} + +impl_attrs! { + @skip_fns, + DateAndTimeFieldSet, + [ + (ATTR_EHM, STR_EHM, "ejm"), + (ATTR_EHMS, STR_EHMS, "ejms"), + ] +} + +impl DateAndTimeFieldSet { + pub(crate) const fn id_str(self) -> Option<&'static DataMarkerAttributes> { + match self { + DateAndTimeFieldSet::E { time: TimeOfDateFieldSet::HM { .. }, ..} => Some(Self::ATTR_EHM), + DateAndTimeFieldSet::E { time: TimeOfDateFieldSet::HMS { .. }, ..} => Some(Self::ATTR_EHMS), + _ => None, + } + } + pub(crate) fn to_raw_options(self) -> RawNeoOptions { + match self { + DateAndTimeFieldSet::D { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::MD { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::YMD { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::DE { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::MDE { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::YMDE { date, time } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options + }, + DateAndTimeFieldSet::E { date, time, alignment } => { + let mut options = date.to_raw_options(); + time.set_on_raw_options(&mut options); + options.alignment = alignment; + options + }, + } + } + pub(crate) fn to_date_field_set(self) -> DateFieldSet { + match self { + DateAndTimeFieldSet::D { date, .. } => DateFieldSet::D(date), + DateAndTimeFieldSet::MD { date, .. } => DateFieldSet::MD(date), + DateAndTimeFieldSet::YMD { date, .. } => DateFieldSet::YMD(date), + DateAndTimeFieldSet::DE { date, .. } => DateFieldSet::DE(date), + DateAndTimeFieldSet::MDE { date, .. } => DateFieldSet::MDE(date), + DateAndTimeFieldSet::YMDE { date, .. } => DateFieldSet::YMDE(date), + DateAndTimeFieldSet::E { date, .. } => DateFieldSet::E(date), + } + } + pub(crate) fn to_time_field_set(self) -> TimeFieldSet { + let (time, length, alignment) = match self { + DateAndTimeFieldSet::D { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::MD { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::YMD { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::DE { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::MDE { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::YMDE { date, time } => (time, date.length, date.alignment), + DateAndTimeFieldSet::E { date, time, alignment } => (time, date.length, alignment), + }; + time.to_time_field_set_with_length_and_alignment(length, alignment) + } +} diff --git a/components/datetime/src/fieldset.rs b/components/datetime/src/fieldset.rs index 46be9cd92f4..cf4fcb31bcf 100644 --- a/components/datetime/src/fieldset.rs +++ b/components/datetime/src/fieldset.rs @@ -10,7 +10,9 @@ use crate::{ format::neo::*, neo_skeleton::*, provider::{neo::*, time_zones::tz, *}, + raw::neo::RawNeoOptions, scaffold::*, + dynamic::*, }; use icu_calendar::{ types::{ @@ -34,6 +36,15 @@ macro_rules! yes_to { }; } +macro_rules! ternary { + ($present:expr, $missing:expr, yes) => { + $present + }; + ($present:expr, $missing:expr,) => { + $missing + }; +} + /// Generates the options argument passed into the docs test constructor macro_rules! length_option_helper { ($type:ty, $length:ident) => { @@ -51,7 +62,7 @@ macro_rules! impl_marker_with_options { $(fractional_second_digits: $fractionalsecondigits_yes:ident,)? ) => { $(#[$attr])* - #[derive(Debug)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] pub struct $type { $( @@ -107,6 +118,14 @@ macro_rules! impl_marker_with_options { pub const fn short() -> Self { Self::with_length(NeoSkeletonLength::Short) } + pub(crate) fn to_raw_options(self) -> RawNeoOptions { + RawNeoOptions { + length: self.length, + alignment: ternary!(self.alignment, None, $($alignment_yes)?), + year_style: ternary!(self.year_style, None, $($yearstyle_yes)?), + fractional_second_digits: ternary!(self.fractional_second_digits, None, $($fractionalsecondigits_yes)?), + } + } } impl_get_field!($type, never); impl_get_field!($type, length, yes); @@ -328,6 +347,12 @@ macro_rules! impl_date_marker { impl HasConstComponents for $type { const COMPONENTS: NeoComponents = NeoComponents::Date($components); } + impl GetField for $type { + #[inline] + fn get_field(&self) -> CompositeFieldSet { + CompositeFieldSet::Date(DateFieldSet::$type(*self)) + } + } }; } @@ -371,6 +396,12 @@ macro_rules! impl_calendar_period_marker { impl HasConstComponents for $type { const COMPONENTS: NeoComponents = NeoComponents::CalendarPeriod($components); } + impl GetField for $type { + #[inline] + fn get_field(&self) -> CompositeFieldSet { + CompositeFieldSet::CalendarPeriod(CalendarPeriodFieldSet::$type(*self)) + } + } }; } @@ -498,6 +529,12 @@ macro_rules! impl_time_marker { impl HasConstComponents for $type { const COMPONENTS: NeoComponents = NeoComponents::Time($components); } + impl GetField for $type { + #[inline] + fn get_field(&self) -> CompositeFieldSet { + CompositeFieldSet::Time(TimeFieldSet::$type(*self)) + } + } }; } diff --git a/components/datetime/src/lib.rs b/components/datetime/src/lib.rs index 1d0a4ae5b35..1a41809b701 100644 --- a/components/datetime/src/lib.rs +++ b/components/datetime/src/lib.rs @@ -88,6 +88,7 @@ extern crate alloc; mod combo; +mod dynamic; mod error; mod external_loaders; pub mod fields; diff --git a/components/datetime/src/neo.rs b/components/datetime/src/neo.rs index c914fb33ef2..c906a10e5dd 100644 --- a/components/datetime/src/neo.rs +++ b/components/datetime/src/neo.rs @@ -4,6 +4,7 @@ //! High-level entrypoints for Neo DateTime Formatter +use crate::dynamic::CompositeFieldSet; use crate::external_loaders::*; use crate::format::datetime::try_write_pattern_items; use crate::format::datetime::DateTimeWriteError; @@ -35,7 +36,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { pub fn $any_fn

( provider: &P, locale: &DataLocale, - skeleton: $fset, + field_set: $fset, ) -> Result where P: AnyProvider + ?Sized, @@ -44,8 +45,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { &provider.as_downcasting(), &ExternalLoaderAny(provider), locale, - RawNeoOptions::from_field_set_and_locale(&skeleton, locale), - skeleton.get_field(), + field_set.get_field(), ) } #[doc = icu_provider::gen_any_buffer_unstable_docs!(BUFFER, Self::$compiled_fn)] @@ -53,7 +53,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { pub fn $buffer_fn

( provider: &P, locale: &DataLocale, - skeleton: $fset, + field_set: $fset, ) -> Result where P: BufferProvider + ?Sized, @@ -62,8 +62,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { &provider.as_deserializing(), &ExternalLoaderBuffer(provider), locale, - RawNeoOptions::from_field_set_and_locale(&skeleton, locale), - skeleton.get_field(), + field_set.get_field(), ) } }; @@ -72,7 +71,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { pub fn $any_fn

( provider: &P, locale: &DataLocale, - options: $fset, + field_set: $fset, ) -> Result where P: AnyProvider + ?Sized, @@ -81,8 +80,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { &provider.as_downcasting(), &ExternalLoaderAny(provider), locale, - RawNeoOptions::from_field_set_and_locale(&options, locale), - $fset::COMPONENTS, + field_set.get_field(), ) } #[doc = icu_provider::gen_any_buffer_unstable_docs!(BUFFER, Self::$compiled_fn)] @@ -90,7 +88,7 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { pub fn $buffer_fn

( provider: &P, locale: &DataLocale, - options: $fset, + field_set: $fset, ) -> Result where P: BufferProvider + ?Sized, @@ -99,45 +97,44 @@ macro_rules! gen_any_buffer_constructors_with_external_loader { &provider.as_deserializing(), &ExternalLoaderBuffer(provider), locale, - RawNeoOptions::from_field_set_and_locale(&options, locale), - $fset::COMPONENTS, + field_set.get_field(), ) } }; } -impl RawNeoOptions { - pub(crate) fn from_field_set_and_locale(field_set: &FSet, locale: &DataLocale) -> Self - where - FSet: DateTimeMarkers, - FSet: GetField, - FSet: GetField, - FSet: GetField, - FSet: GetField, - { - // TODO: Return an error if there are more options than field set - let hour_cycle = locale - .get_unicode_ext(&icu_locale_core::extensions::unicode::key!("hc")) - .as_ref() - .and_then(HourCycle::from_locale_value); - Self { - length: match GetField::::get_field(field_set).into_option() { - Some(length) => length, - None => { - debug_assert!(false, "unreachable"); - NeoSkeletonLength::Medium - } - }, - alignment: GetField::::get_field(field_set).into_option(), - year_style: GetField::::get_field(field_set).into_option(), - fractional_second_digits: GetField::::get_field( - field_set, - ) - .into_option(), - hour_cycle, - } - } -} +// impl RawNeoOptions { +// pub(crate) fn from_field_set_and_locale(field_set: &FSet, locale: &DataLocale) -> Self +// where +// FSet: DateTimeMarkers, +// FSet: GetField, +// FSet: GetField, +// FSet: GetField, +// FSet: GetField, +// { +// // TODO: Return an error if there are more options than field set +// let hour_cycle = locale +// .get_unicode_ext(&icu_locale_core::extensions::unicode::key!("hc")) +// .as_ref() +// .and_then(HourCycle::from_locale_value); +// Self { +// length: match GetField::::get_field(field_set).into_option() { +// Some(length) => length, +// None => { +// debug_assert!(false, "unreachable"); +// NeoSkeletonLength::Medium +// } +// }, +// alignment: GetField::::get_field(field_set).into_option(), +// year_style: GetField::::get_field(field_set).into_option(), +// fractional_second_digits: GetField::::get_field( +// field_set, +// ) +// .into_option(), +// hour_cycle, +// } +// } +// } size_test!(FixedCalendarDateTimeFormatter, typed_neo_year_month_day_formatter_size, 456); @@ -154,12 +151,13 @@ pub struct FixedCalendarDateTimeFormatter, } -impl +impl FixedCalendarDateTimeFormatter where FSet::D: TypedDateDataMarkers, FSet::T: TimeMarkers, FSet::Z: ZoneMarkers, + FSet: GetField, FSet: GetField, FSet: GetField, FSet: GetField, @@ -205,8 +203,7 @@ where &crate::provider::Baked, &ExternalLoaderCompiledData, locale, - RawNeoOptions::from_field_set_and_locale(&field_set, locale), - FSet::COMPONENTS, + field_set.get_field(), ) } @@ -234,18 +231,19 @@ where provider, &ExternalLoaderUnstable(provider), locale, - RawNeoOptions::from_field_set_and_locale(&field_set, locale), - FSet::COMPONENTS, + field_set.get_field(), ) } } -impl +/* +impl FixedCalendarDateTimeFormatter where FSet::D: TypedDateDataMarkers, FSet::T: TimeMarkers, FSet::Z: ZoneMarkers, + FSet: GetField, FSet: GetField, FSet: GetField, FSet: GetField, @@ -397,6 +395,7 @@ where ) } } +*/ impl FixedCalendarDateTimeFormatter where @@ -408,16 +407,15 @@ where provider: &P, loader: &L, locale: &DataLocale, - options: RawNeoOptions, - components: NeoComponents, + field_set: CompositeFieldSet, ) -> Result where P: ?Sized + AllFixedCalendarFormattingDataMarkers, L: FixedDecimalFormatterLoader, { // TODO: Remove this when NeoOptions is gone - let mut options = options; - options.hour_cycle = locale + let mut prefs = RawPreferences::default(); + prefs.hour_cycle = locale .get_unicode_ext(&icu_locale_core::extensions::unicode::key!("hc")) .as_ref() .and_then(HourCycle::from_locale_value); @@ -428,8 +426,8 @@ where &::TimeSkeletonPatternsV1Marker::bind(provider), &FSet::GluePatternV1Marker::bind(provider), locale, - components, - options, + field_set, + prefs, ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_number_formatting(); @@ -539,11 +537,12 @@ pub struct DateTimeFormatter { calendar: AnyCalendar, } -impl DateTimeFormatter +impl DateTimeFormatter where FSet::D: DateDataMarkers, FSet::T: TimeMarkers, FSet::Z: ZoneMarkers, + FSet: GetField, FSet: GetField, FSet: GetField, FSet: GetField, @@ -600,8 +599,7 @@ where &crate::provider::Baked, &ExternalLoaderCompiledData, locale, - RawNeoOptions::from_field_set_and_locale(&field_set, locale), - FSet::COMPONENTS, + field_set.get_field() ) } @@ -627,12 +625,12 @@ where provider, &ExternalLoaderUnstable(provider), locale, - RawNeoOptions::from_field_set_and_locale(&field_set, locale), - FSet::COMPONENTS, + field_set.get_field(), ) } } +/* impl DateTimeFormatter where FSet::D: DateDataMarkers, @@ -784,6 +782,7 @@ where ) } } +*/ impl DateTimeFormatter where @@ -795,16 +794,15 @@ where provider: &P, loader: &L, locale: &DataLocale, - options: RawNeoOptions, - components: NeoComponents, + field_set: CompositeFieldSet, ) -> Result where P: ?Sized + AllAnyCalendarFormattingDataMarkers, L: FixedDecimalFormatterLoader + AnyCalendarLoader, { // TODO: Remove this when NeoOptions is gone - let mut options = options; - options.hour_cycle = locale + let mut prefs = RawPreferences::default(); + prefs.hour_cycle = locale .get_unicode_ext(&icu_locale_core::extensions::unicode::key!("hc")) .as_ref() .and_then(HourCycle::from_locale_value); @@ -817,8 +815,8 @@ where &::TimeSkeletonPatternsV1Marker::bind(provider), &FSet::GluePatternV1Marker::bind(provider), locale, - components, - options, + field_set, + prefs, ) .map_err(LoadError::Data)?; let mut names = RawDateTimeNames::new_without_number_formatting(); diff --git a/components/datetime/src/raw/neo.rs b/components/datetime/src/raw/neo.rs index d08b4591b48..b4ebf9a77e2 100644 --- a/components/datetime/src/raw/neo.rs +++ b/components/datetime/src/raw/neo.rs @@ -2,6 +2,7 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +use crate::dynamic::{CompositeFieldSet, TimeFieldSet}; use crate::fields::{self, FieldLength, FieldSymbol}; use crate::format::neo::FieldForDataLoading; use crate::input::ExtractedInput; @@ -28,6 +29,10 @@ pub(crate) struct RawNeoOptions { pub(crate) alignment: Option, pub(crate) year_style: Option, pub(crate) fractional_second_digits: Option, +} + +#[derive(Debug, Copy, Clone, Default)] +pub(crate) struct RawPreferences { pub(crate) hour_cycle: Option, } @@ -53,6 +58,7 @@ pub(crate) enum DatePatternDataBorrowed<'a> { pub(crate) enum OverlapPatternSelectionData { SkeletonDateTime { options: RawNeoOptions, + prefs: RawPreferences, payload: DataPayload, }, } @@ -61,6 +67,7 @@ pub(crate) enum OverlapPatternSelectionData { pub(crate) enum TimePatternSelectionData { SkeletonTime { options: RawNeoOptions, + prefs: RawPreferences, payload: DataPayload, }, } @@ -299,6 +306,7 @@ impl OverlapPatternSelectionData { locale: &DataLocale, attributes: &DataMarkerAttributes, options: RawNeoOptions, + prefs: RawPreferences, ) -> Result { let payload = provider .load_bound(DataRequest { @@ -306,7 +314,7 @@ impl OverlapPatternSelectionData { ..Default::default() })? .payload; - Ok(Self::SkeletonDateTime { options, payload }) + Ok(Self::SkeletonDateTime { options, prefs, payload }) } /// Borrows a pattern containing all of the fields that need to be loaded. @@ -332,13 +340,13 @@ impl OverlapPatternSelectionData { /// Borrows a resolved pattern based on the given datetime pub(crate) fn select(&self, input: &ExtractedInput) -> TimePatternDataBorrowed { match self { - OverlapPatternSelectionData::SkeletonDateTime { options, payload } => { + OverlapPatternSelectionData::SkeletonDateTime { options, prefs, payload } => { let year_style = options.year_style.unwrap_or(YearStyle::Auto); let variant = input.resolve_year_style(year_style); TimePatternDataBorrowed::Resolved( payload.get().get(options.length, variant), options.alignment, - options.hour_cycle, + prefs.hour_cycle, options.fractional_second_digits, ) } @@ -350,17 +358,18 @@ impl TimePatternSelectionData { pub(crate) fn try_new_with_skeleton( provider: &(impl BoundDataProvider + ?Sized), locale: &DataLocale, - components: NeoTimeComponents, + components: TimeFieldSet, options: RawNeoOptions, + prefs: RawPreferences, ) -> Result { // First try to load with the explicit hour cycle. If there is no explicit hour cycle, // or if loading the explicit hour cycle fails, then load with the default hour cycle. let mut maybe_payload = None; - if let Some(hour_cycle) = options.hour_cycle { + if let Some(hour_cycle) = prefs.hour_cycle { maybe_payload = provider .load_bound(DataRequest { id: DataIdentifierBorrowed::for_marker_attributes_and_locale( - components.with_hour_cycle(hour_cycle.into()).id_str(), + components.id_str_for_hour_cycle(Some(hour_cycle)), locale, ), ..Default::default() @@ -374,7 +383,7 @@ impl TimePatternSelectionData { provider .load_bound(DataRequest { id: DataIdentifierBorrowed::for_marker_attributes_and_locale( - components.id_str(), + components.id_str_for_hour_cycle(None), locale, ), ..Default::default() @@ -382,7 +391,7 @@ impl TimePatternSelectionData { .payload } }; - Ok(Self::SkeletonTime { options, payload }) + Ok(Self::SkeletonTime { options, prefs, payload }) } /// Borrows a pattern containing all of the fields that need to be loaded. @@ -408,13 +417,13 @@ impl TimePatternSelectionData { /// Borrows a resolved pattern based on the given datetime pub(crate) fn select(&self, _input: &ExtractedInput) -> TimePatternDataBorrowed { match self { - TimePatternSelectionData::SkeletonTime { options, payload } => { + TimePatternSelectionData::SkeletonTime { options, prefs, payload } => { TimePatternDataBorrowed::Resolved( payload .get() .get(options.length, PackedSkeletonVariant::Standard), options.alignment, - options.hour_cycle, + prefs.hour_cycle, options.fractional_second_digits, ) } @@ -436,7 +445,7 @@ impl<'a> TimePatternDataBorrowed<'a> { impl ZonePatternSelectionData { pub(crate) fn new_with_skeleton( - components: NeoTimeZoneStyle, + style: NeoTimeZoneStyle, options: RawNeoOptions, is_only_field: bool, ) -> Self { @@ -445,7 +454,7 @@ impl ZonePatternSelectionData { } else { NeoSkeletonLength::Short }; - let time_zone = components.resolve(length); + let time_zone = style.resolve(length); let pattern_item = PatternItem::Field(time_zone.to_field()); Self::SinglePatternItem(time_zone, pattern_item.to_unaligned()) } @@ -483,45 +492,51 @@ impl DateTimeZonePatternSelectionData { time_provider: &(impl BoundDataProvider + ?Sized), glue_provider: &(impl BoundDataProvider + ?Sized), locale: &DataLocale, - components: NeoComponents, - options: RawNeoOptions, + skeleton: CompositeFieldSet, + prefs: RawPreferences, ) -> Result { - match components { - NeoComponents::Date(components) => { + match skeleton { + CompositeFieldSet::Date(field_set) => { + let options = field_set.to_raw_options(); let selection = DatePatternSelectionData::try_new_with_skeleton( date_provider, locale, - components.id_str(), + field_set.id_str(), options, )?; Ok(Self::Date(selection)) } - NeoComponents::CalendarPeriod(components) => { + CompositeFieldSet::CalendarPeriod(field_set) => { + let options = field_set.to_raw_options(); let selection = DatePatternSelectionData::try_new_with_skeleton( date_provider, locale, - components.id_str(), + field_set.id_str(), options, )?; Ok(Self::Date(selection)) } - NeoComponents::Time(components) => { + CompositeFieldSet::Time(field_set) => { + let options = field_set.to_raw_options(); let selection = TimePatternSelectionData::try_new_with_skeleton( time_provider, locale, - components, + field_set, options, + prefs, )?; Ok(Self::Time(selection)) } - NeoComponents::Zone(components) => { + CompositeFieldSet::Zone(field_set) => { + let options = field_set.to_raw_options(); let selection = - ZonePatternSelectionData::new_with_skeleton(components, options, true); + ZonePatternSelectionData::new_with_skeleton(field_set.style, options, true); Ok(Self::Zone(selection)) } - NeoComponents::DateTime(date_components, time_components) => { + CompositeFieldSet::DateTime(field_set) => { + let options = field_set.to_raw_options(); // TODO(#5387): load the patterns for custom hour cycles here - if let (Some(attributes), None) = (components.id_str(), options.hour_cycle) { + if let (Some(attributes), None) = (field_set.id_str(), prefs.hour_cycle) { // Try loading an overlap pattern. if let Some(overlap) = OverlapPatternSelectionData::try_new_with_skeleton( // Note: overlap patterns are stored in the date provider @@ -529,6 +544,7 @@ impl DateTimeZonePatternSelectionData { locale, attributes, options, + prefs, ) .allow_identifier_not_found()? { @@ -538,57 +554,63 @@ impl DateTimeZonePatternSelectionData { let date = DatePatternSelectionData::try_new_with_skeleton( date_provider, locale, - date_components.id_str(), + field_set.to_date_field_set().id_str(), options, )?; let time = TimePatternSelectionData::try_new_with_skeleton( time_provider, locale, - time_components, + field_set.to_time_field_set(), options, + prefs, )?; let glue = Self::load_glue(glue_provider, locale, options, GlueType::DateTime)?; Ok(Self::DateTimeGlue { date, time, glue }) } - NeoComponents::DateZone(date_components, zone_components) => { + CompositeFieldSet::DateZone(field_set, time_zone_style) => { + let options = field_set.to_raw_options(); let date = DatePatternSelectionData::try_new_with_skeleton( date_provider, locale, - date_components.id_str(), + field_set.id_str(), options, )?; let zone = - ZonePatternSelectionData::new_with_skeleton(zone_components, options, false); + ZonePatternSelectionData::new_with_skeleton(time_zone_style, options, false); let glue = Self::load_glue(glue_provider, locale, options, GlueType::DateZone)?; Ok(Self::DateZoneGlue { date, zone, glue }) } - NeoComponents::TimeZone(time_components, zone_components) => { + CompositeFieldSet::TimeZone(field_set, time_zone_style) => { + let options = field_set.to_raw_options(); let time = TimePatternSelectionData::try_new_with_skeleton( time_provider, locale, - time_components, + field_set, options, + prefs, )?; let zone = - ZonePatternSelectionData::new_with_skeleton(zone_components, options, false); + ZonePatternSelectionData::new_with_skeleton(time_zone_style, options, false); let glue = Self::load_glue(glue_provider, locale, options, GlueType::TimeZone)?; Ok(Self::TimeZoneGlue { time, zone, glue }) } - NeoComponents::DateTimeZone(date_components, time_components, zone_components) => { + CompositeFieldSet::DateTimeZone(field_set, time_zone_style) => { + let options = field_set.to_raw_options(); let date = DatePatternSelectionData::try_new_with_skeleton( date_provider, locale, - date_components.id_str(), + field_set.to_date_field_set().id_str(), options, )?; let time = TimePatternSelectionData::try_new_with_skeleton( time_provider, locale, - time_components, + field_set.to_time_field_set(), options, + prefs, )?; let zone = - ZonePatternSelectionData::new_with_skeleton(zone_components, options, false); + ZonePatternSelectionData::new_with_skeleton(time_zone_style, options, false); let glue = Self::load_glue(glue_provider, locale, options, GlueType::DateTimeZone)?; Ok(Self::DateTimeZoneGlue { date, diff --git a/components/datetime/src/scaffold/dynamic_impls.rs b/components/datetime/src/scaffold/dynamic_impls.rs new file mode 100644 index 00000000000..fe5a8afd134 --- /dev/null +++ b/components/datetime/src/scaffold/dynamic_impls.rs @@ -0,0 +1,83 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use super::*; +use crate::{dynamic::*, format::neo::DateTimeNamesMarker, neo_skeleton::NeoComponents}; +use crate::{ + format::neo::*, + neo_skeleton::*, + provider::{neo::*, time_zones::tz, *}, + raw::neo::RawNeoOptions, + scaffold::*, +}; +use icu_calendar::{ + types::{ + DayOfMonth, IsoHour, IsoMinute, IsoSecond, IsoWeekday, MonthInfo, NanoSecond, YearInfo, + }, + AnyCalendarKind, Date, Iso, Time, +}; +use icu_provider::marker::NeverMarker; +use icu_timezone::{TimeZoneBcp47Id, UtcOffset, ZoneVariant}; + +// impl GetField for DateFieldSet { +// fn get_field(&self) -> NeoComponents { +// self.components.into() +// } +// } + +impl UnstableSealed for DateFieldSet {} + +// impl IsRuntimeComponents for DateFieldSet {} + +impl DateTimeNamesMarker for DateFieldSet { + type YearNames = datetime_marker_helper!(@names/year, yes); + type MonthNames = datetime_marker_helper!(@names/month, yes); + type WeekdayNames = datetime_marker_helper!(@names/weekday, yes); + type DayPeriodNames = datetime_marker_helper!(@names/dayperiod,); + type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,); + type ZoneLocations = datetime_marker_helper!(@names/zone/locations,); + type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,); + type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,); + type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,); + type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,); + type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,); +} + +impl DateInputMarkers for DateFieldSet { + type YearInput = datetime_marker_helper!(@input/year, yes); + type MonthInput = datetime_marker_helper!(@input/month, yes); + type DayOfMonthInput = datetime_marker_helper!(@input/day_of_month, yes); + type DayOfWeekInput = datetime_marker_helper!(@input/day_of_week, yes); + type AnyCalendarKindInput = datetime_marker_helper!(@input/any_calendar_kind, yes); +} + +impl TypedDateDataMarkers for DateFieldSet { + type DateSkeletonPatternsV1Marker = datetime_marker_helper!(@dates/typed, yes); + type YearNamesV1Marker = datetime_marker_helper!(@years/typed, yes); + type MonthNamesV1Marker = datetime_marker_helper!(@months/typed, yes); + type WeekdayNamesV1Marker = datetime_marker_helper!(@weekdays, yes); +} + +impl DateDataMarkers for DateFieldSet { + type Skel = datetime_marker_helper!(@calmarkers, yes); + type Year = datetime_marker_helper!(@calmarkers, yes); + type Month = datetime_marker_helper!(@calmarkers, yes); + type WeekdayNamesV1Marker = datetime_marker_helper!(@weekdays, yes); +} + +impl DateTimeMarkers for DateFieldSet { + type D = Self; + type T = NeoNeverMarker; + type Z = NeoNeverMarker; + type LengthOption = datetime_marker_helper!(@option/length, yes); + type AlignmentOption = datetime_marker_helper!(@option/alignment, yes); + type YearStyleOption = datetime_marker_helper!(@option/yearstyle, yes); + type FractionalSecondDigitsOption = datetime_marker_helper!(@option/fractionalsecondigits,); + type GluePatternV1Marker = datetime_marker_helper!(@glue,); +} + +// impl_get_field!(DateFieldSet, never); +// impl_get_field!(DateFieldSet, length, yes); +// impl_get_field!(DateFieldSet, alignment, yes); +// impl_get_field!(DateFieldSet, year_style, yes); diff --git a/components/datetime/src/scaffold/mod.rs b/components/datetime/src/scaffold/mod.rs index bd4ba24aa8a..6ac9180697d 100644 --- a/components/datetime/src/scaffold/mod.rs +++ b/components/datetime/src/scaffold/mod.rs @@ -8,6 +8,7 @@ //! these items in userland code. mod calendar; +mod dynamic_impls; mod fieldset_traits; mod get_field;