use crate::IterableDataProviderCached;
use crate::SourceDataProvider;
use crate::cldr_serde;
use crate::cldr_serde::numbers::NumberPattern;
use crate::cldr_serde::numbers::NumberPatternItem;
use std::borrow::Cow;
use std::collections::HashSet;
use icu_pattern::DoublePlaceholderKey;
use icu_pattern::DoublePlaceholderPattern;
use icu_pattern::PatternItemCow;
use icu_provider::DataProvider;
use icu::experimental::dimension::provider::currency::essentials::*;
use icu_provider::prelude::*;
impl DataProvider<CurrencyEssentialsV1> for SourceDataProvider {
fn load(&self, req: DataRequest) -> Result<DataResponse<CurrencyEssentialsV1>, DataError> {
self.check_req::<CurrencyEssentialsV1>(req)?;
let numbers_resource: &cldr_serde::numbers::Resource = self
.cldr()?
.numbers()
.read_and_parse(req.id.locale, "numbers.json")?;
let nsname = if !req.id.marker_attributes.is_empty() {
req.id.marker_attributes.as_str()
} else {
&numbers_resource.main.value.numbers.default_numbering_system
};
let result = extract_currency_essentials(numbers_resource, nsname);
Ok(DataResponse {
metadata: Default::default(),
payload: DataPayload::from_owned(result?),
})
}
}
impl IterableDataProviderCached<CurrencyEssentialsV1> for SourceDataProvider {
fn iter_ids_cached(&self) -> Result<HashSet<DataIdentifierCow<'static>>, DataError> {
super::iter_numsys_pattern_ids(self, |patterns| !patterns.standard.positive.is_empty())
}
}
fn extract_currency_essentials<'data>(
numbers_resource: &cldr_serde::numbers::Resource,
numsys_name: &str,
) -> Result<CurrencyEssentials<'data>, DataError> {
let numbers_block = &numbers_resource.main.value.numbers;
let currency_formats = numbers_block
.numsys_data
.currency_patterns
.get(numsys_name)
.ok_or_else(|| DataErrorKind::IdentifierNotFound.into_error())?;
let standard = ¤cy_formats.standard;
if standard.positive.is_empty() {
return Err(DataErrorKind::IdentifierNotFound.into_error());
}
let standard_alpha_next_to_number = currency_formats.standard_alpha_next_to_number.as_ref();
let accounting = currency_formats.accounting.as_ref();
let accounting_alpha_next_to_number = currency_formats.accounting_alpha_next_to_number.as_ref();
let (minus_sign, plus_sign) =
if let Some(symbols) = numbers_block.numsys_data.symbols.get(numsys_name) {
(symbols.minus_sign.as_str(), symbols.plus_sign.as_str())
} else {
("-", "+")
};
fn convert_pattern_items<'a>(
items: &'a [NumberPatternItem],
minus_sign: &'a str,
plus_sign: &'a str,
) -> impl Iterator<Item = PatternItemCow<'a, DoublePlaceholderKey>> + 'a {
items.iter().flat_map(move |item| match item {
NumberPatternItem::Currency => {
Some(PatternItemCow::Placeholder(DoublePlaceholderKey::Place1))
}
NumberPatternItem::Literal(s) => Some(PatternItemCow::Literal(Cow::Borrowed(s))),
NumberPatternItem::DecimalSeparator => {
Some(PatternItemCow::Placeholder(DoublePlaceholderKey::Place0))
}
NumberPatternItem::MinusSign => {
Some(PatternItemCow::Literal(Cow::Borrowed(minus_sign)))
}
NumberPatternItem::PlusSign => Some(PatternItemCow::Literal(Cow::Borrowed(plus_sign))),
_ => None,
})
}
let create_positive_pattern =
|pattern: &NumberPattern| -> Result<Cow<'data, DoublePlaceholderPattern>, DataError> {
DoublePlaceholderPattern::try_from_items(convert_pattern_items(
&pattern.positive,
minus_sign,
plus_sign,
))
.map_err(|e| {
DataError::custom("Could not parse positive pattern").with_display_context(&e)
})
.map(Cow::Owned)
};
let create_negative_pattern = |pattern: &NumberPattern| -> Result<
Option<Cow<'data, DoublePlaceholderPattern>>,
DataError,
> {
if let Some(negative_items) = &pattern.negative {
DoublePlaceholderPattern::try_from_items(convert_pattern_items(
negative_items,
minus_sign,
plus_sign,
))
.map_err(|e| {
DataError::custom("Could not parse negative pattern").with_display_context(&e)
})
.map(Cow::Owned)
.map(Some)
} else {
Ok(None)
}
};
let mut patterns = super::PatternSet::new();
let standard_idx = patterns
.add(Some(create_positive_pattern(standard)?))
.unwrap();
let standard_neg_idx = patterns.add(create_negative_pattern(standard)?);
let standard_alpha_idx = patterns
.add(
standard_alpha_next_to_number
.map(create_positive_pattern)
.transpose()?,
)
.unwrap_or(standard_idx);
let standard_alpha_neg_idx = match standard_alpha_next_to_number {
Some(p) => patterns.add(create_negative_pattern(p)?),
None => None,
};
let accounting_pos_idx = patterns
.add(accounting.map(create_positive_pattern).transpose()?)
.unwrap_or(standard_idx);
let accounting_neg_idx = match accounting {
Some(p) => patterns.add(create_negative_pattern(p)?),
None => None,
};
let accounting_alpha_pos_idx = patterns
.add(
accounting_alpha_next_to_number
.map(create_positive_pattern)
.transpose()?,
)
.unwrap_or(accounting_pos_idx);
let accounting_alpha_neg_idx = match accounting_alpha_next_to_number {
Some(p) => patterns.add(create_negative_pattern(p)?),
None => accounting_neg_idx,
};
let indices = PatternIndices {
standard: standard_idx,
standard_negative: standard_neg_idx,
standard_alpha_next_to_number: standard_alpha_idx,
standard_alpha_next_to_number_negative: standard_alpha_neg_idx,
accounting_positive: accounting_pos_idx,
accounting_negative: accounting_neg_idx,
accounting_alpha_next_to_number_positive: accounting_alpha_pos_idx,
accounting_alpha_next_to_number_negative: accounting_alpha_neg_idx,
};
Ok(CurrencyEssentials {
patterns: patterns.into_var_zero_vec(),
indices,
})
}
#[test]
fn test_essentials() {
use icu::locale::data_locale;
use writeable::assert_writeable_eq;
let provider = SourceDataProvider::new_testing();
let en: DataPayload<CurrencyEssentialsV1> = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&data_locale!("en")),
..Default::default()
})
.unwrap()
.payload;
assert_writeable_eq!(
en.get().get_positive(false, false).interpolate((3, "$")),
"$3"
);
assert_writeable_eq!(
en.get().get_positive(true, true).interpolate((3, "USD")),
"USD\u{a0}3"
);
assert_writeable_eq!(
en.get()
.get_positive_accounting(false, false)
.interpolate((3, "$")),
"$3"
);
assert_writeable_eq!(
en.get()
.get_negative_accounting(false, false)
.unwrap()
.interpolate((3, "$")),
"($3)"
);
assert_writeable_eq!(
en.get()
.get_positive_accounting(true, true)
.interpolate((3, "USD")),
"USD\u{a0}3"
);
assert_writeable_eq!(
en.get()
.get_negative_accounting(true, true)
.unwrap()
.interpolate((3, "USD")),
"(USD\u{a0}3)"
);
let ar_eg: DataPayload<CurrencyEssentialsV1> = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&data_locale!("ar-EG")),
..Default::default()
})
.unwrap()
.payload;
assert_writeable_eq!(
ar_eg.get().get_positive(false, false).interpolate((3, "$")),
"\u{200f}3\u{a0}$"
);
let ar_eg_latn: DataPayload<CurrencyEssentialsV1> = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
DataMarkerAttributes::from_str_or_panic("latn"),
&data_locale!("ar-EG"),
),
..Default::default()
})
.unwrap()
.payload;
assert_writeable_eq!(
ar_eg_latn
.get()
.get_negative(false, false)
.unwrap()
.interpolate((3, "$")),
"\u{200f}\u{200e}-3\u{a0}$"
);
assert!(en.get().get_negative(false, false).is_none());
}