use std::{fmt, str::FromStr};
use crate::value::JsVariant;
use crate::{Context, JsNativeError, JsResult, JsString, JsValue, object::JsObject};
pub(crate) trait OptionType: Sized {
fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self>;
}
pub(crate) trait ParsableOptionType: FromStr {}
impl<T: ParsableOptionType> OptionType for T
where
T::Err: fmt::Display,
{
fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {
value
.to_string(context)?
.to_std_string_escaped()
.parse::<Self>()
.map_err(|err| JsNativeError::range().with_message(err.to_string()).into())
}
}
pub(crate) fn get_option<T: OptionType>(
options: &JsObject,
property: JsString,
context: &mut Context,
) -> JsResult<Option<T>> {
let value = options.get(property, context)?;
if value.is_undefined() {
return Ok(None);
}
T::from_value(value, context).map(Some)
}
pub(crate) fn get_options_object(options: &JsValue) -> JsResult<JsObject> {
match options.variant() {
JsVariant::Undefined => {
Ok(JsObject::with_null_proto())
}
JsVariant::Object(obj) => {
Ok(obj.clone())
}
_ => Err(JsNativeError::typ()
.with_message("GetOptionsObject: provided options is not an object")
.into()),
}
}
impl OptionType for bool {
fn from_value(value: JsValue, _: &mut Context) -> JsResult<Self> {
Ok(value.to_boolean())
}
}
impl OptionType for JsString {
fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {
value.to_string(context)
}
}
impl OptionType for f64 {
fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {
let value = value.to_number(context)?;
if !value.is_finite() {
return Err(JsNativeError::range()
.with_message("numeric option must be finite.")
.into());
}
Ok(value)
}
}