rquickjs_core/value/function/
params.rs

1use crate::{
2    function::{Exhaustive, Flat, FuncArg, Opt, Rest, This},
3    qjs, Ctx, FromJs, Result, Value,
4};
5use alloc::vec::Vec;
6use core::slice;
7
8/// A struct which contains the values a callback is called with.
9///
10/// Arguments retrieved from the JavaScript side for calling Rust functions.
11pub struct Params<'a, 'js> {
12    ctx: Ctx<'js>,
13    function: qjs::JSValue,
14    this: qjs::JSValue,
15    args: &'a [qjs::JSValue],
16    is_constructor: bool,
17}
18
19impl<'a, 'js> Params<'a, 'js> {
20    /// Create params from the arguments returned by the class callback.
21    pub(crate) unsafe fn from_ffi_class(
22        ctx: *mut qjs::JSContext,
23        function: qjs::JSValue,
24        this: qjs::JSValue,
25        argc: qjs::c_int,
26        argv: *mut qjs::JSValue,
27        _flags: qjs::c_int,
28    ) -> Self {
29        let args = if argv.is_null() {
30            assert_eq!(
31                argc, 0,
32                "got a null pointer from quickjs for a non-zero number of args"
33            );
34            [].as_slice()
35        } else {
36            let argc = usize::try_from(argc).expect("invalid argument number");
37            slice::from_raw_parts(argv, argc)
38        };
39
40        Self {
41            ctx: Ctx::from_ptr(ctx),
42            function,
43            this,
44            args,
45            is_constructor: false,
46        }
47    }
48
49    /// Checks if the parameters fit the param num requirements.
50    pub fn check_params(&self, num: ParamRequirement) -> Result<()> {
51        if self.args.len() < num.min {
52            return Err(crate::Error::MissingArgs {
53                expected: num.min,
54                given: self.args.len(),
55            });
56        }
57        if num.exhaustive && self.args.len() > num.max {
58            return Err(crate::Error::TooManyArgs {
59                expected: num.max,
60                given: self.args.len(),
61            });
62        }
63        Ok(())
64    }
65
66    /// Returns the context associated with call.
67    pub fn ctx(&self) -> &Ctx<'js> {
68        &self.ctx
69    }
70
71    /// Returns the value on which this function called. i.e. in `bla.foo()` the `foo` value.
72    pub fn function(&self) -> Value<'js> {
73        unsafe { Value::from_js_value_const(self.ctx.clone(), self.function) }
74    }
75
76    /// Returns the this on which this function called. i.e. in `bla.foo()` the `bla` value.
77    pub fn this(&self) -> Value<'js> {
78        unsafe { Value::from_js_value_const(self.ctx.clone(), self.this) }
79    }
80
81    /// Returns the argument at a given index..
82    pub fn arg(&self, index: usize) -> Option<Value<'js>> {
83        self.args
84            .get(index)
85            .map(|arg| unsafe { Value::from_js_value_const(self.ctx.clone(), *arg) })
86    }
87
88    /// Returns the number of arguments.
89    pub fn len(&self) -> usize {
90        self.args.len()
91    }
92
93    /// Returns if there are no arguments
94    pub fn is_empty(&self) -> bool {
95        self.args.is_empty()
96    }
97
98    /// Returns if the function is called as a constructor.
99    ///
100    /// If it is the value return by `this` is actually the `new.target` value.
101    pub fn is_constructor(&self) -> bool {
102        self.is_constructor
103    }
104
105    /// Turns the params into an accessor object for extracting the arguments.
106    pub fn access(self) -> ParamsAccessor<'a, 'js> {
107        ParamsAccessor {
108            params: self,
109            offset: 0,
110        }
111    }
112}
113
114/// Accessor to parameters used for retrieving arguments in order one at the time.
115pub struct ParamsAccessor<'a, 'js> {
116    params: Params<'a, 'js>,
117    offset: usize,
118}
119
120impl<'a, 'js> ParamsAccessor<'a, 'js> {
121    /// Returns the context associated with the params.
122    pub fn ctx(&self) -> &Ctx<'js> {
123        self.params.ctx()
124    }
125
126    /// Returns this value of call from which the params originate.
127    pub fn this(&self) -> Value<'js> {
128        self.params.this()
129    }
130
131    /// Returns the value on which this function called. i.e. in `bla.foo()` the `foo` value.
132    pub fn function(&self) -> Value<'js> {
133        self.params.function()
134    }
135
136    /// Returns the next arguments.
137    ///
138    /// Each call to this function returns a different argument
139    ///
140    /// # Panic
141    /// This function panics if it is called more times then there are arguments.
142    pub fn arg(&mut self) -> Value<'js> {
143        assert!(
144            self.offset < self.params.args.len(),
145            "arg called too many times"
146        );
147        let res = self.params.args[self.offset];
148        self.offset += 1;
149        // TODO: figure out ownership
150        unsafe { Value::from_js_value_const(self.params.ctx.clone(), res) }
151    }
152
153    /// returns the number of arguments remaining
154    pub fn len(&self) -> usize {
155        self.params.args.len() - self.offset
156    }
157    /// returns whether there are any arguments remaining.
158    pub fn is_empty(&self) -> bool {
159        self.len() == 0
160    }
161}
162
163/// A struct encoding the requirements of a parameter set.
164pub struct ParamRequirement {
165    min: usize,
166    max: usize,
167    exhaustive: bool,
168}
169
170impl ParamRequirement {
171    /// Returns the requirement of a single required parameter
172    pub const fn single() -> Self {
173        ParamRequirement {
174            min: 1,
175            max: 1,
176            exhaustive: false,
177        }
178    }
179
180    /// Makes the requirements exhaustive i.e. the parameter set requires that the function is
181    /// called with no arguments than parameters
182    pub const fn exhaustive() -> Self {
183        ParamRequirement {
184            min: 0,
185            max: 0,
186            exhaustive: true,
187        }
188    }
189
190    /// Returns the requirements for a single optional parameter
191    pub const fn optional() -> Self {
192        ParamRequirement {
193            min: 0,
194            max: 1,
195            exhaustive: false,
196        }
197    }
198
199    /// Returns the requirements for a any number of parameters
200    pub const fn any() -> Self {
201        ParamRequirement {
202            min: 0,
203            max: usize::MAX,
204            exhaustive: false,
205        }
206    }
207
208    /// Returns the requirements for no parameters
209    pub const fn none() -> Self {
210        ParamRequirement {
211            min: 0,
212            max: 0,
213            exhaustive: false,
214        }
215    }
216
217    /// Combine to requirements into one which covers both.
218    pub const fn combine(self, other: Self) -> ParamRequirement {
219        Self {
220            min: self.min.saturating_add(other.min),
221            max: self.max.saturating_add(other.max),
222            exhaustive: self.exhaustive || other.exhaustive,
223        }
224    }
225
226    /// Returns the minimum number of arguments for this requirement
227    pub fn min(&self) -> usize {
228        self.min
229    }
230
231    /// Returns the maximum number of arguments for this requirement
232    pub fn max(&self) -> usize {
233        self.max
234    }
235
236    /// Returns whether this function is required to be exhaustive called
237    ///
238    /// i.e. there can be no more arguments then parameters.
239    pub fn is_exhaustive(&self) -> bool {
240        self.exhaustive
241    }
242}
243
244/// A trait to extract argument values.
245pub trait FromParam<'js>: Sized {
246    /// The parameters requirements this value requires.
247    fn param_requirement() -> ParamRequirement;
248
249    /// Convert from a parameter value.
250    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self>;
251}
252
253impl<'js, T: FromJs<'js>> FromParam<'js> for T {
254    fn param_requirement() -> ParamRequirement {
255        ParamRequirement::single()
256    }
257
258    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
259        let ctx = params.ctx().clone();
260        T::from_js(&ctx, params.arg())
261    }
262}
263
264impl<'js> FromParam<'js> for Ctx<'js> {
265    fn param_requirement() -> ParamRequirement {
266        ParamRequirement::none()
267    }
268
269    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
270        Ok(params.ctx().clone())
271    }
272}
273
274impl<'js, T: FromJs<'js>> FromParam<'js> for Opt<T> {
275    fn param_requirement() -> ParamRequirement {
276        ParamRequirement::optional()
277    }
278
279    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
280        if !params.is_empty() {
281            let ctx = params.ctx().clone();
282            Ok(Opt(Some(T::from_js(&ctx, params.arg())?)))
283        } else {
284            Ok(Opt(None))
285        }
286    }
287}
288
289impl<'js, T: FromJs<'js>> FromParam<'js> for This<T> {
290    fn param_requirement() -> ParamRequirement {
291        ParamRequirement::any()
292    }
293
294    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
295        T::from_js(params.ctx(), params.this()).map(This)
296    }
297}
298
299impl<'js, T: FromJs<'js>> FromParam<'js> for FuncArg<T> {
300    fn param_requirement() -> ParamRequirement {
301        ParamRequirement::any()
302    }
303
304    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
305        T::from_js(params.ctx(), params.function()).map(FuncArg)
306    }
307}
308
309impl<'js, T: FromJs<'js>> FromParam<'js> for Rest<T> {
310    fn param_requirement() -> ParamRequirement {
311        ParamRequirement::any()
312    }
313
314    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
315        let mut res = Vec::with_capacity(params.len());
316        for _ in 0..params.len() {
317            let p = params.arg();
318            res.push(T::from_js(params.ctx(), p)?);
319        }
320        Ok(Rest(res))
321    }
322}
323
324impl<'js, T: FromParams<'js>> FromParam<'js> for Flat<T> {
325    fn param_requirement() -> ParamRequirement {
326        T::param_requirements()
327    }
328
329    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
330        T::from_params(params).map(Flat)
331    }
332}
333
334impl<'js> FromParam<'js> for Exhaustive {
335    fn param_requirement() -> ParamRequirement {
336        ParamRequirement::exhaustive()
337    }
338
339    fn from_param<'a>(_params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
340        Ok(Exhaustive)
341    }
342}
343
344/// A trait to extract a tuple of argument values.
345pub trait FromParams<'js>: Sized {
346    /// The parameters requirements this value requires.
347    fn param_requirements() -> ParamRequirement;
348
349    /// Convert from a parameter value.
350    fn from_params<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self>;
351}
352
353macro_rules! impl_from_params{
354    ($($t:ident),*) => {
355        #[allow(non_snake_case)]
356        impl<'js $(,$t)*> FromParams<'js> for ($($t,)*)
357        where
358            $($t : FromParam<'js>,)*
359        {
360
361            fn param_requirements() -> ParamRequirement{
362                ParamRequirement::none()
363                    $(.combine($t::param_requirement()))*
364            }
365
366            fn from_params<'a>(_args: &mut ParamsAccessor<'a,'js>) -> Result<Self>{
367                Ok((
368                    $($t::from_param(_args)?,)*
369                ))
370            }
371        }
372    };
373}
374
375impl_from_params!();
376impl_from_params!(A);
377impl_from_params!(A, B);
378impl_from_params!(A, B, C);
379impl_from_params!(A, B, C, D);
380impl_from_params!(A, B, C, D, E);
381impl_from_params!(A, B, C, D, E, F);
382impl_from_params!(A, B, C, D, E, F, G);