1use convert_case::{Case, Casing};
2use darling::*;
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{Ident, Meta, parse_macro_input};
6
7#[derive(Debug, FromField)]
8#[darling(attributes(define))]
9struct Field {
10 ident: Option<syn::Ident>,
11 ty: syn::Type,
12}
13
14#[derive(Debug, FromMeta)]
15enum Type {
16 Ty(syn::Path),
17 With {
18 ty: syn::Path,
19
20 #[darling(default, multiple)]
21 derives: Vec<syn::Path>,
22
23 #[darling(default, multiple)]
24 attr: Vec<String>,
25 },
26}
27
28impl Type {
29 fn ident(&self) -> &syn::Path {
30 match self {
31 Self::Ty(path) => path,
32 Self::With { ty, .. } => ty,
33 }
34 }
35
36 fn derives(&self) -> Vec<syn::Path> {
37 match self {
38 Self::With { derives, .. } => derives.clone(),
39 _ => vec![],
40 }
41 }
42
43 fn attrs(&self) -> Vec<String> {
44 match self {
45 Self::With { attr, .. } => attr.clone(),
46 _ => vec![],
47 }
48 }
49}
50
51#[derive(Debug, FromDeriveInput)]
52#[darling(attributes(define), supports(struct_any))]
53struct Input {
54 vis: syn::Visibility,
55 ident: syn::Ident,
56 generics: syn::Generics,
57 data: ast::Data<(), Field>,
58
59 #[darling(default, multiple)]
60 types: Vec<Type>,
61
62 #[darling(default, multiple)]
63 derives: Vec<syn::Path>,
64
65 #[darling(default, multiple)]
66 attr: Vec<String>,
67
68 #[darling(default)]
69 sealed: bool,
70}
71
72impl Input {
73 fn merge<T: Clone>(a1: Vec<T>, a2: &Vec<T>) -> Vec<T> {
74 let mut a1 = a1;
75 let mut a2 = a2.clone();
76 a1.append(&mut a2);
77 a1
78 }
79
80 fn derives(&self, derives: Vec<syn::Path>) -> Vec<syn::Path> {
81 Self::merge(derives, &self.derives)
82 }
83
84 fn attrs(&self, attrs: Vec<String>) -> Vec<String> {
85 Self::merge(attrs, &self.attr)
86 }
87}
88
89enum TypeOrGeneric {
90 Type(proc_macro2::TokenStream),
91 Generic(Ident),
92}
93
94impl ToTokens for Input {
95 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
96 let ident = self.ident.clone();
97
98 let vis = self.vis.clone();
99 let mut tys = vec![];
100 for param in &self.generics.params {
101 match param {
102 syn::GenericParam::Const(..) => {}
103 syn::GenericParam::Lifetime(..) => {}
104 syn::GenericParam::Type(ty) => tys.push(ty.ident.clone()),
105 }
106 }
107
108 let mut fields = vec![];
109 let mut fields_eq = vec![];
110
111 for f in self.data.as_ref().take_struct().unwrap() {
112 let field = f.ident.clone().unwrap();
113 let ty = f.ty.clone();
114 let ident = Ident::from_string(
115 "e!(
116 #ty
117 )
118 .to_string(),
119 )
120 .unwrap();
121
122 fields_eq.push(quote!(
123 #field: value.#field,
124 ));
125 if tys.contains(&ident) {
126 fields.push(TypeOrGeneric::Generic(field))
127 } else {
129 fields.push(TypeOrGeneric::Type(quote!(
130 #field: #ty,
131 )))
132 }
133 }
134
135 let fields_eq: proc_macro2::TokenStream = fields_eq.into_iter().collect();
136
137 let seal_mod =
138 Ident::from_string(&("Sealed".to_string() + &ident.to_string()).to_case(Case::Pascal))
139 .unwrap();
140
141 if self.sealed {
142 tokens.extend(quote!(
143 pub(crate) trait #seal_mod {}
144 ));
145 }
146
147 for t in &self.types {
148 let tt = t.ident();
149 let new_ident = Ident::from_string(
150 &format!(
151 "{}{}",
152 ident.to_string(),
153 quote!(#tt).to_string().to_case(Case::Pascal)
154 )
155 .to_case(Case::Pascal),
156 )
157 .unwrap();
158
159 let new_fields: proc_macro2::TokenStream = fields
160 .iter()
161 .map(|it| match it {
162 TypeOrGeneric::Generic(field) => {
163 quote!(
164 #field: #tt,
165 )
166 }
167 TypeOrGeneric::Type(tt) => tt.clone(),
168 })
169 .collect();
170
171 let derives: proc_macro2::TokenStream = self
172 .derives(t.derives())
173 .iter()
174 .map(|it| quote::quote!(#it,))
175 .collect();
176
177 let atts: proc_macro2::TokenStream = self
178 .attrs(t.attrs())
179 .iter()
180 .map(|it| {
181 let it: Meta = syn::parse_str(&it).unwrap();
182 quote::quote!(#[#it])
183 })
184 .collect();
185
186 tokens.extend(quote! {
187 #[derive(#derives)]
188 #atts
189 #vis struct #new_ident {
190 #new_fields
191 }
192 });
193
194 if self.sealed {
195 tokens.extend(quote!(
196 impl #seal_mod for #tt {}
197 ));
198 }
199
200 tokens.extend(quote! {
201 impl From<#new_ident> for #ident<#tt> {
202 fn from(value: #new_ident) -> Self {
203 Self {
204 #fields_eq
205 }
206 }
207 }
208
209 impl From<#ident<#tt>> for #new_ident {
210 fn from(value: #ident<#tt>) -> Self {
211 Self {
212 #fields_eq
213 }
214 }
215 }
216 })
217 }
218 }
219}
220
221#[proc_macro_derive(SealedGeneric, attributes(define))]
222pub fn def_gen(input: TokenStream) -> TokenStream {
223 let parsed = Input::from_derive_input(&parse_macro_input!(input)).unwrap();
224 quote::quote!(#parsed).into()
225}