use crate::{
JSContext, JSFunc, JSObject, JSObjectOps, JSSymbol, JSValue, JSValueConversion, JSValueImpl,
};
#[derive(Clone)]
pub enum PropertyKey<'a, V: JSValueImpl> {
Int32(i32),
Uint32(u32),
Int64(i64),
Uint64(u64),
Str(&'a str),
Symbol(JSSymbol<V>),
}
impl<V: JSValueImpl> From<i32> for PropertyKey<'_, V> {
fn from(value: i32) -> Self {
PropertyKey::Int32(value)
}
}
impl<V: JSValueImpl> From<u32> for PropertyKey<'_, V> {
fn from(value: u32) -> Self {
PropertyKey::Uint32(value)
}
}
impl<V: JSValueImpl> From<i64> for PropertyKey<'_, V> {
fn from(value: i64) -> Self {
PropertyKey::Int64(value)
}
}
impl<V: JSValueImpl> From<u64> for PropertyKey<'_, V> {
fn from(value: u64) -> Self {
PropertyKey::Uint64(value)
}
}
impl<V: JSValueImpl> From<JSSymbol<V>> for PropertyKey<'_, V> {
fn from(value: JSSymbol<V>) -> Self {
PropertyKey::Symbol(value)
}
}
impl<'a, 'b: 'a, V: JSValueImpl> From<&'b str> for PropertyKey<'a, V> {
fn from(value: &'b str) -> Self {
PropertyKey::Str(value)
}
}
impl<V: JSValueImpl> PropertyKey<'_, V> {
pub(crate) fn into_value(self, context: &JSContext<V::Context>) -> V
where
V: JSValueConversion,
{
let ctx = context.as_ref();
match self {
Self::Int32(i) => (ctx, i).into(),
Self::Uint32(i) => (ctx, i).into(),
Self::Int64(i) => (ctx, i).into(),
Self::Uint64(i) => (ctx, i).into(),
Self::Str(s) => (ctx, s).into(),
Self::Symbol(s) => JSSymbol::into_value(s),
}
}
}
impl<V: JSObjectOps> std::fmt::Display for PropertyKey<'_, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PropertyKey::Int32(i) => write!(f, "{}", i),
PropertyKey::Uint32(i) => write!(f, "{}", i),
PropertyKey::Int64(i) => write!(f, "{}", i),
PropertyKey::Uint64(i) => write!(f, "{}", i),
PropertyKey::Str(s) => write!(f, "{}", s),
PropertyKey::Symbol(s) => write!(
f,
"Symbol({})",
s.descripiton().unwrap_or_else(|_| "".to_string())
),
}
}
}
#[derive(Default, Clone, Copy)]
pub struct PropertyAttributes(u32);
impl PropertyAttributes {
const WRITABLE: u32 = 1;
const ENUMERABLE: u32 = 1 << 1;
const CONFIGURABLE: u32 = 1 << 2;
const HAS_VALUE: u32 = 1 << 3;
const HAS_GET: u32 = 1 << 4;
const HAS_SET: u32 = 1 << 5;
pub fn is_writable(&self) -> bool {
self.0 & Self::WRITABLE != 0
}
pub fn is_enumerable(&self) -> bool {
self.0 & Self::ENUMERABLE != 0
}
pub fn is_configurable(&self) -> bool {
self.0 & Self::CONFIGURABLE != 0
}
pub fn has_value(&self) -> bool {
self.0 & Self::HAS_VALUE != 0
}
pub fn has_get(&self) -> bool {
self.0 & Self::HAS_GET != 0
}
pub fn has_set(&self) -> bool {
self.0 & Self::HAS_SET != 0
}
}
pub struct PropertyDescriptor<V: JSValueImpl> {
value: Option<V>,
getter: Option<JSFunc<V>>,
setter: Option<JSFunc<V>>,
attributes: PropertyAttributes,
}
impl<V> PropertyDescriptor<V>
where
V: JSObjectOps,
{
#[must_use]
pub fn builder() -> Self {
Self {
value: None,
getter: None,
setter: None,
attributes: PropertyAttributes::default(),
}
}
#[must_use]
pub fn value(mut self, value: JSValue<V>) -> Self {
self.value = Some(value.into_value());
self
}
#[must_use]
pub fn getter(mut self, getter: JSFunc<V>) -> Self {
self.getter = Some(getter);
self
}
#[must_use]
pub fn setter(mut self, setter: JSFunc<V>) -> Self {
self.setter = Some(setter);
self
}
#[must_use]
pub fn writable(mut self, status: bool) -> Self {
if status {
self.attributes.0 |= PropertyAttributes::WRITABLE;
} else {
self.attributes.0 &= !PropertyAttributes::WRITABLE;
}
self
}
#[must_use]
pub fn enumerable(mut self, status: bool) -> Self {
if status {
self.attributes.0 |= PropertyAttributes::ENUMERABLE;
} else {
self.attributes.0 &= !PropertyAttributes::ENUMERABLE;
}
self
}
#[must_use]
pub fn configurable(mut self, status: bool) -> Self {
if status {
self.attributes.0 |= PropertyAttributes::CONFIGURABLE;
} else {
self.attributes.0 &= !PropertyAttributes::CONFIGURABLE;
}
self
}
pub fn apply_to<K>(mut self, obj: &JSObject<V>, k: K)
where
K: for<'a> Into<PropertyKey<'a, V>>,
V: JSObjectOps,
{
let ctx = &obj.get_ctx();
let undefined = V::create_undefined(ctx.as_ref());
let value = self
.value
.inspect(|_| self.attributes.0 |= PropertyAttributes::HAS_VALUE)
.unwrap_or(undefined.clone());
let getter = self
.getter
.map(|g| {
self.attributes.0 |= PropertyAttributes::HAS_GET;
g.into_value()
})
.unwrap_or(undefined.clone());
let setter = self
.setter
.map(|s| {
self.attributes.0 |= PropertyAttributes::HAS_SET;
s.into_value()
})
.unwrap_or(undefined.clone());
let key = k.into().into_value(ctx);
obj.as_value()
.define_property(key, value, getter, setter, self.attributes);
}
}