1use convert_case::{Case, Casing};
2use darling::*;
3use proc_macro::TokenStream;
4use quote::quote;
5use std::collections::HashMap;
6use syn::{Ident, Meta, TypePath, parse_macro_input};
7
8#[derive(Debug, FromField)]
9#[darling(attributes(define))]
10struct Field {
11 ident: Option<syn::Ident>,
12 ty: syn::Type,
13}
14
15#[derive(Debug, FromMeta)]
16enum Type {
17 Ty(syn::Path),
18 With {
19 ty: syn::Path,
20
21 #[darling(default, multiple)]
22 derives: Vec<syn::Path>,
23
24 #[darling(default, multiple)]
25 attr: Vec<String>,
26 },
27}
28
29impl Type {
30 fn ident(&self) -> &syn::Path {
31 match self {
32 Self::Ty(path) => path,
33 Self::With { ty, .. } => ty,
34 }
35 }
36
37 fn derives(&self) -> Vec<syn::Path> {
38 match self {
39 Self::With { derives, .. } => derives.clone(),
40 _ => vec![],
41 }
42 }
43
44 fn attrs(&self) -> Vec<String> {
45 match self {
46 Self::With { attr, .. } => attr.clone(),
47 _ => vec![],
48 }
49 }
50}
51
52#[derive(Debug, FromDeriveInput)]
53#[darling(attributes(define), supports(struct_any))]
54struct Input {
55 vis: syn::Visibility,
56 ident: syn::Ident,
57 generics: syn::Generics,
58 data: ast::Data<(), Field>,
59
60 #[darling(default, multiple)]
61 types: Vec<Type>,
62
63 #[darling(default, multiple)]
64 derives: Vec<syn::Path>,
65
66 #[darling(default, multiple)]
67 attr: Vec<String>,
68
69 #[darling(default)]
70 sealed: bool,
71}
72
73impl Input {
74 fn merge<T: Clone>(a1: Vec<T>, a2: &Vec<T>) -> Vec<T> {
75 let mut a1 = a1;
76 let mut a2 = a2.clone();
77 a1.append(&mut a2);
78 a1
79 }
80
81 fn derives(&self, derives: Vec<syn::Path>) -> Vec<syn::Path> {
82 Self::merge(derives, &self.derives)
83 }
84
85 fn attrs(&self, attrs: Vec<String>) -> Vec<String> {
86 Self::merge(attrs, &self.attr)
87 }
88}
89
90enum TypeOrGeneric {
91 Type { field: Ident, ty: syn::Type },
92 Generic(Ident),
93 GenericWithNested { field: Ident, path: TypePath },
94}
95
96impl ToTokens for Input {
97 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
98 let ident = self.ident.clone();
99
100 let vis = self.vis.clone();
101
102 let generic: Ident;
103
104 let tys = self.generics.type_params().collect::<Vec<_>>();
105 let lts = self.generics.lifetimes().collect::<Vec<_>>();
106
107 if tys.len() > 1 {
108 panic!("only one generic type may be specified")
109 }
110
111 generic = tys.get(0).unwrap().ident.clone();
112
113 let mut fields = vec![];
114 let mut fields_eq = vec![];
115 let mut field_lts = HashMap::new();
116
117 for f in self.data.as_ref().take_struct().expect("struct") {
118 let field = f.ident.clone().expect("ident");
119 let ty = f.ty.clone();
120
121 fields_eq.push(quote!(
122 #field: value.#field,
123 ));
124
125 let mut with_path = |path: &syn::TypePath| {
126 let segment = path.path.segments.first().expect("path segment");
127 let ident = segment.ident.clone();
128 let field = field.clone();
129 let ty = ty.clone();
130 if generic == ident {
131 fields.push(TypeOrGeneric::Generic(field))
132 } else if !segment.arguments.is_empty() {
133 fields.push(TypeOrGeneric::GenericWithNested {
134 field: field,
135 path: path.clone(),
136 })
137 } else {
138 fields.push(TypeOrGeneric::Type { field, ty })
139 }
140 };
141
142 match ty.clone() {
143 syn::Type::Reference(typeref) => {
144 if let Some(lt) = typeref.lifetime {
145 field_lts.insert(field.clone(), lt);
146 }
147 match *typeref.elem {
148 syn::Type::Path(ref path) => with_path(path),
149 ty => panic!("unknown type: {ty:#?}"),
150 }
151 }
152 syn::Type::Path(path) => {
153 with_path(&path);
154 }
155 _ => panic!("unknown type"),
156 }
157 }
158
159 let fields_eq: proc_macro2::TokenStream = fields_eq.into_iter().collect();
160
161 let seal_mod =
162 Ident::from_string(&("Sealed".to_string() + &ident.to_string()).to_case(Case::Pascal))
163 .expect("sealed trait ident to parse");
164
165 if self.sealed {
166 tokens.extend(quote!(
167 pub(crate) trait #seal_mod {}
168 ));
169 }
170
171 let get_field_lt = |field: &Ident, ty: proc_macro2::TokenStream| {
172 let field_pre = if let Some(lt) = field_lts.get(&field) {
173 let ex: proc_macro2::TokenStream = format!("&{} {}", quote!(#lt), ty.to_string())
174 .parse()
175 .expect("type with lifetime");
176
177 quote!(#field: #ex,)
178 } else {
179 quote!(
180 #field: #ty,
181 )
182 .into()
183 };
184
185 field_pre
186 };
187
188 let has_lts = !lts.is_empty();
189 let lts: proc_macro2::TokenStream = lts.iter().map(|lt| quote!(#lt,)).collect();
190 let lt_quo = if has_lts { quote!(<#lts>) } else { quote!() };
191
192 for t in &self.types {
193 let tt = t.ident();
194 let new_ident = Ident::from_string(
195 &format!(
196 "{}{}",
197 ident.to_string(),
198 quote!(#tt).to_string().to_case(Case::Pascal)
199 )
200 .to_case(Case::Pascal),
201 )
202 .expect("type ident to parse");
203
204 let new_fields: proc_macro2::TokenStream = fields
205 .iter()
206 .map(|it| match it {
207 TypeOrGeneric::Generic(field) => get_field_lt(field, quote!(#tt)),
208 TypeOrGeneric::GenericWithNested { field, path } => {
209 let new_ty = quote!(#path).to_string().replace(" ", "").replace(
210 &format!("<{}>", generic.to_string()),
211 "e!(<#tt>).to_string(),
212 );
213 let as_path = syn::Path::from_string(&new_ty).unwrap();
214 get_field_lt(field, quote!(#as_path))
215 }
216 TypeOrGeneric::Type { field, ty } => get_field_lt(field, quote!(#ty)),
217 })
218 .collect();
219
220 let derives: proc_macro2::TokenStream = self
221 .derives(t.derives())
222 .iter()
223 .map(|it| quote::quote!(#it,))
224 .collect();
225
226 let atts: proc_macro2::TokenStream = self
227 .attrs(t.attrs())
228 .iter()
229 .map(|it| {
230 let it: Meta = syn::parse_str(&it).expect("meta to parse");
231 quote::quote!(#[#it])
232 })
233 .collect();
234
235 tokens.extend(quote! {
236 #[derive(#derives)]
237 #atts
238 #vis struct #new_ident #lt_quo {
239 #new_fields
240 }
241 });
242
243 if self.sealed {
244 tokens.extend(quote!(
245 impl #lt_quo #seal_mod for #tt #lt_quo {}
246 ));
247 }
248
249 tokens.extend(quote! {
250 impl #lt_quo From<#new_ident #lt_quo> for #ident<#lts #tt> {
251 fn from(value: #new_ident #lt_quo) -> Self {
252 Self {
253 #fields_eq
254 }
255 }
256 }
257
258 impl #lt_quo From<#ident<#lts #tt>> for #new_ident #lt_quo {
259 fn from(value: #ident<#lts #tt>) -> Self {
260 Self {
261 #fields_eq
262 }
263 }
264 }
265 })
266 }
267 }
268}
269
270#[proc_macro_derive(SealedGeneric, attributes(define))]
271pub fn def_gen(input: TokenStream) -> TokenStream {
272 let parsed = Input::from_derive_input(&parse_macro_input!(input)).expect("parse");
273 quote::quote!(#parsed).into()
274}