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
24pub trait Callback<F>: RefUnwindSafe {
27 fn argument_count(&self) -> usize;
29
30 fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError>;
38}
39
40macro_rules! impl_callback {
41 (@call $len:literal $self:ident $args:ident ) => {
42 $self()
43 };
44
45 (@call $len:literal $self:ident $args:ident $( $arg:ident ),* ) => {
46 {
47 let mut iter = $args.into_iter();
48 $self(
49 $(
50 $arg::try_from(iter.next().unwrap())?,
51 )*
52 )
53 }
54 };
55
56 [ $( $len:literal : ( $( $arg:ident, )* ), )* ] => {
57 $(
58
59 impl<
60 $( $arg, )*
61 E,
62 R,
63 F,
64 > Callback<PhantomData<(
65 $( &$arg, )*
66 &E,
67 &R,
68 &F,
69 )>> for F
70 where
71 $( $arg: TryFrom<JsValue, Error = E>, )*
72 ValueError: From<E>,
73 R: IntoCallbackResult,
74 F: Fn( $( $arg, )* ) -> R + Sized + RefUnwindSafe,
75 {
76 fn argument_count(&self) -> usize {
77 $len
78 }
79
80 fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
81 if args.len() != $len {
82 return Ok(Err(format!(
83 "Invalid argument count: Expected {}, got {}",
84 self.argument_count(),
85 args.len()
86 )));
87 }
88
89 let res = impl_callback!(@call $len self args $($arg),* );
90 Ok(res.into_callback_res())
91 }
92 }
93 )*
94 };
95}
96
97impl<R, F> Callback<PhantomData<(&R, &F)>> for F
98where
99 R: IntoCallbackResult,
100 F: Fn() -> R + Sized + RefUnwindSafe,
101{
102 fn argument_count(&self) -> usize {
103 0
104 }
105
106 fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
107 if !args.is_empty() {
108 return Ok(Err(format!(
109 "Invalid argument count: Expected 0, got {}",
110 args.len(),
111 )));
112 }
113
114 let res = self();
115 Ok(res.into_callback_res())
116 }
117}
118
119impl_callback![
120 1: (A1,),
121 2: (A1, A2,),
122 3: (A1, A2, A3,),
123 4: (A1, A2, A3, A4,),
124 5: (A1, A2, A3, A4, A5,),
125];
126
127pub struct Arguments(Vec<JsValue>);
132
133impl Arguments {
134 pub fn into_vec(self) -> Vec<JsValue> {
136 self.0
137 }
138}
139
140impl<F> Callback<PhantomData<(&Arguments, &F)>> for F
141where
142 F: Fn(Arguments) + Sized + RefUnwindSafe,
143{
144 fn argument_count(&self) -> usize {
145 0
146 }
147
148 fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
149 (self)(Arguments(args));
150 Ok(Ok(JsValue::Undefined))
151 }
152}
153
154impl<F, R> Callback<PhantomData<(&Arguments, &F, &R)>> for F
155where
156 R: IntoCallbackResult,
157 F: Fn(Arguments) -> R + Sized + RefUnwindSafe,
158{
159 fn argument_count(&self) -> usize {
160 0
161 }
162
163 fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
164 let res = (self)(Arguments(args));
165 Ok(res.into_callback_res())
166 }
167}