use crate::dimension::provider::units::UnitsDisplayNameV1Marker;
use crate::dimension::units::formatter::{UnitsFormatter, UnitsFormatterPreferences};
use crate::dimension::units::options::{UnitsFormatterOptions, Width};
use crate::duration::options::FieldStyle;
use super::format::FormattedDuration;
use super::options::BaseStyle;
use super::validated_options::Unit;
use super::{provider, Duration};
pub use super::validated_options::ValidatedDurationFormatterOptions;
use icu_decimal::provider::{DecimalDigitsV1Marker, DecimalSymbolsV2Marker};
use icu_decimal::{FixedDecimalFormatter, FixedDecimalFormatterPreferences};
use icu_list::{ListFormatter, ListFormatterPreferences, ListLength};
use icu_locale_core::preferences::{
define_preferences, extensions::unicode::keywords::NumberingSystem, prefs_convert,
};
use icu_provider::prelude::*;
define_preferences!(
[Copy]
DurationFormatterPreferences,
{
numbering_system: NumberingSystem
}
);
prefs_convert!(DurationFormatterPreferences, UnitsFormatterPreferences, {
numbering_system
});
prefs_convert!(
DurationFormatterPreferences,
FixedDecimalFormatterPreferences,
{ numbering_system }
);
prefs_convert!(DurationFormatterPreferences, ListFormatterPreferences);
pub struct DurationFormatter {
pub(crate) options: ValidatedDurationFormatterOptions,
pub(crate) digital: DataPayload<provider::DigitalDurationDataV1Marker>,
pub(crate) unit: DurationUnitFormatter,
pub(crate) list: ListFormatter,
pub(crate) fdf: FixedDecimalFormatter,
}
pub(crate) struct DurationUnitFormatter {
pub(crate) year: UnitsFormatter,
pub(crate) month: UnitsFormatter,
pub(crate) week: UnitsFormatter,
pub(crate) day: UnitsFormatter,
pub(crate) hour: UnitsFormatter,
pub(crate) minute: UnitsFormatter,
pub(crate) second: UnitsFormatter,
pub(crate) millisecond: UnitsFormatter,
pub(crate) microsecond: UnitsFormatter,
pub(crate) nanosecond: UnitsFormatter,
}
impl core::ops::Index<Unit> for DurationUnitFormatter {
type Output = UnitsFormatter;
fn index(&self, index: Unit) -> &Self::Output {
match index {
Unit::Year => &self.year,
Unit::Month => &self.month,
Unit::Week => &self.week,
Unit::Day => &self.day,
Unit::Hour => &self.hour,
Unit::Minute => &self.minute,
Unit::Second => &self.second,
Unit::Millisecond => &self.millisecond,
Unit::Microsecond => &self.microsecond,
Unit::Nanosecond => &self.nanosecond,
}
}
}
impl DurationUnitFormatter {
fn field_style_to_unit_width(style: FieldStyle, base: BaseStyle) -> Width {
match style {
FieldStyle::Long => Width::Long,
FieldStyle::Short => Width::Short,
FieldStyle::Narrow => Width::Narrow,
_ => match base {
BaseStyle::Long => Width::Long,
BaseStyle::Short | BaseStyle::Digital => Width::Short,
BaseStyle::Narrow => Width::Narrow,
},
}
}
#[cfg(feature = "compiled_data")]
fn try_new(
prefs: DurationFormatterPreferences,
options: ValidatedDurationFormatterOptions,
) -> Result<Self, DataError> {
let get_unit_formatter = |unit: Unit, style| {
let w = DurationUnitFormatter::field_style_to_unit_width(style, options.base);
let options = UnitsFormatterOptions { width: w };
UnitsFormatter::try_new((&prefs).into(), unit.as_unit_formatter_name(), options)
};
Ok(DurationUnitFormatter {
year: get_unit_formatter(Unit::Year, options.year)?,
month: get_unit_formatter(Unit::Month, options.month)?,
week: get_unit_formatter(Unit::Week, options.week)?,
day: get_unit_formatter(Unit::Day, options.day)?,
hour: get_unit_formatter(Unit::Hour, options.hour)?,
minute: get_unit_formatter(Unit::Minute, options.minute)?,
second: get_unit_formatter(Unit::Second, options.second)?,
millisecond: get_unit_formatter(Unit::Millisecond, options.millisecond)?,
microsecond: get_unit_formatter(Unit::Microsecond, options.microsecond)?,
nanosecond: get_unit_formatter(Unit::Nanosecond, options.nanosecond)?,
})
}
fn try_new_unstable<
D: ?Sized
+ DataProvider<UnitsDisplayNameV1Marker>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV2Marker>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1Marker>
+ DataProvider<icu_plurals::provider::CardinalV1Marker>,
>(
provider: &D,
prefs: DurationFormatterPreferences,
options: ValidatedDurationFormatterOptions,
) -> Result<Self, DataError> {
let get_unit_formatter = |unit: Unit, style| {
let w = DurationUnitFormatter::field_style_to_unit_width(style, options.base);
let options = UnitsFormatterOptions { width: w };
UnitsFormatter::try_new_unstable(
provider,
(&prefs).into(),
unit.as_unit_formatter_name(),
options,
)
};
Ok(DurationUnitFormatter {
year: get_unit_formatter(Unit::Year, options.year)?,
month: get_unit_formatter(Unit::Month, options.month)?,
week: get_unit_formatter(Unit::Week, options.week)?,
day: get_unit_formatter(Unit::Day, options.day)?,
hour: get_unit_formatter(Unit::Hour, options.hour)?,
minute: get_unit_formatter(Unit::Minute, options.minute)?,
second: get_unit_formatter(Unit::Second, options.second)?,
millisecond: get_unit_formatter(Unit::Millisecond, options.millisecond)?,
microsecond: get_unit_formatter(Unit::Microsecond, options.microsecond)?,
nanosecond: get_unit_formatter(Unit::Nanosecond, options.nanosecond)?,
})
}
}
impl From<BaseStyle> for icu_list::ListFormatterOptions {
fn from(style: BaseStyle) -> Self {
let length = match style {
BaseStyle::Long => ListLength::Wide,
BaseStyle::Short | BaseStyle::Digital => ListLength::Short,
BaseStyle::Narrow => ListLength::Narrow,
};
Self::default().with_length(length)
}
}
impl DurationFormatter {
icu_provider::gen_any_buffer_data_constructors!(
(prefs: DurationFormatterPreferences, options: ValidatedDurationFormatterOptions) -> error: DataError,
functions: [
try_new: skip,
try_new_with_any_provider,
try_new_with_buffer_provider,
try_new_unstable,
Self
]
);
#[cfg(feature = "compiled_data")]
pub fn try_new(
prefs: DurationFormatterPreferences,
options: ValidatedDurationFormatterOptions,
) -> Result<Self, DataError> {
let locale = DataLocale::from_preferences_locale::<provider::DigitalDurationDataV1Marker>(
prefs.locale_prefs,
);
let digital = crate::provider::Baked
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&locale),
..Default::default()
})?
.payload;
Ok(Self {
digital,
options,
unit: DurationUnitFormatter::try_new(prefs, options)?,
list: ListFormatter::try_new_unit((&prefs).into(), options.base.into())?,
fdf: FixedDecimalFormatter::try_new((&prefs).into(), Default::default())?,
})
}
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<
D: DataProvider<provider::DigitalDurationDataV1Marker>
+ DataProvider<UnitsDisplayNameV1Marker>
+ DataProvider<DecimalSymbolsV2Marker>
+ DataProvider<DecimalDigitsV1Marker>
+ DataProvider<icu_plurals::provider::CardinalV1Marker>
+ DataProvider<icu_list::provider::UnitListV2Marker>
+ ?Sized,
>(
provider: &D,
prefs: DurationFormatterPreferences,
options: ValidatedDurationFormatterOptions,
) -> Result<Self, DataError> {
let locale = DataLocale::from_preferences_locale::<provider::DigitalDurationDataV1Marker>(
prefs.locale_prefs,
);
let digital = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&locale),
..Default::default()
})?
.payload;
Ok(Self {
digital,
options,
unit: DurationUnitFormatter::try_new_unstable(provider, prefs, options)?,
list: ListFormatter::try_new_unit_unstable(
provider,
(&prefs).into(),
options.base.into(),
)?,
fdf: FixedDecimalFormatter::try_new_unstable(
provider,
(&prefs).into(),
Default::default(),
)?,
})
}
pub fn format<'l>(&'l self, duration: &'l Duration) -> FormattedDuration<'l> {
FormattedDuration {
fmt: self,
duration,
}
}
}