bitwarden_ffi_macro/
wasm_export.rs1use proc_macro2::TokenStream;
2use quote::ToTokens;
3use syn::{Attribute, ImplItem, ItemImpl, parse2};
4
5pub(crate) fn wasm_export(item: TokenStream) -> TokenStream {
17 let mut impl_block = match parse2::<ItemImpl>(item) {
18 Ok(block) => block,
19 Err(err) => return err.to_compile_error(),
20 };
21
22 for item in &mut impl_block.items {
23 let ImplItem::Fn(method) = item else {
24 continue;
25 };
26
27 let wasm_only_idx = method
28 .attrs
29 .iter()
30 .position(|attr| attr.path().is_ident("wasm_only"));
31
32 let Some(idx) = wasm_only_idx else {
33 continue;
34 };
35
36 let custom_note = match extract_wasm_only_note(&method.attrs[idx]) {
38 Ok(note) => note,
39 Err(err) => return err.to_compile_error(),
40 };
41
42 method.attrs.remove(idx);
44
45 let original_name = method.sig.ident.to_string();
46
47 if !has_wasm_bindgen_js_name(&method.attrs) {
50 method
51 .attrs
52 .push(syn::parse_quote!(#[wasm_bindgen(js_name = #original_name)]));
53 }
54
55 method.attrs.push(syn::parse_quote!(#[doc(hidden)]));
57
58 let note = custom_note.unwrap_or_else(|| {
60 "This is a WASM-only binding. Calling it from Rust is not allowed.".to_string()
61 });
62 method
63 .attrs
64 .push(syn::parse_quote!(#[deprecated(note = #note)]));
65
66 method.attrs.push(syn::parse_quote!(#[allow(deprecated)]));
68
69 method.sig.ident = syn::Ident::new(
71 &format!("__wasm_only_{original_name}"),
72 method.sig.ident.span(),
73 );
74 }
75
76 impl_block.into_token_stream()
77}
78
79fn extract_wasm_only_note(attr: &Attribute) -> Result<Option<String>, syn::Error> {
82 if attr.meta.require_path_only().is_ok() {
84 return Ok(None);
85 }
86
87 let mut note = None;
88 attr.parse_nested_meta(|meta| {
89 if meta.path.is_ident("note") {
90 note = Some(meta.value()?.parse::<syn::LitStr>()?.value());
91 Ok(())
92 } else {
93 Err(meta.error("unknown attribute, expected `note`"))
94 }
95 })?;
96 Ok(note)
97}
98
99fn has_wasm_bindgen_js_name(attrs: &[Attribute]) -> bool {
100 attrs.iter().any(|attr| {
101 attr.path().is_ident("wasm_bindgen")
102 && attr.to_token_stream().to_string().contains("js_name")
103 })
104}