1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Fields, ItemStruct, parse_macro_input};
4
5#[proc_macro_attribute]
6pub fn atomic_struct(_attr: TokenStream, item: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(item as ItemStruct);
8 let struct_name = &input.ident;
9 let struct_vis = &input.vis;
10
11 let struct_attrs = &input.attrs;
12 let mut atomic_fields = Vec::new();
13 let mut getters_setters = Vec::new();
14 let mut constructor_params = Vec::new();
15 let mut constructor_inits = Vec::new();
16
17 if let Fields::Named(fields) = &input.fields {
18 for field in &fields.named {
19 let fname = field.ident.as_ref().unwrap();
20 let fty = &field.ty;
21 let attrs = &field.attrs;
22 let vis = &field.vis;
23 let doc_attrs: Vec<_> = field
24 .attrs
25 .iter()
26 .filter(|attr| attr.path().is_ident("doc"))
27 .collect();
28
29 atomic_fields.push(quote! {
31 #(#attrs)*
32 #fname: atomic_struct_core::AtomicMember<#fty>
33 });
34
35 constructor_params.push(quote! {
37 #fname: #fty
38 });
39
40 constructor_inits.push(quote! {
42 #fname: atomic_struct_core::AtomicMember::new(#fname)
43 });
44
45 let get_name = format_ident!("get_{}", fname);
47 getters_setters.push(quote! {
48 #(#doc_attrs)*
49 #vis async fn #get_name(&self) -> #fty {
50 self.#fname.get().await
51 }
52 });
53
54 let set_name = format_ident!("set_{}", fname);
56 getters_setters.push(quote! {
57 #(#doc_attrs)*
58 #vis async fn #set_name(&self, new_val: #fty) {
59 self.#fname.set(new_val).await
60 }
61 });
62 }
63 }
64
65
66 let expanded = quote! {
68 #(#struct_attrs)*
69 #struct_vis struct #struct_name {
70 #(#atomic_fields),*
71 }
72
73 impl #struct_name {
74 #struct_vis fn new(#(#constructor_params),*) -> Self {
75 Self {
76 #(#constructor_inits),*
77 }
78 }
79
80 #(#getters_setters)*
81 }
82 };
83
84 expanded.into()
85}