Skip to main content

bitwarden_ffi_macro/
wasm_object.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{Item, parse2};
4
5use crate::attrs;
6
7/// Declares a `#[wasm_bindgen]` handle type, forwarding this macro's arguments to it.
8pub(crate) fn wasm_object(attr: TokenStream, item: TokenStream) -> TokenStream {
9    let forwarded = match attrs::parse_args(attr) {
10        Ok(args) => args,
11        Err(err) => return err.to_compile_error(),
12    };
13
14    let item = match parse2::<Item>(item) {
15        Ok(item) => item,
16        Err(err) => return err.to_compile_error(),
17    };
18
19    if let Err(err) = attrs::check_exportable_type(&item, "wasm_object") {
20        return err.to_compile_error();
21    }
22
23    let bindgen = attrs::wasm_bindgen_attr(&forwarded);
24    let item = item.to_token_stream();
25    quote! {
26        #bindgen
27        #item
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    fn expand(attr: TokenStream, item: TokenStream) -> String {
36        wasm_object(attr, item).to_string().replace(' ', "")
37    }
38
39    #[test]
40    fn applies_wasm_bindgen_under_the_wasm_feature() {
41        let out = expand(
42            TokenStream::new(),
43            quote! {
44                pub struct CiphersClient { client: Client }
45            },
46        );
47
48        assert!(!out.contains("compile_error!"), "{out}");
49        assert!(
50            out.contains("#[cfg_attr(feature=\"wasm\",::bitwarden_ffi::_macro::wasm_bindgen)]"),
51            "{out}"
52        );
53    }
54
55    #[test]
56    fn forwards_its_arguments_to_wasm_bindgen() {
57        let out = expand(
58            quote!(js_name = Ciphers),
59            quote! {
60                pub struct CiphersClient;
61            },
62        );
63
64        assert!(
65            out.contains("::bitwarden_ffi::_macro::wasm_bindgen(js_name=Ciphers)"),
66            "{out}"
67        );
68    }
69
70    #[test]
71    fn leaves_field_attributes_in_place() {
72        let out = expand(
73            TokenStream::new(),
74            quote! {
75                pub struct JsIpcClient {
76                    #[wasm_bindgen(skip)]
77                    pub client: Arc<dyn IpcClient>,
78                }
79            },
80        );
81
82        assert!(out.contains("#[wasm_bindgen(skip)]"), "{out}");
83    }
84
85    #[test]
86    fn rejects_an_unparseable_argument_list() {
87        let out = expand(quote!(js_name =), quote! { pub struct CiphersClient; });
88
89        assert!(out.contains("compile_error!"), "{out}");
90    }
91
92    #[test]
93    fn rejects_generics() {
94        let out = expand(
95            TokenStream::new(),
96            quote! {
97                pub struct Holder<T> { inner: T }
98            },
99        );
100
101        assert!(out.contains("compile_error!"), "{out}");
102        assert!(out.contains("doesnotsupportgenerics"), "{out}");
103    }
104}