rquickjs_core/value/function/
params.rs1use crate::{
2 function::{Exhaustive, Flat, FuncArg, Opt, Rest, This},
3 qjs, Ctx, FromJs, Result, Value,
4};
5use alloc::vec::Vec;
6use core::slice;
7
8pub 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 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 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 pub fn ctx(&self) -> &Ctx<'js> {
68 &self.ctx
69 }
70
71 pub fn function(&self) -> Value<'js> {
73 unsafe { Value::from_js_value_const(self.ctx.clone(), self.function) }
74 }
75
76 pub fn this(&self) -> Value<'js> {
78 unsafe { Value::from_js_value_const(self.ctx.clone(), self.this) }
79 }
80
81 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 pub fn len(&self) -> usize {
90 self.args.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.args.is_empty()
96 }
97
98 pub fn is_constructor(&self) -> bool {
102 self.is_constructor
103 }
104
105 pub fn access(self) -> ParamsAccessor<'a, 'js> {
107 ParamsAccessor {
108 params: self,
109 offset: 0,
110 }
111 }
112}
113
114pub struct ParamsAccessor<'a, 'js> {
116 params: Params<'a, 'js>,
117 offset: usize,
118}
119
120impl<'a, 'js> ParamsAccessor<'a, 'js> {
121 pub fn ctx(&self) -> &Ctx<'js> {
123 self.params.ctx()
124 }
125
126 pub fn this(&self) -> Value<'js> {
128 self.params.this()
129 }
130
131 pub fn function(&self) -> Value<'js> {
133 self.params.function()
134 }
135
136 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 unsafe { Value::from_js_value_const(self.params.ctx.clone(), res) }
151 }
152
153 pub fn len(&self) -> usize {
155 self.params.args.len() - self.offset
156 }
157 pub fn is_empty(&self) -> bool {
159 self.len() == 0
160 }
161}
162
163pub struct ParamRequirement {
165 min: usize,
166 max: usize,
167 exhaustive: bool,
168}
169
170impl ParamRequirement {
171 pub const fn single() -> Self {
173 ParamRequirement {
174 min: 1,
175 max: 1,
176 exhaustive: false,
177 }
178 }
179
180 pub const fn exhaustive() -> Self {
183 ParamRequirement {
184 min: 0,
185 max: 0,
186 exhaustive: true,
187 }
188 }
189
190 pub const fn optional() -> Self {
192 ParamRequirement {
193 min: 0,
194 max: 1,
195 exhaustive: false,
196 }
197 }
198
199 pub const fn any() -> Self {
201 ParamRequirement {
202 min: 0,
203 max: usize::MAX,
204 exhaustive: false,
205 }
206 }
207
208 pub const fn none() -> Self {
210 ParamRequirement {
211 min: 0,
212 max: 0,
213 exhaustive: false,
214 }
215 }
216
217 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 pub fn min(&self) -> usize {
228 self.min
229 }
230
231 pub fn max(&self) -> usize {
233 self.max
234 }
235
236 pub fn is_exhaustive(&self) -> bool {
240 self.exhaustive
241 }
242}
243
244pub trait FromParam<'js>: Sized {
246 fn param_requirement() -> ParamRequirement;
248
249 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
344pub trait FromParams<'js>: Sized {
346 fn param_requirements() -> ParamRequirement;
348
349 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);