bitwarden_ffi_macro/
attrs.rs1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{Attribute, Item, Meta, Token, parse::Parser, punctuated::Punctuated};
4
5pub(crate) type Args = Punctuated<Meta, Token![,]>;
7
8pub(crate) fn parse_args(attr: TokenStream) -> syn::Result<Args> {
10 if attr.is_empty() {
11 return Ok(Args::new());
12 }
13 Args::parse_terminated.parse2(attr)
14}
15
16pub(crate) fn wasm_bindgen_attr(args: &Args) -> TokenStream {
19 let call = if args.is_empty() {
20 quote!(wasm_bindgen)
21 } else {
22 quote!(wasm_bindgen(#args))
23 };
24 quote!(#[cfg_attr(feature = "wasm", ::bitwarden_ffi::_macro::#call)])
25}
26
27pub(crate) fn wasm_bindgen_args(attr: &Attribute) -> Option<Args> {
30 let inner = if attr.path().is_ident("wasm_bindgen") {
31 attr.meta.clone()
32 } else if attr.path().is_ident("cfg_attr") {
33 let Meta::List(list) = &attr.meta else {
34 return None;
35 };
36 list.parse_args_with(Args::parse_terminated)
37 .ok()?
38 .into_iter()
39 .find(|meta| meta.path().is_ident("wasm_bindgen"))?
40 } else {
41 return None;
42 };
43
44 Some(match inner {
45 Meta::List(list) => list.parse_args_with(Args::parse_terminated).ok()?,
46 _ => Args::new(),
47 })
48}
49
50pub(crate) fn find_wasm_bindgen(attrs: &[Attribute]) -> Option<&Attribute> {
52 attrs.iter().find(|attr| wasm_bindgen_args(attr).is_some())
53}
54
55pub(crate) fn check_exportable_type(item: &Item, macro_name: &str) -> syn::Result<()> {
58 let generics = match item {
59 Item::Struct(item) => &item.generics,
60 Item::Enum(item) => &item.generics,
61 other => {
62 return Err(syn::Error::new_spanned(
63 other,
64 format!("#[{macro_name}] applies to a struct or an enum"),
65 ));
66 }
67 };
68
69 if !generics.params.is_empty() {
70 return Err(syn::Error::new_spanned(
71 generics,
72 format!(
73 "#[{macro_name}] does not support generics; wasm_bindgen cannot export generic types"
74 ),
75 ));
76 }
77
78 Ok(())
79}