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.