bitwarden_ffi_macro/
wasm_object.rs1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{Item, parse2};
4
5use crate::attrs;
6
7pub(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}