-
-
Notifications
You must be signed in to change notification settings - Fork 411
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new type Convert<> to convert values (#3786)
* Add TryFromJs for more types * Add a new type Coerce<> to coerce values This can be used to transform values in to coerced versions by using the type system and TryFromJs. * Fix build error * Rename Coerce<> to Convert<> * replace missed coerce to Convert * cargo fmt and clippy
- Loading branch information
Showing
4 changed files
with
182 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
//! Types and functions for applying JavaScript Convert rules to [`JsValue`] when | ||
//! converting. See <https://262.ecma-international.org/5.1/#sec-9> (Section 9) for | ||
//! conversion rules of JavaScript types. | ||
//! | ||
//! Some conversions are not specified in the spec (e.g. integer conversions), | ||
//! and we apply rules that make sense (e.g. converting to Number and rounding | ||
//! if necessary). | ||
use boa_engine::JsNativeError; | ||
|
||
use crate::value::TryFromJs; | ||
use crate::{Context, JsResult, JsString, JsValue}; | ||
|
||
/// A wrapper type that allows converting a `JsValue` to a specific type. | ||
/// This is useful when you want to convert a `JsValue` to a Rust type. | ||
/// | ||
/// # Example | ||
/// Convert a string to number. | ||
/// ``` | ||
/// # use boa_engine::{Context, js_string, JsValue}; | ||
/// # use boa_engine::value::{Convert, TryFromJs}; | ||
/// # let mut context = Context::default(); | ||
/// let value = JsValue::from(js_string!("42")); | ||
/// let Convert(converted): Convert<i32> = Convert::try_from_js(&value, &mut context).unwrap(); | ||
/// | ||
/// assert_eq!(converted, 42); | ||
/// ``` | ||
/// | ||
/// Convert a number to a bool. | ||
/// ``` | ||
/// # use boa_engine::{Context, js_string, JsValue}; | ||
/// # use boa_engine::value::{Convert, TryFromJs}; | ||
/// # let mut context = Context::default(); | ||
/// let Convert(conv0): Convert<bool> = Convert::try_from_js(&JsValue::Integer(0), &mut context).unwrap(); | ||
/// let Convert(conv5): Convert<bool> = Convert::try_from_js(&JsValue::Integer(5), &mut context).unwrap(); | ||
/// let Convert(conv_nan): Convert<bool> = Convert::try_from_js(&JsValue::Rational(f64::NAN), &mut context).unwrap(); | ||
/// | ||
/// assert_eq!(conv0, false); | ||
/// assert_eq!(conv5, true); | ||
/// assert_eq!(conv_nan, false); | ||
/// ``` | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub struct Convert<T: TryFromJs>(pub T); | ||
|
||
impl<T: TryFromJs> From<T> for Convert<T> { | ||
fn from(value: T) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
macro_rules! decl_convert_to_int { | ||
($($ty:ty),*) => { | ||
$( | ||
impl TryFromJs for Convert<$ty> { | ||
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> { | ||
value.to_numeric_number(context).and_then(|num| { | ||
if num.is_finite() { | ||
if num >= f64::from(<$ty>::MAX) { | ||
Err(JsNativeError::typ() | ||
.with_message("cannot convert value to integer, it is too large") | ||
.into()) | ||
} else if num <= f64::from(<$ty>::MIN) { | ||
Err(JsNativeError::typ() | ||
.with_message("cannot convert value to integer, it is too small") | ||
.into()) | ||
// Only round if it differs from the next integer by an epsilon | ||
} else if num.abs().fract() >= (1.0 - f64::EPSILON) { | ||
Ok(Convert(num.round() as $ty)) | ||
} else { | ||
Ok(Convert(num as $ty)) | ||
} | ||
} else if num.is_nan() { | ||
Err(JsNativeError::typ() | ||
.with_message("cannot convert NaN to integer") | ||
.into()) | ||
} else if num.is_infinite() { | ||
Err(JsNativeError::typ() | ||
.with_message("cannot convert Infinity to integer") | ||
.into()) | ||
} else { | ||
Err(JsNativeError::typ() | ||
.with_message("cannot convert non-finite number to integer") | ||
.into()) | ||
} | ||
}) | ||
} | ||
} | ||
)* | ||
}; | ||
} | ||
|
||
decl_convert_to_int!(i8, i16, i32, u8, u16, u32); | ||
|
||
macro_rules! decl_convert_to_float { | ||
($($ty:ty),*) => { | ||
$( | ||
impl TryFromJs for Convert<$ty> { | ||
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> { | ||
value.to_numeric_number(context).and_then(|num| Ok(Convert(<$ty>::try_from(num).map_err(|_| { | ||
JsNativeError::typ() | ||
.with_message("cannot convert value to float") | ||
})?))) | ||
} | ||
} | ||
)* | ||
}; | ||
} | ||
|
||
decl_convert_to_float!(f64); | ||
|
||
impl TryFromJs for Convert<String> { | ||
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> { | ||
value | ||
.to_string(context) | ||
.and_then(|s| s.to_std_string().map_err(|_| JsNativeError::typ().into())) | ||
.map(Convert) | ||
} | ||
} | ||
|
||
impl TryFromJs for Convert<JsString> { | ||
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> { | ||
value.to_string(context).map(Convert) | ||
} | ||
} | ||
|
||
impl TryFromJs for Convert<bool> { | ||
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> { | ||
Ok(Self(value.to_boolean())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters