1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{format_ident, quote};
4use syn::{Data, DeriveInput, Fields, parse_macro_input};
5
6#[proc_macro_attribute]
18pub fn shared(_attr: TokenStream, item: TokenStream) -> TokenStream {
19 let input = parse_macro_input!(item as DeriveInput);
20 expand(input)
21 .unwrap_or_else(|e| e.to_compile_error())
22 .into()
23}
24
25enum FieldKind {
26 Mutex,
27 Raw,
28}
29
30fn field_kind(field: &syn::Field) -> FieldKind {
31 for attr in &field.attrs {
32 if attr.path().is_ident("raw") {
33 return FieldKind::Raw;
34 }
35 }
36 FieldKind::Mutex
37}
38
39fn strip_field_attrs(field: &syn::Field) -> syn::Field {
40 let mut f = field.clone();
41 f.attrs.retain(|a| !a.path().is_ident("raw"));
42 f
43}
44
45fn expand(input: DeriveInput) -> syn::Result<TokenStream2> {
46 let vis = &input.vis;
47 let name = &input.ident;
48 let inner_name = format_ident!("{}Inner", name);
49
50 let inner_attrs: Vec<_> = input
51 .attrs
52 .iter()
53 .filter(|a| !a.path().is_ident("shared"))
54 .map(|a| quote! { #a })
55 .collect();
56
57 let Data::Struct(data) = &input.data else {
58 return Err(syn::Error::new_spanned(
59 name,
60 "#[shared] only supports structs",
61 ));
62 };
63 let Fields::Named(fields) = &data.fields else {
64 return Err(syn::Error::new_spanned(
65 name,
66 "#[shared] only supports named fields",
67 ));
68 };
69
70 let mut inner_fields = Vec::new();
71 let mut inner_inits = Vec::new();
72 let mut new_params = Vec::new();
73 let mut accessors = Vec::new();
74
75 for field in &fields.named {
76 let fname = field.ident.as_ref().unwrap();
77 let ftype = &field.ty;
78 let fvis = &field.vis;
79 let kind = field_kind(field);
80 let stripped = strip_field_attrs(field);
81 let extra_attrs: Vec<_> = stripped.attrs.iter().collect();
82
83 match kind {
84 FieldKind::Mutex => {
85 inner_fields.push(quote! {
86 #(#extra_attrs)*
87 #fvis #fname: ::tokio::sync::Mutex<#ftype>
88 });
89 inner_inits.push(quote! { #fname: ::tokio::sync::Mutex::new(#fname) });
90 new_params.push(quote! { #fname: #ftype });
91 accessors.push(quote! {
92 #fvis async fn #fname(&self) -> ::tokio::sync::MutexGuard<'_, #ftype> {
93 self.0.#fname.lock().await
94 }
95 });
96 }
97 FieldKind::Raw => {
98 inner_fields.push(quote! {
99 #(#extra_attrs)*
100 #fvis #fname: #ftype
101 });
102 inner_inits.push(quote! { #fname });
103 new_params.push(quote! { #fname: #ftype });
104 accessors.push(quote! {
105 #fvis fn #fname(&self) -> &#ftype {
106 &self.0.#fname
107 }
108 });
109 }
110 }
111 }
112
113 Ok(quote! {
114 #(#inner_attrs)*
115 #vis struct #inner_name {
116 #(#inner_fields),*
117 }
118
119 #[derive(Clone)]
120 #vis struct #name(::std::sync::Arc<#inner_name>);
121
122 impl #name {
123 #vis fn new(#(#new_params),*) -> Self {
124 Self(::std::sync::Arc::new(#inner_name {
125 #(#inner_inits),*
126 }))
127 }
128
129 #(#accessors)*
130 }
131 })
132}