use crate::{
builtins::{
iterable::IteratorHint,
map::{add_entries_from_iterable, ordered_map::OrderedMap},
Map,
},
error::JsNativeError,
js_string,
object::{JsFunction, JsMapIterator, JsObject},
value::TryFromJs,
Context, JsResult, JsValue,
};
use boa_gc::{Finalize, Trace};
use std::ops::Deref;
#[derive(Debug, Clone, Trace, Finalize)]
pub struct JsMap {
inner: JsObject,
}
impl JsMap {
#[inline]
pub fn new(context: &mut Context) -> Self {
let map = Self::create_map(context);
Self { inner: map }
}
pub fn from_js_iterable(iterable: &JsValue, context: &mut Context) -> JsResult<Self> {
let map = Self::create_map(context);
let adder = map
.get(js_string!("set"), context)?
.as_function()
.ok_or_else(|| {
JsNativeError::typ().with_message("property `set` on new `Map` must be callable")
})?;
let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?;
Ok(Self { inner: map })
}
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is::<OrderedMap<JsValue>>() {
Ok(Self { inner: object })
} else {
Err(JsNativeError::typ()
.with_message("object is not a Map")
.into())
}
}
fn create_map(context: &mut Context) -> JsObject {
let prototype = context.intrinsics().constructors().map().prototype();
JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
<OrderedMap<JsValue>>::new(),
)
}
#[inline]
pub fn entries(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::entries(&self.inner.clone().into(), &[], context)?
.get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone())
}
#[inline]
pub fn keys(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::keys(&self.inner.clone().into(), &[], context)?
.get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone())
}
pub fn set<K, V>(&self, key: K, value: V, context: &mut Context) -> JsResult<JsValue>
where
K: Into<JsValue>,
V: Into<JsValue>,
{
Map::set(
&self.inner.clone().into(),
&[key.into(), value.into()],
context,
)
}
#[inline]
pub fn get_size(&self, context: &mut Context) -> JsResult<JsValue> {
Map::get_size(&self.inner.clone().into(), &[], context)
}
pub fn delete<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Map::delete(&self.inner.clone().into(), &[key.into()], context)
}
pub fn get<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Map::get(&self.inner.clone().into(), &[key.into()], context)
}
#[inline]
pub fn clear(&self, context: &mut Context) -> JsResult<JsValue> {
Map::clear(&self.inner.clone().into(), &[], context)
}
pub fn has<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Map::has(&self.inner.clone().into(), &[key.into()], context)
}
#[inline]
pub fn for_each(
&self,
callback: JsFunction,
this_arg: JsValue,
context: &mut Context,
) -> JsResult<JsValue> {
Map::for_each(
&self.inner.clone().into(),
&[callback.into(), this_arg],
context,
)
}
#[inline]
pub fn for_each_native<F>(&self, f: F) -> JsResult<()>
where
F: FnMut(JsValue, JsValue) -> JsResult<()>,
{
let this = self.inner.clone().into();
Map::for_each_native(&this, f)
}
#[inline]
pub fn values(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::values(&self.inner.clone().into(), &[], context)?
.get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone())
}
}
impl From<JsMap> for JsObject {
#[inline]
fn from(o: JsMap) -> Self {
o.inner.clone()
}
}
impl From<JsMap> for JsValue {
#[inline]
fn from(o: JsMap) -> Self {
o.inner.clone().into()
}
}
impl Deref for JsMap {
type Target = JsObject;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl TryFromJs for JsMap {
fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Map object")
.into()),
}
}
}