1use proc_macro::TokenStream;
2use quote::quote;
3use std::{cell::RefCell, rc::Rc};
4use syn::{parse_macro_input, Ident};
5
6mod template;
7mod tools;
8mod utils;
9
10use template::{
11 generate_enums_auto_macros, generate_enums_quote, generate_structs_auto_macros,
12 generate_structs_quote,
13};
14use tools::{
15 auto_macros::AutoMacrosType, AutoMacros, DeriveBox, DeriveEnum, DeriveStruct, DeriveVisibility,
16 StructName,
17};
18use utils::flatten;
19
20#[proc_macro]
26pub fn derive_struct(input: TokenStream) -> TokenStream {
27 let input = parse_macro_input!(input as DeriveStruct);
28
29 let is_public = input.visibility == DeriveVisibility::Public;
30 let macro_visibility = input.extra_macros.macros_visibility;
31 let root_ident = match input.ident.clone() {
32 StructName::Named(v) => v,
33 StructName::Unnamed(_) => {
34 return syn::Error::new(
35 proc_macro2::Span::call_site(),
36 "unnamed root struct is not supported; provide a name for the struct",
37 )
38 .to_compile_error()
39 .into();
40 }
41 };
42 let mod_ident = syn::Ident::new(&format!("__{}", root_ident), root_ident.span());
43 let (structs, enums) = match flatten(
44 root_ident.to_string(),
45 Rc::new(RefCell::new(0)),
46 DeriveBox::Struct(Box::new(input.clone())),
47 ) {
48 Ok(v) => v,
49 Err(e) => {
50 return syn::Error::new(root_ident.span(), e.to_string())
51 .to_compile_error()
52 .into();
53 }
54 };
55
56 let structs_auto_macros = generate_structs_auto_macros(structs.clone(), macro_visibility);
57 let enums_auto_macros = generate_enums_auto_macros(enums.clone(), macro_visibility);
58
59 let structs = generate_structs_quote(structs);
60 let enums = generate_enums_quote(enums);
61
62 let ret = if is_public {
63 quote! {
64 #[macro_use]
65 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code)]
66 pub mod #mod_ident {
67 use super::*;
68
69 #( #structs )*
70 #( #enums )*
71
72 #( #structs_auto_macros )*
73 #( #enums_auto_macros )*
74 }
75
76 pub use #mod_ident::*;
77 }
78 } else {
79 quote! {
80 #[macro_use]
81 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code)]
82 pub(crate) mod #mod_ident {
83 use super::*;
84
85 #( #structs )*
86 #( #enums )*
87
88 #( #structs_auto_macros )*
89 #( #enums_auto_macros )*
90 }
91
92 pub(crate) use #mod_ident::*;
93 }
94 };
95
96 ret.into()
97}
98
99#[proc_macro]
105pub fn derive_enum(input: TokenStream) -> TokenStream {
106 let input = parse_macro_input!(input as DeriveEnum);
107
108 let is_public = input.visibility == DeriveVisibility::Public;
109 let macro_visibility = input.extra_macros.macros_visibility;
110 let root_ident = match input.ident.clone() {
111 StructName::Named(v) => v,
112 StructName::Unnamed(_) => {
113 return syn::Error::new(
114 proc_macro2::Span::call_site(),
115 "unnamed root enum is not supported; provide a name for the enum",
116 )
117 .to_compile_error()
118 .into();
119 }
120 };
121 let mod_ident = syn::Ident::new(&format!("__{}", root_ident), root_ident.span());
122 let (structs, enums) = match flatten(
123 root_ident.to_string(),
124 Rc::new(RefCell::new(0)),
125 DeriveBox::Enum(Box::new(input.clone())),
126 ) {
127 Ok(v) => v,
128 Err(e) => {
129 return syn::Error::new(root_ident.span(), e.to_string())
130 .to_compile_error()
131 .into();
132 }
133 };
134
135 let structs_auto_macros = generate_structs_auto_macros(structs.clone(), macro_visibility);
136 let enums_auto_macros = generate_enums_auto_macros(enums.clone(), macro_visibility);
137
138 let structs = generate_structs_quote(structs);
139 let enums = generate_enums_quote(enums);
140
141 let ret = if is_public {
142 quote! {
143 #[macro_use]
144 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code)]
145 pub mod #mod_ident {
146 use super::*;
147
148 #( #structs )*
149 #( #enums )*
150
151 #( #structs_auto_macros )*
152 #( #enums_auto_macros )*
153 }
154
155 pub use #mod_ident::*;
156 }
157 } else {
158 quote! {
159 #[macro_use]
160 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code)]
161 pub(crate) mod #mod_ident {
162 use super::*;
163
164 #( #structs )*
165 #( #enums )*
166
167 #( #structs_auto_macros )*
168 #( #enums_auto_macros )*
169 }
170
171 pub(crate) use #mod_ident::*;
172 }
173 };
174
175 ret.into()
176}
177
178#[proc_macro]
185pub fn auto(input: TokenStream) -> TokenStream {
186 let input = parse_macro_input!(input as AutoMacros);
187 let ident = input.ident.clone();
188 let body = input.body;
189
190 let macro_ident = Ident::new(&format!("__auto_{}", input.ident), input.ident.span());
191 match body {
192 AutoMacrosType::Struct {
193 items,
194 expand_exprs,
195 } => {
196 let list = items
197 .iter()
198 .map(|(key, value)| {
199 quote! {
200 #key: #macro_ident!(#key #value)
201 }
202 })
203 .collect::<Vec<_>>();
204
205 if let Some(expand_exprs) = expand_exprs {
206 quote! {
207 #ident {
208 #( #list ),*,
209 ..#expand_exprs
210 }
211 }
212 .into()
213 } else {
214 quote! {
215 #ident {
216 #( #list ),*
217 }
218 }
219 .into()
220 }
221 }
222
223 AutoMacrosType::EnumEmpty { key } => quote! {
224 #ident::#key
225 }
226 .into(),
227 AutoMacrosType::EnumStruct {
228 key,
229 items,
230 expand_exprs,
231 } => {
232 let list = items
233 .iter()
234 .map(|(item_key, value)| {
235 quote! {
236 #item_key: #macro_ident!(#key #item_key #value)
237 }
238 })
239 .collect::<Vec<_>>();
240
241 if let Some(expand_exprs) = expand_exprs {
242 quote! {
243 #ident::#key {
244 #( #list ),*,
245 ..#expand_exprs
246 }
247 }
248 .into()
249 } else {
250 quote! {
251 #ident::#key {
252 #( #list ),*
253 }
254 }
255 .into()
256 }
257 }
258 AutoMacrosType::EnumTuple { key, items } => {
259 if let [first_item] = items.as_slice() {
260 quote! {
261 #ident::#key(#macro_ident!(#key #first_item))
262 }
263 .into()
264 } else {
265 let list = items
266 .iter()
267 .enumerate()
268 .map(|(index, item)| {
269 quote! {
270 #macro_ident!(#key #index #item)
271 }
272 })
273 .collect::<Vec<_>>();
274
275 quote! {
276 #ident::#key(#( #list ),*)
277 }
278 .into()
279 }
280 }
281 AutoMacrosType::EnumSinglePath { key, next_key } => quote! {
282 #ident::#key(#macro_ident!(#key 0 #next_key))
283 }
284 .into(),
285
286 AutoMacrosType::Value { items } => {
287 if let [first_item] = items.as_slice() {
288 quote! {
289 #first_item
290 }
291 .into()
292 } else {
293 let list = items
294 .iter()
295 .enumerate()
296 .map(|(index, item)| {
297 quote! {
298 #macro_ident!(#index #item)
299 }
300 })
301 .collect::<Vec<_>>();
302
303 quote! {
304 (#( #list ),*)
305 }
306 .into()
307 }
308 }
309 }
310}