wasm_bindgen_utils/
macros.rs1#[macro_export]
28macro_rules! impl_main_wasm_traits {
29 ($type_name:path) => {
30 impl $type_name {
31 const TYPE_NAME: &'static str = stringify!($type_name);
32 pub fn try_into_js_value(&self) -> Result<$crate::prelude::JsValue, $crate::prelude::serde_wasm_bindgen::Error> {
33 $crate::prelude::to_js_value(&self)
34 }
35 pub fn try_from_js_value(js: $crate::prelude::JsValue) -> Result<Self, $crate::prelude::serde_wasm_bindgen::Error> {
36 $crate::prelude::from_js_value(js)
37 }
38 }
39 impl $crate::prelude::wasm_bindgen::describe::WasmDescribe for $type_name {
40 #[inline]
41 fn describe() {
42 <Self as $crate::prelude::Tsify>::JsType::describe()
43 }
44 }
45 impl $crate::prelude::wasm_bindgen::convert::IntoWasmAbi for $type_name {
46 type Abi = <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::IntoWasmAbi>::Abi;
47
48 #[inline]
49 fn into_abi(self) -> Self::Abi {
50 let mut err = String::new();
51 err.push_str(Self::TYPE_NAME);
52 err.push_str(": ");
53 let result = self.try_into_js_value().map(<<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::JsCast>::unchecked_from_js);
54 $crate::prelude::UnwrapThrowExt::expect_throw(result.inspect_err(|e| err.push_str(&e.to_string())), &err).into_abi()
55 }
56 }
57 impl $crate::prelude::wasm_bindgen::convert::OptionIntoWasmAbi for $type_name {
58 #[inline]
59 fn none() -> Self::Abi {
60 <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::OptionIntoWasmAbi>::none()
61 }
62 }
63 impl $crate::prelude::wasm_bindgen::convert::FromWasmAbi for $type_name {
64 type Abi = <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::Abi;
65
66 #[inline]
67 unsafe fn from_abi(js: Self::Abi) -> Self {
68 let mut err = String::new();
69 err.push_str(Self::TYPE_NAME);
70 err.push_str(": ");
71 let result = Self::try_from_js_value(<Self as $crate::prelude::Tsify>::JsType::from_abi(js).into());
72 $crate::prelude::UnwrapThrowExt::expect_throw(result.inspect_err(|e| err.push_str(&e.to_string())), &err)
73 }
74 }
75 impl $crate::prelude::wasm_bindgen::convert::OptionFromWasmAbi for $type_name {
76 #[inline]
77 fn is_none(js: &Self::Abi) -> bool {
78 <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::OptionFromWasmAbi>::is_none(js)
79 }
80 }
81 };
82}
83
84#[macro_export]
108macro_rules! impl_complementary_wasm_traits {
109 ($type_name:path) => {
110 impl $crate::prelude::wasm_bindgen::convert::RefFromWasmAbi for $type_name {
111 type Abi = <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::RefFromWasmAbi>::Abi;
112 type Anchor = Box<Self>;
113 unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
114 Box::new(<Self as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::from_abi(js))
115 }
116 }
117 impl $crate::prelude::wasm_bindgen::convert::RefMutFromWasmAbi for $type_name {
118 type Abi = <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::Abi;
119 type Anchor = Box<Self>;
120 unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor {
121 Box::new(<Self as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::from_abi(js))
122 }
123 }
124 impl $crate::prelude::wasm_bindgen::convert::LongRefFromWasmAbi for $type_name {
125 type Abi = <<Self as $crate::prelude::Tsify>::JsType as $crate::prelude::wasm_bindgen::convert::LongRefFromWasmAbi>::Abi;
126 type Anchor = Box<Self>;
127 unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
128 Box::new(<Self as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::from_abi(js))
129 }
130 }
131 impl $crate::prelude::wasm_bindgen::convert::VectorIntoWasmAbi for $type_name {
132 type Abi = <Box<[<Self as $crate::prelude::Tsify>::JsType]> as $crate::prelude::wasm_bindgen::convert::IntoWasmAbi>::Abi;
133 fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
134 $crate::prelude::wasm_bindgen::convert::js_value_vector_into_abi(vector)
135 }
136 }
137 impl $crate::prelude::wasm_bindgen::convert::VectorFromWasmAbi for $type_name {
138 type Abi = <Box<[<Self as $crate::prelude::Tsify>::JsType]> as $crate::prelude::wasm_bindgen::convert::FromWasmAbi>::Abi;
139 unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
140 $crate::prelude::wasm_bindgen::convert::js_value_vector_from_abi(js)
141 }
142 }
143 impl $crate::prelude::wasm_bindgen::describe::WasmDescribeVector for $type_name {
144 fn describe_vector() {
145 $crate::prelude::wasm_bindgen::describe::inform($crate::prelude::wasm_bindgen::describe::VECTOR);
146 <Self as $crate::prelude::wasm_bindgen::describe::WasmDescribe>::describe();
147 }
148 }
149 impl From<$type_name> for $crate::prelude::wasm_bindgen::JsValue {
150 fn from(value: $type_name) -> Self {
151 let mut err = String::new();
152 err.push_str(<$type_name>::TYPE_NAME);
153 err.push_str(": ");
154 let result = value.try_into_js_value();
155 $crate::prelude::UnwrapThrowExt::expect_throw(
156 result.inspect_err(|e| err.push_str(&e.to_string())),
157 &err,
158 )
159 }
160 }
161 impl $crate::prelude::wasm_bindgen::convert::TryFromJsValue for $type_name {
162 type Error = $crate::prelude::serde_wasm_bindgen::Error;
163 fn try_from_js_value(value: $crate::prelude::wasm_bindgen::JsValue) -> Result<Self, Self::Error> {
164 Self::try_from_js_value(value)
165 }
166 }
167 };
168}
169
170#[macro_export]
191macro_rules! impl_wasm_traits {
192 ($type_name:path) => {
193 $crate::impl_main_wasm_traits!($type_name);
194 $crate::impl_complementary_wasm_traits!($type_name);
195 };
196}
197
198#[macro_export]
251macro_rules! impl_custom_tsify {
252 ($type_name:ident, $decl:literal) => {
253 $crate::prelude::paste::paste! {
254 #[$crate::prelude::wasm_bindgen]
255 extern "C" {
256 #[wasm_bindgen(typescript_type = [<$type_name>])]
257 pub type [<Js $type_name>];
258 }
259
260 #[$crate::prelude::wasm_bindgen(typescript_custom_section)]
261 const TYPESCRIPT_CONTENT: &'static str = $decl;
262
263 impl $crate::prelude::Tsify for $type_name {
264 type JsType = [<Js $type_name>];
265 const DECL: &'static str = $decl;
266 }
267 }
268 };
269}
270
271#[macro_export]
285macro_rules! add_ts_content {
286 ($decl:literal) => {
287 $crate::prelude::paste::paste! {
288 #[$crate::prelude::wasm_bindgen(typescript_custom_section)]
289 const TYPESCRIPT_CONTENT: &'static str = $decl;
290 }
291 };
292}
293
294#[cfg(target_family = "wasm")]
295#[cfg(test)]
296mod tests {
297 use crate::*;
298 use js_sys::JsString;
299 use wasm_bindgen_test::wasm_bindgen_test;
300 use std::{collections::HashMap, str::FromStr};
301
302 #[derive(serde::Deserialize, serde::Serialize, Default)]
303 pub struct A {
304 pub field1: String,
305 #[serde(serialize_with = "serialize_as_bytes")]
306 pub field2: Vec<u8>,
307 #[serde(serialize_with = "serialize_hashmap_as_object")]
308 pub field3: HashMap<String, u64>,
309 }
310
311 impl_custom_tsify!(
315 A,
316 "export interface A {
317 field1: String;
318 field2: Uint8Array;
319 field3: Record<string, bigint>;
320 };"
321 );
322 impl_wasm_traits!(A);
323 add_ts_content!("export type SomeType = string;");
324
325 #[wasm_bindgen_test]
326 fn test_macros() {
327 let res = A::default().try_into_js_value().unwrap();
328
329 assert!(JsString::from_str("field1").unwrap().js_in(&res));
331 assert!(JsString::from_str("field2").unwrap().js_in(&res));
332 assert!(JsString::from_str("field3").unwrap().js_in(&res));
333
334 assert!(!JsString::from_str("field4").unwrap().js_in(&res));
336 }
337}