Skip to content

Commit

Permalink
Add with_fset and DateTimeNamesFrom
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc committed Dec 20, 2024
1 parent 736c567 commit e9419be
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 1 deletion.
96 changes: 96 additions & 0 deletions components/datetime/src/neo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,55 @@ impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, F
calendar: calendar.to_any(),
}
}

/// Maps a [`FixedCalendarDateTimeFormatter`] of a specific `FSet` to a more general `FSet`.
///
/// For example, this can transform a formatter for [`YMD`] to one for [`DateFieldSet`].
///
/// [`YMD`]: crate::fieldsets::YMD
/// [`DateFieldSet`]: crate::fieldsets::enums::DateFieldSet
///
/// # Examples
///
/// ```
/// use icu::calendar::Gregorian;
/// use icu::calendar::DateTime;
/// use icu::datetime::FixedCalendarDateTimeFormatter;
/// use icu::datetime::fieldsets::{YMD, enums::DateFieldSet};
/// use icu::locale::locale;
/// use writeable::assert_writeable_eq;
///
/// let specific_formatter = FixedCalendarDateTimeFormatter::try_new(
/// locale!("fr").into(),
/// YMD::medium(),
/// )
/// .unwrap();
///
/// // Test that the specific formatter works:
/// let datetime = DateTime::try_new_gregorian(2024, 12, 20, 14, 30, 0).unwrap();
/// assert_writeable_eq!(
/// specific_formatter.format(&datetime),
/// "20 déc. 2024"
/// );
///
/// // Make a more general formatter:
/// let general_formatter = specific_formatter.with_fset::<DateFieldSet>();
///
/// // Test that it still works:
/// assert_writeable_eq!(
/// general_formatter.format(&datetime),
/// "20 déc. 2024"
/// );
/// ```
pub fn with_fset<FSet2: DateTimeNamesFrom<FSet>>(
self,
) -> FixedCalendarDateTimeFormatter<C, FSet2> {
FixedCalendarDateTimeFormatter {
selection: self.selection,
names: self.names.with_fset(),
_calendar: PhantomData,
}
}
}

impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet> {
Expand Down Expand Up @@ -772,6 +821,53 @@ impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet> {
})
}

/// Maps a [`DateTimeFormatter`] of a specific `FSet` to a more general `FSet`.
///
/// For example, this can transform a formatter for [`YMD`] to one for [`DateFieldSet`].
///
/// [`YMD`]: crate::fieldsets::YMD
/// [`DateFieldSet`]: crate::fieldsets::enums::DateFieldSet
///
/// # Examples
///
/// ```
/// use icu::calendar::Gregorian;
/// use icu::calendar::DateTime;
/// use icu::datetime::DateTimeFormatter;
/// use icu::datetime::fieldsets::{YMD, enums::DateFieldSet};
/// use icu::locale::locale;
/// use writeable::assert_writeable_eq;
///
/// let specific_formatter = DateTimeFormatter::try_new(
/// locale!("fr").into(),
/// YMD::medium(),
/// )
/// .unwrap();
///
/// // Test that the specific formatter works:
/// let datetime = DateTime::try_new_gregorian(2024, 12, 20, 14, 30, 0).unwrap();
/// assert_writeable_eq!(
/// specific_formatter.format_any_calendar(&datetime),
/// "20 déc. 2024"
/// );
///
/// // Make a more general formatter:
/// let general_formatter = specific_formatter.with_fset::<DateFieldSet>();
///
/// // Test that it still works:
/// assert_writeable_eq!(
/// general_formatter.format_any_calendar(&datetime),
/// "20 déc. 2024"
/// );
/// ```
pub fn with_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> DateTimeFormatter<FSet2> {
DateTimeFormatter {
selection: self.selection,
names: self.names.with_fset(),
calendar: self.calendar,
}
}

/// Returns the calendar system used in this formatter.
///
/// # Examples
Expand Down
82 changes: 82 additions & 0 deletions components/datetime/src/pattern/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,27 @@ impl<FSet: DateTimeNamesMarker> fmt::Debug for RawDateTimeNames<FSet> {
}
}

impl<FSet: DateTimeNamesMarker> RawDateTimeNames<FSet> {
pub(crate) fn with_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> RawDateTimeNames<FSet2> {
RawDateTimeNames {
year_names: FSet2::map_year_names(self.year_names),
month_names: FSet2::map_month_names(self.month_names),
weekday_names: FSet2::map_weekday_names(self.weekday_names),
dayperiod_names: FSet2::map_day_period_names(self.dayperiod_names),
zone_essentials: FSet2::map_zone_essentials(self.zone_essentials),
locations_root: FSet2::map_zone_locations(self.locations_root),
locations: FSet2::map_zone_locations(self.locations),
mz_generic_long: FSet2::map_zone_generic_long(self.mz_generic_long),
mz_generic_short: FSet2::map_zone_generic_short(self.mz_generic_short),
mz_specific_long: FSet2::map_zone_specific_long(self.mz_specific_long),
mz_specific_short: FSet2::map_zone_specific_short(self.mz_specific_short),
mz_periods: FSet2::map_metazone_lookup(self.mz_periods),
fixed_decimal_formatter: self.fixed_decimal_formatter,
_marker: PhantomData,
}
}
}

#[derive(Debug, Copy, Clone)]
pub(crate) struct RawDateTimeNamesBorrowed<'l> {
year_names: OptionalNames<FieldLength, &'l YearNamesV1<'l>>,
Expand Down Expand Up @@ -1299,6 +1320,67 @@ impl<C: CldrCalendar, FSet: DateTimeNamesMarker> TypedDateTimeNames<C, FSet> {
self.inner.as_borrowed(),
))
}

/// Maps a [`TypedDateTimeNames`] of a specific `FSet` to a more general `FSet`.
///
/// For example, this can transform a formatter for [`DateFieldSet`] to one for
/// [`CompositeDateTimeFieldSet`].
///
/// [`DateFieldSet`]: crate::fieldsets::enums::DateFieldSet
/// [`CompositeDateTimeFieldSet`]: crate::fieldsets::enums::CompositeDateTimeFieldSet
///
/// # Examples
///
/// ```
/// use icu::calendar::Gregorian;
/// use icu::calendar::DateTime;
/// use icu::datetime::pattern::TypedDateTimeNames;
/// use icu::datetime::fields::FieldLength;
/// use icu::datetime::fields;
/// use icu::datetime::fieldsets::enums::{DateFieldSet, CompositeDateTimeFieldSet};
/// use icu::datetime::pattern::DateTimePattern;
/// use icu::locale::locale;
/// use writeable::assert_try_writeable_eq;
///
/// // Create an instance that can format abbreviated month names:
/// let mut names: TypedDateTimeNames<Gregorian, DateFieldSet> =
/// TypedDateTimeNames::try_new(locale!("uk").into()).unwrap();
/// names
/// .include_month_names(fields::Month::Format, FieldLength::Three)
/// .unwrap();
///
/// // Test it with a pattern:
/// let pattern_str = "MMM d y";
/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
/// let datetime = DateTime::try_new_gregorian(2023, 11, 20, 12, 35, 3).unwrap();
/// assert_try_writeable_eq!(names.with_pattern_unchecked(&pattern).format(&datetime), "лист. 20 2023");
///
/// // Convert the field set to `CompositeDateTimeFieldSet`:
/// let composite_names = names.with_fset::<CompositeDateTimeFieldSet>();
///
/// // It should still work:
/// assert_try_writeable_eq!(composite_names.with_pattern_unchecked(&pattern).format(&datetime), "лист. 20 2023");
/// ```
///
/// Converting into a narrower type is not supported:
///
/// ```compile_fail,E0277
/// use icu::calendar::Gregorian;
/// use icu::datetime::pattern::TypedDateTimeNames;
/// use icu::datetime::fieldsets::enums::{DateFieldSet, CompositeDateTimeFieldSet};
///
/// let composite_names: TypedDateTimeNames<Gregorian, CompositeDateTimeFieldSet> = todo!();
///
/// // error[E0277]: the trait bound `(): From<DataPayloadWithVariables<DayPeriodNamesV1Marker, FieldLength>>` is not satisfied
/// let narrow_names = composite_names.with_fset::<DateFieldSet>();
/// ```
pub fn with_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> TypedDateTimeNames<C, FSet2> {
TypedDateTimeNames {
prefs: self.prefs,
inner: self.inner.with_fset(),
_calendar: PhantomData,
}
}
}

impl<FSet: DateTimeNamesMarker> RawDateTimeNames<FSet> {
Expand Down
1 change: 1 addition & 0 deletions components/datetime/src/scaffold/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub use get_field::GetField;

pub use names_storage::DataPayloadWithVariables;
pub use names_storage::DataPayloadWithVariablesBorrowed;
pub use names_storage::DateTimeNamesFrom;
pub use names_storage::DateTimeNamesMarker;
pub use names_storage::MaybePayload;
pub use names_storage::MaybePayloadError;
Expand Down
147 changes: 146 additions & 1 deletion components/datetime/src/scaffold/names_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub trait MaybePayload<M: DynamicDataMarker, Variables>: UnstableSealed {
fn get(&self) -> DataPayloadWithVariablesBorrowed<M, Variables>;
}

/// An implementation of [`MaybePayload`] that wraps a [`DataPayload`],
/// An implementation of [`MaybePayload`] that wraps an optional [`DataPayload`],
/// parameterized by `Variables`.
pub struct DataPayloadWithVariables<M: DynamicDataMarker, Variables> {
inner: OptionalNames<Variables, DataPayload<M>>,
Expand All @@ -127,6 +127,15 @@ where
}
}

impl<M: DynamicDataMarker, Variables> From<()> for DataPayloadWithVariables<M, Variables> {
#[inline]
fn from(_: ()) -> Self {
Self {
inner: OptionalNames::None,
}
}
}

/// Borrowed version of [`DataPayloadWithVariables`].
#[allow(missing_docs)]
pub struct DataPayloadWithVariablesBorrowed<'data, M: DynamicDataMarker, Variables> {
Expand Down Expand Up @@ -281,3 +290,139 @@ where
}
}
}

/// A trait for a [`DateTimeNamesMarker`] that can be created from a more specific one, `M`.
///
/// This trait is blanket-implemented on all field sets that are more general than `M`.
///
/// # Examples
///
/// Example pairs of field sets where the trait is implemented:
///
/// ```
/// use icu::datetime::fieldsets::T;
/// use icu::datetime::fieldsets::YMD;
/// use icu::datetime::fieldsets::enums::DateFieldSet;
/// use icu::datetime::fieldsets::enums::TimeFieldSet;
/// use icu::datetime::fieldsets::enums::CompositeDateTimeFieldSet;
/// use icu::datetime::fieldsets::enums::CompositeFieldSet;
/// use icu::datetime::scaffold::DateTimeNamesFrom;
/// use icu::datetime::scaffold::DateTimeNamesMarker;
///
/// fn is_trait_implemented<Source, Target>()
/// where
/// Source: DateTimeNamesMarker,
/// Target: DateTimeNamesFrom<Source>
/// {}
///
/// is_trait_implemented::<YMD, DateFieldSet>();
/// is_trait_implemented::<YMD, CompositeDateTimeFieldSet>();
/// is_trait_implemented::<YMD, CompositeFieldSet>();
/// is_trait_implemented::<T, TimeFieldSet>();
/// is_trait_implemented::<T, CompositeDateTimeFieldSet>();
/// is_trait_implemented::<T, CompositeFieldSet>();
/// is_trait_implemented::<DateFieldSet, CompositeDateTimeFieldSet>();
/// is_trait_implemented::<DateFieldSet, CompositeFieldSet>();
/// is_trait_implemented::<TimeFieldSet, CompositeDateTimeFieldSet>();
/// is_trait_implemented::<TimeFieldSet, CompositeFieldSet>();
/// ```
#[allow(missing_docs)]
pub trait DateTimeNamesFrom<M: DateTimeNamesMarker>: DateTimeNamesMarker {
fn map_year_names(
other: <M::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container,
) -> <Self::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container;
fn map_month_names(other: <M::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container) -> <Self::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container;
fn map_weekday_names(
other: <M::WeekdayNames as NamesContainer<
WeekdayNamesV1Marker,
(fields::Weekday, FieldLength),
>>::Container,
) -> <Self::WeekdayNames as NamesContainer<
WeekdayNamesV1Marker,
(fields::Weekday, FieldLength),
>>::Container;
fn map_day_period_names(
other: <M::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container,
) -> <Self::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container;
fn map_zone_essentials(
other: <M::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container,
) -> <Self::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container;
fn map_zone_locations(
other: <M::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container,
) -> <Self::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container;
fn map_zone_generic_long(
other: <M::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container,
) -> <Self::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container;
fn map_zone_generic_short(
other: <M::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container,
) -> <Self::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container;
fn map_zone_specific_long(
other: <M::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container,
) -> <Self::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container;
fn map_zone_specific_short(
other: <M::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container,
) -> <Self::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container;
fn map_metazone_lookup(
other: <M::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container,
) -> <Self::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container;
}

impl<M: DateTimeNamesMarker, T: DateTimeNamesMarker> DateTimeNamesFrom<M> for T
where
<Self::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container: From<<M::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container>,
<Self::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container: From<<M::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container>,
<Self::WeekdayNames as NamesContainer<WeekdayNamesV1Marker, (fields::Weekday, FieldLength)>>::Container: From<<M::WeekdayNames as NamesContainer<WeekdayNamesV1Marker, (fields::Weekday, FieldLength)>>::Container>,
<Self::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container: From<<M::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container>,
<Self::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container: From<<M::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container>,
<Self::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container: From<<M::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container>,
<Self::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container: From<<M::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container>,
<Self::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container: From<<M::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container>,
<Self::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container: From<<M::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container>,
<Self::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container: From<<M::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container>,
<Self::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container: From<<M::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container>,
{
#[inline]
fn map_year_names(other: <M::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container) -> <Self::YearNames as NamesContainer<YearNamesV1Marker, FieldLength>>::Container {
other.into()
}
#[inline]
fn map_month_names(other: <M::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container) -> <Self::MonthNames as NamesContainer<MonthNamesV1Marker, (fields::Month, FieldLength)>>::Container {
other.into()
}
#[inline]
fn map_weekday_names(other: <M::WeekdayNames as NamesContainer<WeekdayNamesV1Marker, (fields::Weekday, FieldLength)>>::Container) -> <Self::WeekdayNames as NamesContainer<WeekdayNamesV1Marker, (fields::Weekday, FieldLength)>>::Container {
other.into()
}
#[inline]
fn map_day_period_names(other: <M::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container) -> <Self::DayPeriodNames as NamesContainer<DayPeriodNamesV1Marker, FieldLength>>::Container {
other.into()
}
#[inline]
fn map_zone_essentials(other: <M::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container) -> <Self::ZoneEssentials as NamesContainer<tz::EssentialsV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_zone_locations(other: <M::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container) -> <Self::ZoneLocations as NamesContainer<tz::LocationsV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_zone_generic_long(other: <M::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container) -> <Self::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_zone_generic_short(other: <M::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container) -> <Self::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_zone_specific_long(other: <M::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container) -> <Self::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_zone_specific_short(other: <M::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container) -> <Self::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1Marker, ()>>::Container {
other.into()
}
#[inline]
fn map_metazone_lookup(other: <M::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container) -> <Self::MetazoneLookup as NamesContainer<tz::MzPeriodV1Marker, ()>>::Container {
other.into()
}
}

0 comments on commit e9419be

Please sign in to comment.