rquickjs_core/value/function/
params.rs

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