hayabusa_account_attribute_macro/
lib.rs1use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, Attribute, ItemStruct, Result};
7
8fn strip_account_attr(attrs: &[Attribute]) -> Vec<Attribute> {
9 attrs
10 .iter()
11 .filter(|attr| !attr.path().is_ident("account"))
12 .cloned()
13 .collect()
14}
15
16#[proc_macro_attribute]
23pub fn account(attr: TokenStream, item: TokenStream) -> TokenStream {
24 if !proc_macro2::TokenStream::from(attr.clone()).is_empty() {
25 return syn::Error::new_spanned(
26 proc_macro2::TokenStream::from(attr),
27 "#[account] does not take arguments",
28 )
29 .to_compile_error()
30 .into();
31 }
32
33 let input = parse_macro_input!(item as ItemStruct);
34
35 match expand_account(input) {
36 Ok(ts) => ts.into(),
37 Err(e) => e.to_compile_error().into(),
38 }
39}
40
41fn expand_account(input: ItemStruct) -> Result<proc_macro2::TokenStream> {
42 let ItemStruct {
43 attrs,
44 vis,
45 ident,
46 generics,
47 fields,
48 semi_token,
49 ..
50 } = input;
51
52 if semi_token.is_some() {
53 return Err(syn::Error::new_spanned(
54 ident,
55 "#[account] does not support tuple/unit structs",
56 ));
57 }
58
59 let preserved_struct_attrs = strip_account_attr(&attrs);
60 let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
61
62 Ok(quote! {
63 #(#preserved_struct_attrs)*
64 #[derive(
65 ::bytemuck::Pod,
66 ::bytemuck::Zeroable,
67 Discriminator,
68 Len,
69 Deserialize,
70 DeserializeMut,
71 Zc,
72 ZcDeserialize,
73 ZcDeserializeMut,
74 ZcInitialize,
75 Copy,
76 Clone,
77 )]
78 #[repr(C)]
79 #vis struct #ident #impl_generics #fields #where_clause
80 })
81}