js_helpers/
lib.rs

1#![forbid(unsafe_code)]
2#![no_implicit_prelude]
3#![doc = include_str!("../README.md")]
4
5pub use ::wasm_bindgen;
6pub use ::web_sys::{self, js_sys};
7
8#[derive(Debug)]
9pub enum JsMacroError {
10    Lookup { object: wasm_bindgen::JsValue, index: wasm_bindgen::JsValue },
11    Assign { object: wasm_bindgen::JsValue, index: wasm_bindgen::JsValue },
12    NotFunction { object: wasm_bindgen::JsValue },
13    FunctionError { object: wasm_bindgen::JsValue, error: wasm_bindgen::JsValue },
14    NoWindow,
15}
16pub type JsMacroResult = ::std::result::Result<wasm_bindgen::JsValue, JsMacroError>;
17
18#[macro_export]
19macro_rules! js {
20    (null $($rest:tt)*) => {{
21        let js_helpers_null = $crate::wasm_bindgen::JsValue::null();
22        $crate::js!(js_helpers_null $($rest)*)
23    }};
24    (undefined $($rest:tt)*) => {{
25        let js_helpers_undefined = $crate::wasm_bindgen::JsValue::undefined();
26        $crate::js!(js_helpers_undefined $($rest)*)
27    }};
28    (window $($rest:tt)*) => {
29        match $crate::web_sys::window().map(<$crate::wasm_bindgen::JsValue as ::std::convert::From<_>>::from) {
30            ::std::option::Option::Some(js_helpers_window) => $crate::js!(js_helpers_window $($rest)*),
31            ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::NoWindow),
32        }
33    };
34
35    // --------------------------------------------------------------------------------------------------------------
36
37    ([ $($t:tt)* ] $($rest:tt)*) => {{
38        let mut js_helpers_array_target = $crate::js_sys::Array::new();
39        match $crate::js!(@fill_array js_helpers_array_target () $($t)*) {
40            $crate::JsMacroResult::Ok(js_helpers_root) => $crate::js!(js_helpers_root $($rest)*),
41            x => x,
42        }
43    }};
44    (@fill_array $target:ident ()) => {
45        $crate::JsMacroResult::Ok(::std::convert::Into::into($target))
46    };
47    (@fill_array $target:ident ( $($stuff:tt)* ) $(, $($rest:tt)*)?) => {
48        match $crate::js!($($stuff)*) {
49            $crate::JsMacroResult::Ok(v) => {
50                $target.push(&v);
51                $crate::js!(@fill_array $target () $($($rest)*)?)
52            }
53            x => x,
54        }
55    };
56    (@fill_array $target:ident ( $($stuff:tt)* ) $head:tt $($rest:tt)*) => {
57        $crate::js!(@fill_array $target ( $($stuff)* $head ) $($rest)*)
58    };
59
60    // --------------------------------------------------------------------------------------------------------------
61
62    ({ $var:ident } $($rest:tt)*) => {
63        $crate::js!({ $var: $var })
64    };
65    ({ $rust_expr:expr } $($rest:tt)*) => {{
66        let mut js_helpers_rust_object: $crate::wasm_bindgen::JsValue = ::std::convert::Into::into($rust_expr);
67        $crate::js!(js_helpers_rust_object $($rest)*)
68    }};
69    ({ $($t:tt)* } $($rest:tt)*) => {{
70        let mut js_helpers_object_target: $crate::wasm_bindgen::JsValue = ::std::convert::Into::into($crate::js_sys::Object::new());
71        match $crate::js!(@fill_object js_helpers_object_target () () $($t)*) {
72            $crate::JsMacroResult::Ok(js_helpers_root) => $crate::js!(js_helpers_root $($rest)*),
73            x => x,
74        }
75    }};
76    (@fill_object $target:ident () ()) => {
77        $crate::JsMacroResult::Ok($target)
78    };
79    (@fill_object $target:ident () () $key:ident $(, $($rest:tt)*)?) => {
80        $crate::js!(@fill_object $target () () $key : $key $(, $($rest)*)?)
81    };
82    (@fill_object $target:ident () () $key:ident : $($rest:tt)*) => {{
83        let js_helpers_key_name: $crate::wasm_bindgen::JsValue = ::std::convert::Into::into(::std::stringify!($key));
84        $crate::js!(@fill_object $target ( js_helpers_key_name ) () $($rest)*)
85    }};
86    (@fill_object $target:ident () () $key:literal : $($rest:tt)*) => {{
87        let js_helpers_key_name: $crate::wasm_bindgen::JsValue = ::std::convert::Into::into($key);
88        $crate::js!(@fill_object $target ( js_helpers_key_name ) () $($rest)*)
89    }};
90    (@fill_object $target:ident ( $key:ident ) ( $($stuff:tt)* ) $(, $($rest:tt)*)?) => {
91        match $crate::js!($($stuff)*) {
92            $crate::JsMacroResult::Ok(v) => {
93                $crate::js_sys::Reflect::set(&$target, &$key, &v).unwrap();
94                $crate::js!(@fill_object $target () () $($($rest)*)?)
95            }
96            x => x,
97        }
98    };
99    (@fill_object $target:ident ( $key:ident ) ( $($stuff:tt)* ) $head:tt $($rest:tt)*) => {
100        $crate::js!(@fill_object $target ( $key ) ( $($stuff)* $head ) $($rest)*)
101    };
102
103    // --------------------------------------------------------------------------------------------------------------
104
105    (( $($vars:ident),* $(,)? ) => { $($body:tt)* }) => {
106        $crate::JsMacroResult::Ok(::std::convert::Into::into($crate::js_sys::Function::new_with_args(::std::concat!($(::std::stringify!($vars), ","),*), ::std::stringify!($($body)*))))
107    };
108    (( $($vars:ident),* $(,)? ) => $($body:tt)*) => {
109        $crate::js!(( $($vars),* ) => { return $($body)*; })
110    };
111    ($var:ident => $($body:tt)*) => {
112        $crate::js!(( $var ) => $($body)*)
113    };
114    (function ( $($vars:ident),* $(,)? ) { $($body:tt)* }) => {
115        $crate::js!(( $($vars),* ) => { $($body)* })
116    };
117
118    // --------------------------------------------------------------------------------------------------------------
119
120    ($root:ident . $field:ident ( $($args:tt)* ) $($rest:tt)*) => {
121        match $crate::js!($root . $field) {
122            $crate::JsMacroResult::Ok(js_helpers_function) => $crate::js!(@function_call $root js_helpers_function ( $($args)* ) $($rest)*),
123            x => x,
124        }
125    };
126    ($root:ident [ $($index:tt)* ] ( $($args:tt)* ) $($rest:tt)*) => {
127        match $crate::js!($root [ $($index)* ]) {
128            $crate::JsMacroResult::Ok(js_helpers_function) => $crate::js!(@function_call $root js_helpers_function ( $($args)* ) $($rest)*),
129            x => x,
130        }
131    };
132    ($root:ident ( $($args:tt)* ) $($rest:tt)*) => {{
133        let js_helpers_context = $crate::wasm_bindgen::JsValue::undefined();
134        $crate::js!(@function_call js_helpers_context $root ( $($args)* ) $($rest)*)
135    }};
136    (@function_call $root:ident $func:ident ( $($args:tt)* ) $($rest:tt)*) => {
137        match $crate::wasm_bindgen::JsCast::dyn_ref::<$crate::js_sys::Function>(&$func) {
138            ::std::option::Option::Some(js_helpers_function) => match $crate::js!([$($args)*]) {
139                $crate::JsMacroResult::Ok(js_helpers_args) => match $crate::js_sys::Reflect::apply(&js_helpers_function, &$root, &$crate::wasm_bindgen::JsCast::dyn_into(js_helpers_args).unwrap()) {
140                    ::std::result::Result::Ok(js_helpers_result) => $crate::js!(js_helpers_result $($rest)*),
141                    ::std::result::Result::Err(error) => $crate::JsMacroResult::Err($crate::JsMacroError::FunctionError { object: ::std::convert::Into::into(::std::clone::Clone::clone(&$func)), error }),
142                }
143                x => x,
144            }
145            ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::NotFunction { object: ::std::convert::Into::into(::std::clone::Clone::clone(&$func)) }),
146        }
147    };
148
149    // --------------------------------------------------------------------------------------------------------------
150
151    ($root:ident . $field:ident = $($rest:tt)*) => {
152        match $crate::js!($($rest)*) {
153            $crate::JsMacroResult::Ok(x) => match $crate::js_sys::Reflect::set(&$root, &::std::convert::Into::into(::std::stringify!($field)), &x) {
154                ::std::result::Result::Ok(true) => $crate::JsMacroResult::Ok(x),
155                _ => $crate::JsMacroResult::Err($crate::JsMacroError::Assign { object: ::std::clone::Clone::clone(&$root), index: ::std::convert::Into::into(::std::stringify!($field)) }),
156            }
157            x => x,
158        }
159    };
160    ($root:ident . $field:ident $($rest:tt)*) => {
161        match $crate::js_sys::Reflect::get(&$root, &::std::convert::Into::into(::std::stringify!($field))) {
162            ::std::result::Result::Ok(js_helpers_sub_object) => $crate::js!(js_helpers_sub_object $($rest)*),
163            ::std::result::Result::Err(_) => $crate::JsMacroResult::Err($crate::JsMacroError::Lookup { object: ::std::clone::Clone::clone(&$root), index: ::std::convert::Into::into(::std::stringify!($field)) }),
164        }
165    };
166    ($root:ident ?. $field:ident $($rest:tt)*) => {{
167        let js_helpers_sub_object = $crate::js_sys::Reflect::get(&$root, &::std::convert::Into::into(::std::stringify!($field))).unwrap_or_else(|_| $crate::wasm_bindgen::JsValue::undefined());
168        $crate::js!(js_helpers_sub_object $($rest)*)
169    }};
170
171    // --------------------------------------------------------------------------------------------------------------
172
173    ($root:ident [ $($index:tt)* ] = $($rest:tt)*) => {
174        match $crate::js!($($index)*) {
175            $crate::JsMacroResult::Ok(js_helpers_index) => match $crate::wasm_bindgen::JsCast::dyn_ref::<$crate::js_sys::Array>(&$root) {
176                ::std::option::Option::Some(js_helpers_sub_object) => {
177                    let js_helpers_index_f64 = js_helpers_index.as_f64().or_else(|| js_helpers_index.as_string().and_then(|x| x.parse::<f64>().ok())).unwrap_or_else(|| 0.5);
178                    let js_helpers_index_u32 = js_helpers_index_f64 as u32;
179                    match js_helpers_index_f64 == js_helpers_index_u32 as f64 {
180                        true => match $crate::js!($($rest)*) {
181                            $crate::JsMacroResult::Ok(x) => {
182                                js_helpers_sub_object.set(js_helpers_index_u32, ::std::clone::Clone::clone(&x));
183                                $crate::JsMacroResult::Ok(x)
184                            }
185                            x => x,
186                        }
187                        false => $crate::JsMacroResult::Err($crate::JsMacroError::Assign { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
188                    }
189                }
190                ::std::option::Option::None => match $crate::wasm_bindgen::JsCast::dyn_ref::<$crate::js_sys::Object>(&$root) {
191                    ::std::option::Option::Some(js_helpers_sub_object) => match js_helpers_index.as_string().or_else(|| js_helpers_index.as_f64().map(|x| ::std::string::ToString::to_string(&x))) {
192                        ::std::option::Option::Some(js_helpers_index_name) => match $crate::js!($($rest)*) {
193                            $crate::JsMacroResult::Ok(x) => match $crate::js_sys::Reflect::set(&$root, &::std::convert::Into::into(js_helpers_index_name), &x) {
194                                ::std::result::Result::Ok(true) => $crate::JsMacroResult::Ok(x),
195                                _ => $crate::JsMacroResult::Err($crate::JsMacroError::Assign { object: ::std::clone::Clone::clone(&$root), index: ::std::convert::Into::into(::std::stringify!($field)) }),
196                            }
197                            x => x,
198                        }
199                        ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::Assign { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
200                    }
201                    ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::Assign { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
202                }
203            }
204            x => x,
205        }
206    };
207    ($root:ident [ $($index:tt)* ] $($rest:tt)*) => {
208        match $crate::js!($($index)*) {
209            $crate::JsMacroResult::Ok(js_helpers_index) => match $crate::wasm_bindgen::JsCast::dyn_ref::<$crate::js_sys::Array>(&$root) {
210                ::std::option::Option::Some(js_helpers_sub_object) => {
211                    let js_helpers_index_f64 = js_helpers_index.as_f64().or_else(|| js_helpers_index.as_string().and_then(|x| x.parse::<f64>().ok())).unwrap_or_else(|| 0.5);
212                    let js_helpers_index_u32 = js_helpers_index_f64 as u32;
213                    match js_helpers_index_f64 == js_helpers_index_u32 as f64 {
214                        true => {
215                            let js_helpers_sub_object = js_helpers_sub_object.get(js_helpers_index_u32);
216                            $crate::js!(js_helpers_sub_object $($rest)*)
217                        }
218                        false => $crate::JsMacroResult::Err($crate::JsMacroError::Lookup { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
219                    }
220                }
221                ::std::option::Option::None => match $crate::wasm_bindgen::JsCast::dyn_ref::<$crate::js_sys::Object>(&$root) {
222                    ::std::option::Option::Some(js_helpers_sub_object) => match js_helpers_index.as_string().or_else(|| js_helpers_index.as_f64().map(|x| ::std::string::ToString::to_string(&x))) {
223                        ::std::option::Option::Some(js_helpers_index_name) => match $crate::js_sys::Reflect::get(&js_helpers_sub_object, &::std::convert::Into::into(js_helpers_index_name)) {
224                            ::std::result::Result::Ok(js_helpers_sub_object) => $crate::js!(js_helpers_sub_object $($rest)*),
225                            ::std::result::Result::Err(_) => $crate::JsMacroResult::Err($crate::JsMacroError::Lookup { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
226                        }
227                        ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::Lookup { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
228                    }
229                    ::std::option::Option::None => $crate::JsMacroResult::Err($crate::JsMacroError::Lookup { object: ::std::clone::Clone::clone(&$root), index: js_helpers_index }),
230                }
231            }
232            x => x,
233        }
234    };
235
236    // --------------------------------------------------------------------------------------------------------------
237
238    (( $($stuff:tt)* ) $($rest:tt)*) => {
239        match $crate::js!($($stuff)*) {
240            $crate::JsMacroResult::Ok(js_helpers_paren_value) => $crate::js!(js_helpers_paren_value $($rest)*),
241            x => x,
242        }
243    };
244    ($v:expr) => {
245        $crate::JsMacroResult::Ok(::std::convert::Into::into(::std::clone::Clone::clone(&$v)))
246    };
247}