use std::{fmt, str::FromStr};
use num_traits::FromPrimitive;
use crate::{
Context, JsNativeError, JsResult, JsString, JsValue,
builtins::{OrdinaryObject, options::ParsableOptionType},
object::JsObject,
};
#[derive(Debug, Default)]
pub(super) struct IntlOptions<O> {
pub(super) matcher: LocaleMatcher,
pub(super) service_options: O,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub(super) enum LocaleMatcher {
Lookup,
#[default]
BestFit,
}
#[derive(Debug)]
pub(super) struct ParseLocaleMatcherError;
impl fmt::Display for ParseLocaleMatcherError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"provided string was not `lookup` or `best fit`".fmt(f)
}
}
impl FromStr for LocaleMatcher {
type Err = ParseLocaleMatcherError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"lookup" => Ok(Self::Lookup),
"best fit" => Ok(Self::BestFit),
_ => Err(ParseLocaleMatcherError),
}
}
}
impl ParsableOptionType for LocaleMatcher {}
pub(super) fn get_number_option<T>(
options: &JsObject,
property: JsString,
minimum: T,
maximum: T,
context: &mut Context,
) -> JsResult<Option<T>>
where
T: Into<f64> + FromPrimitive,
{
let value = options.get(property, context)?;
default_number_option(&value, minimum, maximum, context)
}
pub(super) fn default_number_option<T>(
value: &JsValue,
minimum: T,
maximum: T,
context: &mut Context,
) -> JsResult<Option<T>>
where
T: Into<f64> + FromPrimitive,
{
if value.is_undefined() {
return Ok(None);
}
let value = value.to_number(context)?;
if value.is_nan() || value < minimum.into() || value > maximum.into() {
return Err(JsNativeError::range()
.with_message("DefaultNumberOption: value is out of range.")
.into());
}
Ok(T::from_f64(value))
}
pub(super) fn coerce_options_to_object(
options: &JsValue,
context: &mut Context,
) -> JsResult<JsObject> {
if options.is_undefined() {
return Ok(JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
None,
OrdinaryObject,
));
}
options.to_object(context)
}