Skip to main content

boa_engine/value/conversions/try_from_js/
collections.rs

1//! [`JsValue`] conversions for std collections.
2
3use std::collections::{BTreeMap, HashMap};
4use std::hash::Hash;
5
6use crate::object::JsMap;
7use crate::value::TryFromJs;
8use crate::{Context, JsNativeError, JsResult, JsValue};
9
10impl<K, V> TryFromJs for BTreeMap<K, V>
11where
12    K: TryFromJs + Ord,
13    V: TryFromJs,
14{
15    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {
16        let Some(object) = value.as_object() else {
17            return Err(JsNativeError::typ()
18                .with_message("cannot convert value to a BTreeMap")
19                .into());
20        };
21
22        // JsMap case
23        if let Ok(js_map) = JsMap::from_object(object.clone()) {
24            let mut map = Self::default();
25            js_map.for_each_native(|key, value| {
26                map.insert(
27                    K::try_from_js(&key, context)?,
28                    V::try_from_js(&value, context)?,
29                );
30                Ok(())
31            })?;
32            return Ok(map);
33        }
34
35        // key-valued JsObject case:
36        let keys = object.__own_property_keys__(context)?;
37
38        keys.into_iter()
39            .map(|key| {
40                let js_value = object.get(key.clone(), context)?;
41                let js_key: JsValue = key.into();
42
43                let key = K::try_from_js(&js_key, context)?;
44                let value = V::try_from_js(&js_value, context)?;
45
46                Ok((key, value))
47            })
48            .collect()
49    }
50}
51
52impl<K, V, S> TryFromJs for HashMap<K, V, S>
53where
54    K: TryFromJs + Ord + Hash,
55    V: TryFromJs,
56    S: std::hash::BuildHasher + Default,
57{
58    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {
59        let Some(object) = value.as_object() else {
60            return Err(JsNativeError::typ()
61                .with_message("cannot convert value to a BTreeMap")
62                .into());
63        };
64
65        // JsMap case
66        if let Ok(js_map) = JsMap::from_object(object.clone()) {
67            let mut map = Self::default();
68            js_map.for_each_native(|key, value| {
69                map.insert(
70                    K::try_from_js(&key, context)?,
71                    V::try_from_js(&value, context)?,
72                );
73                Ok(())
74            })?;
75            return Ok(map);
76        }
77
78        // key-valued JsObject case:
79        let keys = object.__own_property_keys__(context)?;
80
81        keys.into_iter()
82            .map(|key| {
83                let js_value = object.get(key.clone(), context)?;
84                let js_key: JsValue = key.into();
85
86                let key = K::try_from_js(&js_key, context)?;
87                let value = V::try_from_js(&js_value, context)?;
88
89                Ok((key, value))
90            })
91            .collect()
92    }
93}