use crate::{
function::{Opt, Rest, This},
qjs, Ctx, Error, FromJs, Result, Value,
};
use std::{ops::Range, slice};
pub struct Input<'js> {
ctx: Ctx<'js>,
this: qjs::JSValue,
args: &'js [qjs::JSValue],
}
impl<'js> Input<'js> {
#[inline]
pub(crate) unsafe fn new_raw(
ctx: *mut qjs::JSContext,
this: qjs::JSValue,
argc: qjs::c_int,
argv: *mut qjs::JSValue,
) -> Self {
let ctx = Ctx::from_ptr(ctx);
let args = slice::from_raw_parts(argv, argc as _);
Self { ctx, this, args }
}
#[inline]
pub fn access(&self) -> InputAccessor<'_, 'js> {
InputAccessor {
input: self,
arg: 0,
}
}
#[inline]
pub fn len(&self) -> usize {
self.args.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.args.is_empty()
}
}
pub struct InputAccessor<'i, 'js> {
input: &'i Input<'js>,
arg: usize,
}
impl<'i, 'js> InputAccessor<'i, 'js> {
#[inline]
pub fn ctx(&self) -> Ctx<'js> {
self.input.ctx
}
#[inline]
pub fn this<T>(&self) -> Result<T>
where
T: FromJs<'js>,
{
let value = unsafe { Value::from_js_value_const(self.input.ctx, self.input.this) };
T::from_js(self.input.ctx, value)
}
#[inline]
pub fn len(&self) -> usize {
self.input.args.len() - self.arg
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn arg<T>(&mut self) -> Result<T>
where
T: FromJs<'js>,
{
if self.arg < self.input.args.len() {
let value = self.input.args[self.arg];
self.arg += 1;
let value = unsafe { Value::from_js_value_const(self.input.ctx, value) };
T::from_js(self.input.ctx, value)
} else {
Err(Error::new_from_js_message(
"uninitialized",
"value",
"out of range",
))
}
}
#[inline]
pub fn args<T>(&mut self) -> Result<Vec<T>>
where
T: FromJs<'js>,
{
self.input.args[self.arg..]
.iter()
.map(|value| {
let value = unsafe { Value::from_js_value_const(self.input.ctx, *value) };
T::from_js(self.input.ctx, value)
})
.collect::<Result<Vec<_>>>()
}
#[inline]
pub fn get<T>(&mut self) -> Result<T>
where
T: FromInput<'js>,
{
T::from_input(self)
}
}
pub trait FromInput<'js>: Sized {
fn num_args() -> Range<usize>;
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self>;
}
impl<'js> FromInput<'js> for Ctx<'js> {
fn num_args() -> Range<usize> {
0..0
}
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self> {
Ok(accessor.ctx())
}
}
impl<'js, T> FromInput<'js> for This<T>
where
T: FromJs<'js>,
{
fn num_args() -> Range<usize> {
0..0
}
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self> {
accessor.this().map(Self)
}
}
impl<'js, T> FromInput<'js> for Opt<T>
where
T: FromJs<'js>,
{
fn num_args() -> Range<usize> {
0..1
}
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self> {
if !accessor.is_empty() {
accessor.arg().map(Self)
} else {
Ok(Self(None))
}
}
}
impl<'js, T> FromInput<'js> for Rest<T>
where
T: FromJs<'js>,
{
fn num_args() -> Range<usize> {
0..usize::MAX
}
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self> {
accessor.args().map(Self)
}
}
impl<'js, T> FromInput<'js> for T
where
T: FromJs<'js>,
{
fn num_args() -> Range<usize> {
1..1
}
fn from_input<'i>(accessor: &mut InputAccessor<'i, 'js>) -> Result<Self> {
accessor.arg()
}
}