use crate::{
Context, JsArgs, JsData, JsResult, JsString, JsValue,
builtins::{Array, BuiltInBuilder, BuiltInObject, IntrinsicObject},
context::{icu::IntlProvider, intrinsics::Intrinsics},
js_string,
object::JsObject,
property::Attribute,
realm::Realm,
string::StaticJsStrings,
symbol::JsSymbol,
};
use boa_gc::{Finalize, Trace};
use icu_locale::{LanguageIdentifier, extensions::unicode};
use icu_provider::{DataMarker, DataMarkerAttributes};
use static_assertions::const_assert;
pub(crate) use self::{
collator::Collator, date_time_format::DateTimeFormat, list_format::ListFormat, locale::Locale,
number_format::NumberFormat, plural_rules::PluralRules, segmenter::Segmenter,
};
macro_rules! impl_service_preferences {
($($field:ident),*) => {
fn extended(&self, other: &Self) -> Self {
let mut result = *self;
result.extend(*other);
result
}
fn as_unicode(&self) -> unicode::Unicode {
let mut exts = unicode::Unicode::new();
$(
if let Some(key) = &self.$field
&& let Some((key, value)) = $crate::builtins::intl::get_kv_from_pref(key)
{
exts.keywords.set(key, value);
}
)*
exts
}
fn intersection(&self, other: &Self) -> Self {
let mut inter = *self;
if inter.locale_preferences != other.locale_preferences {
inter.locale_preferences = LocalePreferences::default();
}
$(
if inter.$field != other.$field {
inter.$field.take();
}
)*
inter
}
};
}
pub(crate) mod collator;
pub(crate) mod date_time_format;
pub(crate) mod list_format;
pub(crate) mod locale;
pub(crate) mod number_format;
pub(crate) mod plural_rules;
pub(crate) mod segmenter;
mod options;
const_assert! {!<Collator as Service>::LangMarker::INFO.is_singleton}
const_assert! {!<ListFormat as Service>::LangMarker::INFO.is_singleton}
const_assert! {!<NumberFormat as Service>::LangMarker::INFO.is_singleton}
const_assert! {!<PluralRules as Service>::LangMarker::INFO.is_singleton}
const_assert! {!<Segmenter as Service>::LangMarker::INFO.is_singleton}
const_assert! {!<DateTimeFormat as Service>::LangMarker::INFO.is_singleton}
#[derive(Debug, Clone, Trace, Finalize, JsData)]
#[boa_gc(unsafe_empty_trace)]
pub struct Intl {
fallback_symbol: JsSymbol,
}
impl Intl {
#[must_use]
pub fn fallback_symbol(&self) -> JsSymbol {
self.fallback_symbol.clone()
}
pub(crate) fn new() -> Option<Self> {
let fallback_symbol = JsSymbol::new(Some(js_string!("IntlLegacyConstructedSymbol")))?;
Some(Self { fallback_symbol })
}
}
impl IntrinsicObject for Intl {
fn init(realm: &Realm) {
BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::CONFIGURABLE,
)
.static_property(
Collator::NAME,
realm.intrinsics().constructors().collator().constructor(),
Collator::ATTRIBUTE,
)
.static_property(
ListFormat::NAME,
realm
.intrinsics()
.constructors()
.list_format()
.constructor(),
ListFormat::ATTRIBUTE,
)
.static_property(
Locale::NAME,
realm.intrinsics().constructors().locale().constructor(),
Locale::ATTRIBUTE,
)
.static_property(
Segmenter::NAME,
realm.intrinsics().constructors().segmenter().constructor(),
Segmenter::ATTRIBUTE,
)
.static_property(
PluralRules::NAME,
realm
.intrinsics()
.constructors()
.plural_rules()
.constructor(),
PluralRules::ATTRIBUTE,
)
.static_property(
DateTimeFormat::NAME,
realm
.intrinsics()
.constructors()
.date_time_format()
.constructor(),
DateTimeFormat::ATTRIBUTE,
)
.static_property(
NumberFormat::NAME,
realm
.intrinsics()
.constructors()
.number_format()
.constructor(),
NumberFormat::ATTRIBUTE,
)
.static_method(
Self::get_canonical_locales,
js_string!("getCanonicalLocales"),
1,
)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().intl().upcast()
}
}
impl BuiltInObject for Intl {
const NAME: JsString = StaticJsStrings::INTL;
}
impl Intl {
pub(crate) fn get_canonical_locales(
_: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let locales = args.get_or_undefined(0);
let ll = locale::canonicalize_locale_list(locales, context)?;
Ok(JsValue::new(Array::create_array_from_list(
ll.into_iter().map(|loc| js_string!(loc.to_string()).into()),
context,
)))
}
}
fn get_kv_from_pref<T: icu_locale::preferences::PreferenceKey>(
pref: &T,
) -> Option<(unicode::Key, unicode::Value)> {
T::unicode_extension_key().zip(pref.unicode_extension_value())
}
trait ServicePreferences: for<'a> From<&'a icu_locale::Locale> + Clone {
fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider);
fn as_unicode(&self) -> unicode::Unicode;
fn extended(&self, other: &Self) -> Self;
fn intersection(&self, other: &Self) -> Self;
}
trait Service {
type LangMarker: DataMarker;
const ATTRIBUTES: &'static DataMarkerAttributes = DataMarkerAttributes::empty();
type Preferences: ServicePreferences;
}