use crate::{
FromJSValue, JSArrayOps, JSClass, JSContext, JSContextImpl, JSObject, JSObjectOps, JSResult,
JSTypeOf, JSValue, JSValueImpl,
};
use std::cell::{Ref, RefMut};
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::ops::Deref;
pub struct ParamsAccessor<'a, V: JSValueImpl> {
ctx: &'a JSContext<V::Context>,
this: V,
args: VecDeque<V>,
is_last_param: bool,
}
impl<'a, V: JSValueImpl> ParamsAccessor<'a, V> {
pub fn new(ctx: &'a JSContext<V::Context>, this: V, args: Vec<V>) -> Self {
Self {
ctx,
this,
args: args.into(),
is_last_param: false,
}
}
fn set_last_param(&mut self, is_last: bool) {
self.is_last_param = is_last;
}
fn next_arg(&mut self) -> Option<V> {
self.args.pop_front()
}
pub fn get_this(&self) -> V {
self.this.clone()
}
pub(crate) fn context(&self) -> &JSContext<V::Context> {
self.ctx
}
pub(crate) fn args_len(&self) -> usize {
self.args.len()
}
}
pub struct This<T>(pub T);
pub struct ThisMut<T, V: JSValueImpl>(pub(crate) JSObject<V>, PhantomData<T>);
pub struct JSClassRef<T, V: JSValueImpl>(JSObject<V>, PhantomData<T>);
pub struct Optional<T>(pub Option<T>);
pub struct Rest<T>(pub Vec<T>);
impl<T> Deref for This<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T, V> ThisMut<T, V>
where
V: JSObjectOps,
T: JSClass<V>,
{
pub fn object(&self) -> JSObject<V> {
self.0.clone()
}
pub fn borrow_mut(&self) -> JSResult<RefMut<'_, T>> {
self.0.borrow_mut::<T>()
}
}
impl<T, V> JSClassRef<T, V>
where
V: JSObjectOps,
T: JSClass<V>,
{
pub fn object(&self) -> JSObject<V> {
self.0.clone()
}
pub fn borrow(&self) -> JSResult<Ref<'_, T>> {
self.0.borrow::<T>()
}
pub fn borrow_mut(&self) -> JSResult<RefMut<'_, T>> {
self.0.borrow_mut::<T>()
}
}
impl<T, V> Clone for JSClassRef<T, V>
where
V: JSObjectOps,
{
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T, V> Deref for JSClassRef<T, V>
where
V: JSValueImpl,
{
type Target = JSObject<V>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Deref for Optional<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Deref for Rest<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait FromParams<V: JSValueImpl>: Sized {
fn from_params(accessor: &mut ParamsAccessor<V>) -> JSResult<Self>;
fn param_requirements() -> ParamRequirement;
}
pub struct ParamRequirement {
required_count: usize,
exhaustive: bool,
}
impl ParamRequirement {
pub fn required_count(&self) -> usize {
self.required_count
}
const fn single() -> Self {
Self {
required_count: 1,
exhaustive: true,
}
}
const fn optional() -> Self {
Self {
required_count: 0,
exhaustive: false,
}
}
const fn any() -> Self {
Self {
required_count: 0,
exhaustive: false,
}
}
}
pub trait ParameterKind {
fn param_requirement() -> ParamRequirement;
}
pub struct Regular<T>(PhantomData<T>);
impl<T> ParameterKind for Regular<T> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::single()
}
}
pub struct ThisKind<T>(PhantomData<T>);
impl<T> ParameterKind for ThisKind<T> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::any()
}
}
pub struct ThisMutKind<T>(PhantomData<T>);
impl<T> ParameterKind for ThisMutKind<T> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::any()
}
}
pub struct OptionalKind<T>(PhantomData<T>);
impl<T> ParameterKind for OptionalKind<T> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::optional()
}
}
pub struct RestKind<T>(PhantomData<T>);
impl<T> ParameterKind for RestKind<T> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::any()
}
}
impl<C: JSContextImpl> ParameterKind for JSContext<C> {
fn param_requirement() -> ParamRequirement {
ParamRequirement::any()
}
}
pub trait GetParam<V: JSValueImpl> {
type Kind: ParameterKind;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self>
where
Self: Sized;
}
impl<T, V> GetParam<V> for T
where
V: JSValueImpl,
T: FromJSValue<V> + Sized,
T: JSParameterType,
{
type Kind = Regular<T>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let value = accessor.next_arg().unwrap(); T::from_js_value(accessor.ctx, JSValue::from_raw(accessor.ctx, value))
}
}
impl<V: JSValueImpl> GetParam<V> for JSContext<V::Context> {
type Kind = JSContext<V::Context>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let ctx = accessor.context();
Ok(ctx.clone())
}
}
impl<T, V> GetParam<V> for This<T>
where
V: JSValueImpl,
T: FromJSValue<V> + JSParameterType,
{
type Kind = ThisKind<T>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let value = accessor.get_this();
let val = T::from_js_value(accessor.ctx, JSValue::from_raw(accessor.ctx, value))?;
Ok(Self(val))
}
}
impl<T, V> GetParam<V> for ThisMut<T, V>
where
V: JSObjectOps,
T: JSClass<V>,
{
type Kind = ThisMutKind<T>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let value = accessor.get_this();
let obj =
JSObject::from_js_value(accessor.context(), JSValue::from_raw(accessor.ctx, value))?;
if !crate::Class::instance_of::<T>(&obj) {
return Err(crate::HostError::new(
crate::error::E_TYPE,
format!("Not instance of {}", std::any::type_name::<T>()),
)
.with_name("TypeError")
.into());
}
Ok(ThisMut(obj, PhantomData))
}
}
impl<T, V> GetParam<V> for Optional<T>
where
V: JSValueImpl,
T: FromJSValue<V>,
{
type Kind = OptionalKind<T>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
match accessor.next_arg() {
Some(v) => T::from_js_value(accessor.ctx, JSValue::from_raw(accessor.ctx, v))
.map(|t| Optional(Some(t))),
None => Ok(Optional(None)),
}
}
}
impl<T, V> GetParam<V> for Rest<T>
where
V: JSValueImpl,
T: FromJSValue<V>,
{
type Kind = RestKind<T>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let mut values = Vec::new();
if accessor.is_last_param {
while let Some(value) = accessor.next_arg() {
values.push(T::from_js_value(
accessor.ctx,
JSValue::from_raw(accessor.ctx, value),
)?);
}
}
Ok(Rest(values))
}
}
impl<T, V> GetParam<V> for Vec<T>
where
V: JSValueImpl + JSTypeOf + JSObjectOps + JSArrayOps,
T: FromJSValue<V>,
{
type Kind = Regular<Vec<T>>;
fn get_param(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let value = accessor.next_arg().unwrap(); <Vec<T> as FromJSValue<V>>::from_js_value(
accessor.ctx,
JSValue::from_raw(accessor.ctx, value),
)
}
}
pub trait JSParameterType {}
impl JSParameterType for () {}
impl JSParameterType for i8 {}
impl JSParameterType for u8 {}
impl JSParameterType for i16 {}
impl JSParameterType for u16 {}
impl JSParameterType for i32 {}
impl JSParameterType for u32 {}
impl JSParameterType for i64 {}
impl JSParameterType for u64 {}
impl JSParameterType for f32 {}
impl JSParameterType for f64 {}
impl JSParameterType for bool {}
impl JSParameterType for String {}
impl JSParameterType for isize {}
impl JSParameterType for usize {}
impl JSParameterType for &str {}
impl<T> JSParameterType for Option<T> where T: JSParameterType {}
impl<T, V> FromJSValue<V> for JSClassRef<T, V>
where
V: JSObjectOps,
T: JSClass<V>,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
let obj = JSObject::from_js_value(ctx, value)?;
if !crate::Class::instance_of::<T>(&obj) {
return Err(crate::HostError::new(
crate::error::E_TYPE,
format!("Not instance of {}", std::any::type_name::<T>()),
)
.with_name("TypeError")
.into());
}
Ok(Self(obj, PhantomData))
}
}
impl<T, V> JSParameterType for JSClassRef<T, V> where V: JSValueImpl {}
macro_rules! impl_from_params {
($($T:ident),*) => {
impl<V: JSValueImpl, $($T,)*> FromParams<V> for ($($T,)*)
where
$($T: GetParam<V>,)*
{
#[allow(unused_variables)]
fn from_params(accessor: &mut ParamsAccessor<V>) -> JSResult<Self> {
let param_count = count_idents!($($T),*);
#[allow(unused_mut)]
let mut current_param = 0;
Ok(($(
{
current_param += 1;
accessor.set_last_param(current_param == param_count);
$T::get_param(accessor)?
},
)*))
}
fn param_requirements() -> ParamRequirement {
#[allow(unused_mut)]
let mut req = ParamRequirement {
required_count: 0,
exhaustive: true,
};
$(
let param_req = <$T::Kind>::param_requirement();
req.required_count += param_req.required_count;
if !param_req.exhaustive {
req.exhaustive = false;
}
)*
req
}
}
}
}
macro_rules! count_idents {
() => { 0 };
($head:ident $(,$tail:ident)*) => { 1 + count_idents!($($tail),*) };
}
impl_from_params!();
impl_from_params!(A);
impl_from_params!(A, B);
impl_from_params!(A, B, C);
impl_from_params!(A, B, C, D);
impl_from_params!(A, B, C, D, E);
impl_from_params!(A, B, C, D, E, F);
impl_from_params!(A, B, C, D, E, F, G);
impl_from_params!(A, B, C, D, E, F, G, H);