hayabusa_account_attribute_macro/
lib.rs

1// Copyright (c) 2025, Arcane Labs <dev@arcane.fi>
2// SPDX-License-Identifier: Apache-2.0
3
4use 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/// Expands to:
17/// ```
18/// #[derive(::bytemuck::Pod, ::bytemuck::Zeroable)]
19/// #[derive(Discriminator, Len, ZcDeserialize, ZcDeserializeMut, ZcInitialize, Copy, Clone)]
20/// #[repr(C)]
21/// ```
22#[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}