quick_js/
callback.rs

1use std::{convert::TryFrom, marker::PhantomData, panic::RefUnwindSafe};
2
3use crate::value::{JsValue, ValueError};
4
5pub trait IntoCallbackResult {
6    fn into_callback_res(self) -> Result<JsValue, String>;
7}
8
9impl<T: Into<JsValue>> IntoCallbackResult for T {
10    fn into_callback_res(self) -> Result<JsValue, String> {
11        Ok(self.into())
12    }
13}
14
15impl<T: Into<JsValue>, E: std::fmt::Display> IntoCallbackResult for Result<T, E> {
16    fn into_callback_res(self) -> Result<JsValue, String> {
17        match self {
18            Ok(v) => Ok(v.into()),
19            Err(e) => Err(e.to_string()),
20        }
21    }
22}
23
24/// The Callback trait is implemented for functions/closures that can be
25/// used as callbacks in the JS runtime.
26pub trait Callback<F>: RefUnwindSafe {
27    /// The number of JS arguments required.
28    fn argument_count(&self) -> usize;
29    /// Execute the callback.
30    ///
31    /// Should return:
32    ///   - Err(_) if the JS values could not be converted
33    ///   - Ok(Err(_)) if an error ocurred while processing.
34    ///       The given error will be raised as a JS exception.
35    ///   - Ok(Ok(result)) when execution succeeded.
36    fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError>;
37}
38
39macro_rules! impl_callback {
40    (@call $len:literal $self:ident $args:ident ) => {
41        $self()
42    };
43
44    (@call $len:literal $self:ident $args:ident $( $arg:ident ),* ) => {
45        {
46            let mut iter = $args.into_iter();
47            $self(
48                $(
49                    $arg::try_from(iter.next().unwrap())?,
50                )*
51            )
52        }
53    };
54
55    [ $(  $len:literal : ( $( $arg:ident, )* ), )* ] => {
56        $(
57
58            impl<
59                $( $arg, )*
60                R,
61                F,
62            > Callback<PhantomData<(
63                $( &$arg, )*
64                &R,
65                &F,
66            )>> for F
67            where
68                $( $arg: TryFrom<JsValue, Error = ValueError>, )*
69                R: IntoCallbackResult,
70                F: Fn( $( $arg, )*  ) -> R + Sized + RefUnwindSafe,
71            {
72                fn argument_count(&self) -> usize {
73                    $len
74                }
75
76                fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
77                    if args.len() != $len {
78                        return Ok(Err(format!(
79                            "Invalid argument count: Expected {}, got {}",
80                            self.argument_count(),
81                            args.len()
82                        )));
83                    }
84
85                    let res = impl_callback!(@call $len self args $($arg),* );
86                    Ok(res.into_callback_res())
87                }
88            }
89        )*
90    };
91}
92
93impl_callback![
94    0: (),
95    1: (A1,),
96    2: (A1, A2,),
97    3: (A1, A2, A3,),
98    4: (A1, A2, A3, A4,),
99    5: (A1, A2, A3, A4, A5,),
100];
101
102/// A wrapper around Vec<JsValue>, used for vararg callbacks.
103///
104/// To create a callback with a variable number of arguments, a callback closure
105/// must take a single `Arguments` argument.
106pub struct Arguments(Vec<JsValue>);
107
108impl Arguments {
109    /// Unpack the arguments into a Vec.
110    pub fn into_vec(self) -> Vec<JsValue> {
111        self.0
112    }
113}
114
115impl<F> Callback<PhantomData<(&Arguments, &F)>> for F
116where
117    F: Fn(Arguments) + Sized + RefUnwindSafe,
118{
119    fn argument_count(&self) -> usize {
120        0
121    }
122
123    fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
124        (self)(Arguments(args));
125        Ok(Ok(JsValue::Undefined))
126    }
127}
128
129impl<F, R> Callback<PhantomData<(&Arguments, &F, &R)>> for F
130where
131    R: IntoCallbackResult,
132    F: Fn(Arguments) -> R + Sized + RefUnwindSafe,
133{
134    fn argument_count(&self) -> usize {
135        0
136    }
137
138    fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
139        let res = (self)(Arguments(args));
140        Ok(res.into_callback_res())
141    }
142}
143
144// Implement Callback for Fn() -> R functions.
145//impl<R, F> Callback<PhantomData<(&R, &F)>> for F
146//where
147//R: Into<JsValue>,
148//F: Fn() -> R + Sized + RefUnwindSafe,
149//{
150//fn argument_count(&self) -> usize {
151//0
152//}
153
154//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
155//if !args.is_empty() {
156//return Ok(Err(format!(
157//"Invalid argument count: Expected 0, got {}",
158//args.len()
159//)));
160//}
161
162//let res = self().into();
163//Ok(Ok(res))
164//}
165//}
166
167// Implement Callback for Fn(A) -> R functions.
168//impl<A1, R, F> Callback<PhantomData<(&A1, &R, &F)>> for F
169//where
170//A1: TryFrom<JsValue, Error = ValueError>,
171//R: Into<JsValue>,
172//F: Fn(A1) -> R + Sized + RefUnwindSafe,
173//{
174//fn argument_count(&self) -> usize {
175//1
176//}
177//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
178//if args.len() != 1 {
179//return Ok(Err(format!(
180//"Invalid argument count: Expected 1, got {}",
181//args.len()
182//)));
183//}
184
185//let arg_raw = args.into_iter().next().expect("Invalid argument count");
186//let arg = A1::try_from(arg_raw)?;
187//let res = self(arg).into();
188//Ok(Ok(res))
189//}
190//}
191
192//// Implement Callback for Fn(A1, A2) -> R functions.
193//impl<A1, A2, R, F> Callback<PhantomData<(&A1, &A2, &R, &F)>> for F
194//where
195//A1: TryFrom<JsValue, Error = ValueError>,
196//A2: TryFrom<JsValue, Error = ValueError>,
197//R: Into<JsValue>,
198//F: Fn(A1, A2) -> R + Sized + RefUnwindSafe,
199//{
200//fn argument_count(&self) -> usize {
201//2
202//}
203
204//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
205//if args.len() != 2 {
206//return Ok(Err(format!(
207//"Invalid argument count: Expected 2, got {}",
208//args.len()
209//)));
210//}
211
212//let mut iter = args.into_iter();
213//let arg1_raw = iter.next().expect("Invalid argument count");
214//let arg1 = A1::try_from(arg1_raw)?;
215
216//let arg2_raw = iter.next().expect("Invalid argument count");
217//let arg2 = A2::try_from(arg2_raw)?;
218
219//let res = self(arg1, arg2).into();
220//Ok(Ok(res))
221//}
222//}
223
224// Implement Callback for Fn(A1, A2, A3) -> R functions.
225//impl<A1, A2, A3, R, F> Callback<PhantomData<(&A1, &A2, &A3, &R, &F)>> for F
226//where
227//A1: TryFrom<JsValue, Error = ValueError>,
228//A2: TryFrom<JsValue, Error = ValueError>,
229//A3: TryFrom<JsValue, Error = ValueError>,
230//R: Into<JsValue>,
231//F: Fn(A1, A2, A3) -> R + Sized + RefUnwindSafe,
232//{
233//fn argument_count(&self) -> usize {
234//3
235//}
236
237//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
238//if args.len() != self.argument_count() {
239//return Ok(Err(format!(
240//"Invalid argument count: Expected 3, got {}",
241//args.len()
242//)));
243//}
244
245//let mut iter = args.into_iter();
246//let arg1_raw = iter.next().expect("Invalid argument count");
247//let arg1 = A1::try_from(arg1_raw)?;
248
249//let arg2_raw = iter.next().expect("Invalid argument count");
250//let arg2 = A2::try_from(arg2_raw)?;
251
252//let arg3_raw = iter.next().expect("Invalid argument count");
253//let arg3 = A3::try_from(arg3_raw)?;
254
255//let res = self(arg1, arg2, arg3).into();
256//Ok(Ok(res))
257//}
258//}
259
260//// Implement Callback for Fn(A1, A2, A3, A4) -> R functions.
261//impl<A1, A2, A3, A4, R, F> Callback<PhantomData<(&A1, &A2, &A3, &A4, &R, &F)>> for F
262//where
263//A1: TryFrom<JsValue, Error = ValueError>,
264//A2: TryFrom<JsValue, Error = ValueError>,
265//A3: TryFrom<JsValue, Error = ValueError>,
266//A4: TryFrom<JsValue, Error = ValueError>,
267//R: Into<JsValue>,
268//F: Fn(A1, A2, A3) -> R + Sized + RefUnwindSafe,
269//{
270//fn argument_count(&self) -> usize {
271//4
272//}
273
274//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
275//if args.len() != self.argument_count() {
276//return Ok(Err(format!(
277//"Invalid argument count: Expected 3, got {}",
278//args.len()
279//)));
280//}
281
282//let mut iter = args.into_iter();
283//let arg1_raw = iter.next().expect("Invalid argument count");
284//let arg1 = A1::try_from(arg1_raw)?;
285
286//let arg2_raw = iter.next().expect("Invalid argument count");
287//let arg2 = A2::try_from(arg2_raw)?;
288
289//let arg3_raw = iter.next().expect("Invalid argument count");
290//let arg3 = A3::try_from(arg3_raw)?;
291
292//let res = self(arg1, arg2, arg3).into();
293//Ok(Ok(res))
294//}
295//}
296
297// RESULT variants.