Files
sausage/host/cred-script/vendor/llrt_utils_watts/src/object.rs
T

177 lines
4.9 KiB
Rust

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::collections::BTreeMap;
use rquickjs::{
atom::PredefinedAtom, function::IntoJsFunc, prelude::Func, Array, Coerced, Ctx, Error,
Exception, FromJs, IntoAtom, IntoJs, Object, Result, Symbol, Undefined, Value,
};
use crate::primordials::{BasePrimordials, Primordial};
pub trait ObjectExt<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>>;
fn get_required<K: AsRef<str>, V: FromJs<'js>>(
&self,
k: K,
object_name: &'static str,
) -> Result<V>;
fn into_object_or_throw(self, ctx: &Ctx<'js>, object_name: &'static str)
-> Result<Object<'js>>;
}
impl<'js> ObjectExt<'js> for Object<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js> + Sized>(
&self,
k: K,
) -> Result<Option<V>> {
self.get::<K, Option<V>>(k)
}
fn get_required<K: AsRef<str>, V: FromJs<'js>>(
&self,
k: K,
object_name: &'static str,
) -> Result<V> {
let k = k.as_ref();
self.get::<&str, Option<V>>(k)?.ok_or_else(|| {
Exception::throw_type(
self.ctx(),
&[object_name, " '", k, "' property required"].concat(),
)
})
}
fn into_object_or_throw(self, _: &Ctx<'js>, _: &'static str) -> Result<Object<'js>> {
Ok(self)
}
}
impl<'js> ObjectExt<'js> for Value<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>> {
if let Some(obj) = self.as_object() {
return obj.get_optional(k);
}
Ok(None)
}
fn get_required<K: AsRef<str>, V: FromJs<'js>>(
&self,
k: K,
object_name: &'static str,
) -> Result<V> {
self.as_object()
.ok_or_else(|| not_a_object_error(self.ctx(), object_name))?
.get_required(k, object_name)
}
fn into_object_or_throw(
self,
ctx: &Ctx<'js>,
object_name: &'static str,
) -> Result<Object<'js>> {
self.into_object()
.ok_or_else(|| not_a_object_error(ctx, object_name))
}
}
pub fn not_a_object_error(ctx: &Ctx<'_>, object_name: &str) -> Error {
Exception::throw_type(ctx, &[object_name, " is not an object"].concat())
}
pub trait CreateSymbol<'js> {
fn for_description(ctx: &Ctx<'js>, description: &str) -> Result<Symbol<'js>>;
}
impl<'js> CreateSymbol<'js> for Symbol<'js> {
fn for_description(ctx: &Ctx<'js>, description: &str) -> Result<Symbol<'js>> {
BasePrimordials::get(ctx)?
.function_symbol_for
.call((description,))
}
}
pub struct Proxy<'js> {
target: Value<'js>,
options: Object<'js>,
}
impl<'js> IntoJs<'js> for Proxy<'js> {
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
BasePrimordials::get(ctx)?
.constructor_proxy
.construct::<_, Value>((self.target, self.options))
}
}
impl<'js> Proxy<'js> {
pub fn new(ctx: Ctx<'js>) -> Result<Self> {
let options = Object::new(ctx.clone())?;
Ok(Self {
target: Undefined.into_value(ctx),
options,
})
}
pub fn with_target(ctx: Ctx<'js>, target: Value<'js>) -> Result<Self> {
let options = Object::new(ctx)?;
Ok(Self { target, options })
}
pub fn setter<T, P>(&self, setter: Func<T, P>) -> Result<()>
where
T: IntoJsFunc<'js, P> + 'js,
{
self.options.set(PredefinedAtom::Setter, setter)?;
Ok(())
}
pub fn getter<T, P>(&self, getter: Func<T, P>) -> Result<()>
where
T: IntoJsFunc<'js, P> + 'js,
{
self.options.set(PredefinedAtom::Getter, getter)?;
Ok(())
}
}
pub fn map_to_entries<'js, K, V, M>(ctx: &Ctx<'js>, map: M) -> Result<Array<'js>>
where
M: IntoIterator<Item = (K, V)>,
K: IntoJs<'js>,
V: IntoJs<'js>,
{
let array = Array::new(ctx.clone())?;
for (idx, (key, value)) in map.into_iter().enumerate() {
let entry = Array::new(ctx.clone())?;
entry.set(0, key)?;
entry.set(1, value)?;
array.set(idx, entry)?;
}
Ok(array)
}
pub fn array_to_btree_map<'js>(
ctx: &Ctx<'js>,
array: Array<'js>,
) -> Result<BTreeMap<String, Coerced<String>>> {
let value = object_from_entries(ctx, array)?;
let value = value.into_value();
BTreeMap::from_js(ctx, value)
}
pub fn object_from_entries<'js>(ctx: &Ctx<'js>, array: Array<'js>) -> Result<Object<'js>> {
let obj = Object::new(ctx.clone())?;
for value in array.into_iter().flatten() {
if let Some(entry) = value.as_array() {
if let Ok(key) = entry.get::<Value>(0) {
if let Ok(value) = entry.get::<Value>(1) {
let _ = obj.set(key, value); //ignore result of failed
}
}
}
}
Ok(obj)
}