rquickjs_core/value/function/
params.rs1use crate::{
2 function::{Exhaustive, Flat, FuncArg, Opt, Rest, This},
3 qjs, Ctx, FromJs, Result, Value,
4};
5use std::slice;
6
7pub 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 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 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 pub fn ctx(&self) -> &Ctx<'js> {
67 &self.ctx
68 }
69
70 pub fn function(&self) -> Value<'js> {
72 unsafe { Value::from_js_value_const(self.ctx.clone(), self.function) }
73 }
74
75 pub fn this(&self) -> Value<'js> {
77 unsafe { Value::from_js_value_const(self.ctx.clone(), self.this) }
78 }
79
80 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 pub fn len(&self) -> usize {
89 self.args.len()
90 }
91
92 pub fn is_empty(&self) -> bool {
94 self.args.is_empty()
95 }
96
97 pub fn is_constructor(&self) -> bool {
101 self.is_constructor
102 }
103
104 pub fn access(self) -> ParamsAccessor<'a, 'js> {
106 ParamsAccessor {
107 params: self,
108 offset: 0,
109 }
110 }
111}
112
113pub struct ParamsAccessor<'a, 'js> {
115 params: Params<'a, 'js>,
116 offset: usize,
117}
118
119impl<'a, 'js> ParamsAccessor<'a, 'js> {
120 pub fn ctx(&self) -> &Ctx<'js> {
122 self.params.ctx()
123 }
124
125 pub fn this(&self) -> Value<'js> {
127 self.params.this()
128 }
129
130 pub fn function(&self) -> Value<'js> {
132 self.params.function()
133 }
134
135 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 unsafe { Value::from_js_value_const(self.params.ctx.clone(), res) }
150 }
151
152 pub fn len(&self) -> usize {
154 self.params.args.len() - self.offset
155 }
156 pub fn is_empty(&self) -> bool {
158 self.len() == 0
159 }
160}
161
162pub struct ParamRequirement {
164 min: usize,
165 max: usize,
166 exhaustive: bool,
167}
168
169impl ParamRequirement {
170 pub const fn single() -> Self {
172 ParamRequirement {
173 min: 1,
174 max: 1,
175 exhaustive: false,
176 }
177 }
178
179 pub const fn exhaustive() -> Self {
182 ParamRequirement {
183 min: 0,
184 max: 0,
185 exhaustive: true,
186 }
187 }
188
189 pub const fn optional() -> Self {
191 ParamRequirement {
192 min: 0,
193 max: 1,
194 exhaustive: false,
195 }
196 }
197
198 pub const fn any() -> Self {
200 ParamRequirement {
201 min: 0,
202 max: usize::MAX,
203 exhaustive: false,
204 }
205 }
206
207 pub const fn none() -> Self {
209 ParamRequirement {
210 min: 0,
211 max: 0,
212 exhaustive: false,
213 }
214 }
215
216 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 pub fn min(&self) -> usize {
227 self.min
228 }
229
230 pub fn max(&self) -> usize {
232 self.max
233 }
234
235 pub fn is_exhaustive(&self) -> bool {
239 self.exhaustive
240 }
241}
242
243pub trait FromParam<'js>: Sized {
245 fn param_requirement() -> ParamRequirement;
247
248 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
343pub trait FromParams<'js>: Sized {
345 fn param_requirements() -> ParamRequirement;
347
348 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);