use crate::error::DateTimeFormatterLoadError;
use crate::external_loaders::*;
use crate::fieldsets::enums::CompositeFieldSet;
use crate::format::datetime::try_write_pattern_items;
use crate::format::ExtractedInput;
use crate::pattern::*;
use crate::preferences::{CalendarAlgorithm, HourCycle, NumberingSystem};
use crate::raw::neo::*;
use crate::scaffold::*;
use crate::scaffold::{
AllInputMarkers, ConvertCalendar, DateDataMarkers, DateInputMarkers, DateTimeMarkers, GetField,
InFixedCalendar, InSameCalendar, TimeMarkers, TypedDateDataMarkers, ZoneMarkers,
};
use crate::size_test_macro::size_test;
use crate::MismatchedCalendarError;
use core::fmt;
use core::marker::PhantomData;
use icu_calendar::any_calendar::IntoAnyCalendar;
use icu_calendar::{AnyCalendar, AnyCalendarPreferences};
use icu_decimal::DecimalFormatterPreferences;
use icu_locale_core::preferences::{define_preferences, prefs_convert};
use icu_provider::prelude::*;
use writeable::{impl_display_with_writeable, Writeable};
define_preferences!(
[Copy]
DateTimeFormatterPreferences,
{
numbering_system: NumberingSystem,
hour_cycle: HourCycle,
calendar_algorithm: CalendarAlgorithm
}
);
prefs_convert!(DateTimeFormatterPreferences, DecimalFormatterPreferences, {
numbering_system
});
prefs_convert!(DateTimeFormatterPreferences, AnyCalendarPreferences, {
calendar_algorithm
});
macro_rules! gen_buffer_constructors_with_external_loader {
(@runtime_fset, $fset:ident, $compiled_fn:ident $buffer_fn:ident, $internal_fn:ident) => {
#[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::$compiled_fn)]
#[cfg(feature = "serde")]
pub fn $buffer_fn<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set: $fset,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: BufferProvider + ?Sized,
{
Self::$internal_fn(
&provider.as_deserializing(),
&ExternalLoaderBuffer(provider),
prefs,
field_set.get_field(),
)
}
};
(@compiletime_fset, $fset:ident, $compiled_fn:ident, $buffer_fn:ident, $internal_fn:ident) => {
#[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::$compiled_fn)]
#[cfg(feature = "serde")]
pub fn $buffer_fn<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set: $fset,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: BufferProvider + ?Sized,
{
Self::$internal_fn(
&provider.as_deserializing(),
&ExternalLoaderBuffer(provider),
prefs,
field_set.get_field(),
)
}
};
}
size_test!(FixedCalendarDateTimeFormatter<icu_calendar::Gregorian, crate::fieldsets::YMD>, typed_neo_year_month_day_formatter_size, 328);
#[doc = typed_neo_year_month_day_formatter_size!()]
#[derive(Debug)]
pub struct FixedCalendarDateTimeFormatter<C: CldrCalendar, FSet: DateTimeNamesMarker> {
selection: DateTimeZonePatternSelectionData,
pub(crate) names: RawDateTimeNames<FSet>,
_calendar: PhantomData<C>,
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: TypedDateDataMarkers<C>,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
FSet: GetField<CompositeFieldSet>,
{
#[cfg(feature = "compiled_data")]
pub fn try_new(
prefs: DateTimeFormatterPreferences,
field_set: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
crate::provider::Baked: AllFixedCalendarFormattingDataMarkers<C, FSet>,
{
Self::try_new_internal(
&crate::provider::Baked,
&ExternalLoaderCompiledData,
prefs,
field_set.get_field(),
)
}
gen_buffer_constructors_with_external_loader!(
@compiletime_fset,
FSet,
try_new,
try_new_with_buffer_provider,
try_new_internal
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized
+ AllFixedCalendarFormattingDataMarkers<C, FSet>
+ AllFixedCalendarExternalDataMarkers,
{
Self::try_new_internal(
provider,
&ExternalLoaderUnstable(provider),
prefs,
field_set.get_field(),
)
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: TypedDateDataMarkers<C>,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
fn try_new_internal<P, L>(
provider: &P,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set: CompositeFieldSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllFixedCalendarFormattingDataMarkers<C, FSet>,
L: DecimalFormatterLoader,
{
let names = RawDateTimeNames::new_without_number_formatting();
Self::try_new_internal_with_names(provider, provider, loader, prefs, field_set, names)
.map_err(|e| e.0)
}
#[allow(clippy::result_large_err)] pub(crate) fn try_new_internal_with_names<P0, P1, L>(
provider_p: &P0,
provider: &P1,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set: CompositeFieldSet,
mut names: RawDateTimeNames<FSet>,
) -> Result<Self, (DateTimeFormatterLoadError, RawDateTimeNames<FSet>)>
where
P0: ?Sized + AllFixedCalendarPatternDataMarkers<C, FSet>,
P1: ?Sized + AllFixedCalendarFormattingDataMarkers<C, FSet>,
L: DecimalFormatterLoader,
{
let selection = DateTimeZonePatternSelectionData::try_new_with_skeleton(
&<FSet::D as TypedDateDataMarkers<C>>::DateSkeletonPatternsV1::bind(provider_p),
&<FSet::T as TimeMarkers>::TimeSkeletonPatternsV1::bind(provider_p),
&FSet::GluePatternV1::bind(provider_p),
prefs,
field_set,
);
let selection = match selection {
Ok(selection) => selection,
Err(e) => return Err((DateTimeFormatterLoadError::Data(e), names)),
};
let result = names.load_for_pattern(
&<FSet::D as TypedDateDataMarkers<C>>::YearNamesV1::bind(provider),
&<FSet::D as TypedDateDataMarkers<C>>::MonthNamesV1::bind(provider),
&<FSet::D as TypedDateDataMarkers<C>>::WeekdayNamesV1::bind(provider),
&<FSet::T as TimeMarkers>::DayPeriodNamesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::EssentialsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::StandardLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::MetazonePeriodV1::bind(provider),
loader, prefs,
selection.pattern_items_for_data_loading(),
);
match result {
Ok(()) => (),
Err(e) => return Err((DateTimeFormatterLoadError::Names(e), names)),
};
Ok(Self {
selection,
names,
_calendar: PhantomData,
})
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: DateInputMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
pub fn format<I>(&self, input: &I) -> FormattedDateTime
where
I: ?Sized + InFixedCalendar<C> + AllInputMarkers<FSet>,
{
let input = ExtractedInput::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I>(input);
FormattedDateTime {
pattern: self.selection.select(&input),
input,
names: self.names.as_borrowed(),
}
}
}
size_test!(
DateTimeFormatter<crate::fieldsets::YMD>,
neo_year_month_day_formatter_size,
384
);
#[doc = neo_year_month_day_formatter_size!()]
#[derive(Debug)]
pub struct DateTimeFormatter<FSet: DateTimeNamesMarker> {
selection: DateTimeZonePatternSelectionData,
pub(crate) names: RawDateTimeNames<FSet>,
pub(crate) calendar: AnyCalendar,
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateDataMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
FSet: GetField<CompositeFieldSet>,
{
#[inline(never)]
#[cfg(feature = "compiled_data")]
pub fn try_new(
prefs: DateTimeFormatterPreferences,
field_set: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
crate::provider::Baked: AllAnyCalendarFormattingDataMarkers<FSet>,
{
Self::try_new_internal(
&crate::provider::Baked,
&ExternalLoaderCompiledData,
prefs,
field_set.get_field(),
)
}
gen_buffer_constructors_with_external_loader!(
@compiletime_fset,
FSet,
try_new,
try_new_with_buffer_provider,
try_new_internal
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet> + AllAnyCalendarExternalDataMarkers,
{
Self::try_new_internal(
provider,
&ExternalLoaderUnstable(provider),
prefs,
field_set.get_field(),
)
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateDataMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
fn try_new_internal<P, L>(
provider: &P,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set: CompositeFieldSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet>,
L: DecimalFormatterLoader + AnyCalendarLoader,
{
let calendar = AnyCalendarLoader::load(loader, (&prefs).into())
.map_err(DateTimeFormatterLoadError::Data)?;
let names = RawDateTimeNames::new_without_number_formatting();
Self::try_new_internal_with_calendar_and_names(
provider, provider, loader, prefs, field_set, calendar, names,
)
.map_err(|e| e.0)
}
#[allow(clippy::result_large_err)] pub(crate) fn try_new_internal_with_calendar_and_names<P0, P1, L>(
provider_p: &P0,
provider: &P1,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set: CompositeFieldSet,
calendar: AnyCalendar,
mut names: RawDateTimeNames<FSet>,
) -> Result<
Self,
(
DateTimeFormatterLoadError,
(AnyCalendar, RawDateTimeNames<FSet>),
),
>
where
P0: ?Sized + AllAnyCalendarPatternDataMarkers<FSet>,
P1: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet>,
L: DecimalFormatterLoader,
{
let kind = calendar.kind();
let selection = DateTimeZonePatternSelectionData::try_new_with_skeleton(
&AnyCalendarProvider::<<FSet::D as DateDataMarkers>::Skel, _>::new(provider_p, kind),
&<FSet::T as TimeMarkers>::TimeSkeletonPatternsV1::bind(provider_p),
&FSet::GluePatternV1::bind(provider_p),
prefs,
field_set,
);
let selection = match selection {
Ok(selection) => selection,
Err(e) => return Err((DateTimeFormatterLoadError::Data(e), (calendar, names))),
};
let result = names.load_for_pattern(
&AnyCalendarProvider::<<FSet::D as DateDataMarkers>::Year, _>::new(provider, kind),
&AnyCalendarProvider::<<FSet::D as DateDataMarkers>::Month, _>::new(provider, kind),
&<FSet::D as DateDataMarkers>::WeekdayNamesV1::bind(provider),
&<FSet::T as TimeMarkers>::DayPeriodNamesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::EssentialsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::StandardLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::MetazonePeriodV1::bind(provider),
loader, prefs,
selection.pattern_items_for_data_loading(),
);
match result {
Ok(()) => (),
Err(e) => return Err((DateTimeFormatterLoadError::Names(e), (calendar, names))),
};
Ok(Self {
selection,
names,
calendar,
})
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateInputMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
pub fn format_same_calendar<I>(
&self,
datetime: &I,
) -> Result<FormattedDateTime, crate::MismatchedCalendarError>
where
I: ?Sized + InSameCalendar + AllInputMarkers<FSet>,
{
datetime.check_any_calendar_kind(self.calendar.kind())?;
let datetime =
ExtractedInput::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I>(datetime);
Ok(FormattedDateTime {
pattern: self.selection.select(&datetime),
input: datetime,
names: self.names.as_borrowed(),
})
}
pub fn format<'a, I>(&'a self, datetime: &I) -> FormattedDateTime<'a>
where
I: ?Sized + ConvertCalendar,
I::Converted<'a>: Sized + AllInputMarkers<FSet>,
{
let datetime = datetime.to_calendar(&self.calendar);
let datetime =
ExtractedInput::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I::Converted<'a>>(
&datetime,
);
FormattedDateTime {
pattern: self.selection.select(&datetime),
input: datetime,
names: self.names.as_borrowed(),
}
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet> {
pub fn into_formatter(self, calendar: C) -> DateTimeFormatter<FSet>
where
C: IntoAnyCalendar,
{
DateTimeFormatter {
selection: self.selection,
names: self.names,
calendar: calendar.to_any(),
}
}
pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(
self,
) -> FixedCalendarDateTimeFormatter<C, FSet2> {
FixedCalendarDateTimeFormatter {
selection: self.selection,
names: self.names.cast_into_fset(),
_calendar: PhantomData,
}
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet> {
pub fn try_into_typed_formatter<C>(
self,
) -> Result<FixedCalendarDateTimeFormatter<C, FSet>, MismatchedCalendarError>
where
C: CldrCalendar + IntoAnyCalendar,
{
if let Err(cal) = C::from_any(self.calendar) {
return Err(MismatchedCalendarError {
this_kind: cal.kind(),
date_kind: None,
});
}
Ok(FixedCalendarDateTimeFormatter {
selection: self.selection,
names: self.names,
_calendar: PhantomData,
})
}
pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> DateTimeFormatter<FSet2> {
DateTimeFormatter {
selection: self.selection,
names: self.names.cast_into_fset(),
calendar: self.calendar,
}
}
pub fn calendar(&self) -> icu_calendar::Ref<AnyCalendar> {
icu_calendar::Ref(&self.calendar)
}
}
pub type NoCalendarFormatter<FSet> = FixedCalendarDateTimeFormatter<(), FSet>;
#[derive(Debug)]
pub struct FormattedDateTime<'a> {
pattern: DateTimeZonePatternDataBorrowed<'a>,
input: ExtractedInput,
names: RawDateTimeNamesBorrowed<'a>,
}
impl Writeable for FormattedDateTime<'_> {
fn write_to_parts<S: writeable::PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<(), fmt::Error> {
let result = try_write_pattern_items(
self.pattern.metadata(),
self.pattern.iter_items(),
&self.input,
&self.names,
self.names.decimal_formatter,
sink,
);
match result {
Ok(Ok(())) => Ok(()),
Err(fmt::Error) => Err(fmt::Error),
Ok(Err(e)) => {
debug_assert!(false, "unexpected error in FormattedDateTime: {e:?}");
Ok(())
}
}
}
}
impl_display_with_writeable!(FormattedDateTime<'_>);
impl FormattedDateTime<'_> {
pub fn pattern(&self) -> DateTimePattern {
self.pattern.to_pattern()
}
}