Skip to content

Commit

Permalink
Adding TryFromJs implementations for BTreeMap and HashMap (#3844)
Browse files Browse the repository at this point in the history
  • Loading branch information
hansl authored Jun 6, 2024
1 parent 4419e6d commit 109dd3d
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
47 changes: 47 additions & 0 deletions core/engine/src/value/conversions/try_from_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use num_bigint::BigInt;

use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue};

mod collections;

/// 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`.
Expand Down Expand Up @@ -364,3 +366,48 @@ fn value_into_vec() {
),
]);
}

#[test]
fn value_into_map() {
use boa_engine::{run_test_actions, TestAction};
use indoc::indoc;

run_test_actions([
TestAction::assert_with_op(indoc! {r#" ({ a: 1, b: 2, c: 3 }) "#}, |value, context| {
let value = std::collections::BTreeMap::<String, i32>::try_from_js(&value, context);

match value {
Ok(value) => {
value
== vec![
("a".to_string(), 1),
("b".to_string(), 2),
("c".to_string(), 3),
]
.into_iter()
.collect::<std::collections::BTreeMap<String, i32>>()
}
_ => false,
}
}),
TestAction::assert_with_op(indoc! {r#" ({ a: 1, b: 2, c: 3 }) "#}, |value, context| {
let value = std::collections::HashMap::<String, i32>::try_from_js(&value, context);

match value {
Ok(value) => {
value
== std::collections::HashMap::from_iter(
vec![
("a".to_string(), 1),
("b".to_string(), 2),
("c".to_string(), 3),
]
.into_iter()
.collect::<std::collections::BTreeMap<String, i32>>(),
)
}
_ => false,
}
}),
]);
}
64 changes: 64 additions & 0 deletions core/engine/src/value/conversions/try_from_js/collections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! [`JsValue`] conversions for std collections.
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;

use crate::value::TryFromJs;
use crate::{Context, JsNativeError, JsResult, JsValue};

impl<K, V> TryFromJs for BTreeMap<K, V>
where
K: TryFromJs + Ord,
V: 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 BTreeMap")
.into());
};

let keys = object.__own_property_keys__(context)?;

keys.into_iter()
.map(|key| {
let js_value = object.get(key.clone(), context)?;
let js_key: JsValue = key.into();

let key = K::try_from_js(&js_key, context)?;
let value = V::try_from_js(&js_value, context)?;

Ok((key, value))
})
.collect()
}
}

impl<K, V, S> TryFromJs for HashMap<K, V, S>
where
K: TryFromJs + Ord + Hash,
V: TryFromJs,
S: std::hash::BuildHasher + Default,
{
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 BTreeMap")
.into());
};

let keys = object.__own_property_keys__(context)?;

keys.into_iter()
.map(|key| {
let js_value = object.get(key.clone(), context)?;
let js_key: JsValue = key.into();

let key = K::try_from_js(&js_key, context)?;
let value = V::try_from_js(&js_value, context)?;

Ok((key, value))
})
.collect()
}
}

0 comments on commit 109dd3d

Please sign in to comment.