Browse Source

Add a try_from_js implementation for Vec<T> (accept any Array-like) (#3755)

* Add a try_from_js implementation for Vec<T> (accept any Array-like)

* Use safe conversion to usize

* Fix typing compile error

* Fix clippies and fmt

* Remove println from test

* Add a negative test
pull/3762/head
Hans Larsen 2 months ago committed by GitHub
parent
commit
1c6a095196
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 94
      core/engine/src/value/conversions/try_from_js.rs

94
core/engine/src/value/conversions/try_from_js.rs

@ -1,8 +1,9 @@
//! This module contains the [`TryFromJs`] trait, and conversions to basic Rust types.
use crate::{Context, JsBigInt, JsNativeError, JsResult, JsValue};
use num_bigint::BigInt;
use crate::{js_string, Context, JsBigInt, JsNativeError, JsResult, JsValue};
/// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types.
pub trait TryFromJs: Sized {
/// This function tries to convert a JavaScript value into `Self`.
@ -58,6 +59,38 @@ where
}
}
impl<T> TryFromJs for Vec<T>
where
T: TryFromJs,
{
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {
let JsValue::Object(object) = value else {
return Err(JsNativeError::typ()
.with_message("cannot convert value to a Vec")
.into());
};
let length = object
.get(js_string!("length"), context)?
.to_length(context)?;
let length = match usize::try_from(length) {
Ok(length) => length,
Err(e) => {
return Err(JsNativeError::typ()
.with_message(format!("could not convert length to usize: {e}"))
.into());
}
};
let mut vec = Vec::with_capacity(length);
for i in 0..length {
let value = object.get(i, context)?;
vec.push(T::try_from_js(&value, context)?);
}
Ok(vec)
}
}
impl TryFromJs for JsBigInt {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match value {
@ -250,3 +283,62 @@ impl TryFromJs for u128 {
}
}
}
#[test]
fn value_into_vec() {
use boa_engine::{run_test_actions, TestAction};
use indoc::indoc;
#[derive(Debug, PartialEq, Eq, boa_macros::TryFromJs)]
struct TestStruct {
inner: bool,
my_int: i16,
my_vec: Vec<String>,
}
run_test_actions([
TestAction::assert_with_op(
indoc! {r#"
let value = {
inner: true,
my_int: 11,
my_vec: ["a", "b", "c"]
};
value
"#},
|value, context| {
let value = TestStruct::try_from_js(&value, context);
match value {
Ok(value) => {
value
== TestStruct {
inner: true,
my_int: 11,
my_vec: vec!["a".to_string(), "b".to_string(), "c".to_string()],
}
}
_ => false,
}
},
),
TestAction::assert_with_op(
indoc!(
r#"
let wrong = {
inner: false,
my_int: 22,
my_vec: [{}, "e", "f"]
};
wrong"#
),
|value, context| {
let Err(value) = TestStruct::try_from_js(&value, context) else {
return false;
};
assert!(value.to_string().contains("TypeError"));
true
},
),
]);
}

Loading…
Cancel
Save