1use proc_macro::TokenStream;
6use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
7use quote::{quote, ToTokens};
8use syn::{
9 parse_macro_input,
10 parse_quote,
11 spanned::Spanned,
12 Attribute,
13 Data,
14 DataEnum,
15 DataStruct,
16 DeriveInput,
17 Error,
18 Fields,
19 GenericParam,
20 Index,
21 Meta,
22 Type,
23 Variant,
24};
25
26use crate::numbers_to_words::encode_ordinal;
27
28mod numbers_to_words;
29
30#[proc_macro_derive(Default, attributes(default))]
112pub fn default(input: TokenStream) -> TokenStream {
113 let input = parse_macro_input!(input as DeriveInput);
114
115 let DeriveInput {
116 mut generics,
117 ident,
118 data,
119 ..
120 } = input;
121
122 for param in &mut generics.params {
123 if let GenericParam::Type(param) = param {
124 param.bounds.push(parse_quote!(::core::default::Default))
125 }
126 }
127
128 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
129
130 let body = match data {
131 Data::Struct(DataStruct { fields, .. }) => {
132 let init = expand_default_for_fields(fields);
133
134 quote!(Self #init)
135 },
136
137 Data::Enum(r#enum) => expand_default_for_enum(r#enum).unwrap_or_else(|error| error.into_compile_error()),
138
139 Data::Union(_) => unimplemented!("unions are not supported"),
140 };
141
142 let tokens = quote! {
143 impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
144 fn default() -> Self {
145 #body
146 }
147 }
148 };
149
150 tokens.into()
151}
152
153fn expand_default_for_enum(r#enum: DataEnum) -> syn::Result<TokenStream2> {
154 let variant = {
155 let mut variant = None;
156
157 for var in r#enum.variants {
158 for attr in &var.attrs {
159 if attr.meta.require_path_only().map(|path| path.is_ident("default"))? {
160 match &variant {
161 None => variant = Some(var),
162 Some(_) => return Err(Error::new(attr.span(), "conflicting #[default] attribute")),
163 }
164
165 break;
166 }
167 }
168 }
169
170 variant
171 };
172
173 match variant {
174 Some(Variant { ident, fields, .. }) => {
175 let init = expand_default_for_fields(fields);
176
177 Ok(quote!(Self::#ident #init))
178 },
179
180 None => Err(Error::new(
181 Span::call_site(),
182 "one enum variant must have a #[default] attribute",
183 )),
184 }
185}
186
187fn expand_default_for_fields(fields: Fields) -> Option<TokenStream2> {
188 match fields {
189 Fields::Unit => None,
190
191 Fields::Named(fields) => {
192 let field_init = fields.named.into_pairs().map(|pair| {
193 let (field, comma) = pair.into_tuple();
194
195 let (ident, ty, attrs) = (field.ident, field.ty, field.attrs);
196
197 let value = field_value(ty, attrs);
198
199 quote!(#ident: #value #comma)
200 });
201
202 Some(quote!({ #(#field_init)* }))
203 },
204
205 Fields::Unnamed(fields) => {
206 let field_init = fields.unnamed.into_pairs().map(|pair| {
207 let (field, comma) = pair.into_tuple();
208
209 let value = field_value(field.ty, field.attrs);
210
211 quote!(#value #comma)
212 });
213
214 Some(quote!((#(#field_init)*)))
215 },
216 }
217}
218
219fn field_value(ty: Type, attrs: Vec<Attribute>) -> TokenStream2 {
220 let default_attr = attrs
221 .into_iter()
222 .find(|attribute| attribute.meta.path().is_ident("default"));
223
224 match default_attr {
225 Some(default_attr) => match default_attr.meta {
227 Meta::Path(path) => Error::new(
228 path.span(),
229 format!(
230 "expected a value for this attribute: `{}(...)` or `{} = ...`",
231 "default", "default",
232 ),
233 )
234 .into_compile_error(),
235
236 Meta::List(meta) => {
237 let tokens = meta.tokens;
238
239 quote!({ #tokens })
240 },
241
242 Meta::NameValue(meta) => meta.value.into_token_stream(),
243 },
244
245 None => {
247 quote!(<#ty as ::core::default::Default>::default())
248 },
249 }
250}
251
252#[proc_macro_derive(builder, attributes(new))]
340pub fn builder(input: TokenStream) -> TokenStream {
341 let input = parse_macro_input!(input as DeriveInput);
342
343 let tokens = match input.data {
344 Data::Struct(r#struct) => {
345 let name = input.ident;
346
347 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
348
349 let new = input.attrs.iter().any(|attr| attr.path().is_ident("new")).then(|| {
350 quote! {
351 #[doc = concat!("Creates a new `", stringify!(#name), "`.")]
352 #[doc = ""]
353 #[doc = concat!("This is equivalent to <code>", stringify!(#name), "::[default()]</code>.")]
354 #[doc = ""]
355 #[doc = "[default()]: ::core::default::Default::default()"]
356 pub fn new() -> Self
357 where
358 Self: ::core::default::Default,
359 {
360 <Self as ::core::default::Default>::default()
361 }
362 }
363 });
364
365 let config_methods = match &r#struct.fields {
366 Fields::Unit => None,
367
368 Fields::Named(fields) => {
370 let methods: TokenStream2 = fields
371 .named
372 .iter()
373 .map(|field| {
374 let vis = &field.vis;
375 let ty = &field.ty;
376
377 let ident = &field.ident;
378
379 let docs = ident
380 .as_ref()
381 .map(|ident| format!("Sets `{ident}` to the given value."));
382
383 quote! {
384 #[doc = #docs]
385 #vis fn #ident(mut self, #ident: #ty) -> Self {
386 self.#ident = #ident;
387
388 self
389 }
390 }
391 })
392 .collect();
393
394 Some(methods)
395 },
396
397 Fields::Unnamed(fields) => {
399 let methods = fields
400 .unnamed
401 .iter()
402 .enumerate()
403 .map(|(i, field)| {
404 let vis = &field.vis;
405 let ty = &field.ty;
406
407 let index = Index::from(i);
408 let ident = Ident::new(&encode_ordinal(i + 1, '_'), Span::call_site());
409
410 let ordinal = encode_ordinal(i + 1, ' ');
411 let docs = format!("Sets the {ordinal} field to the given value.");
412
413 quote! {
414 #[doc = #docs]
415 #vis fn #ident(mut self, #ident: #ty) -> Self {
416 self.#index = #ident;
417
418 self
419 }
420 }
421 })
422 .collect();
423
424 Some(methods)
425 },
426 };
427
428 quote! {
430 impl #impl_generics #name #ty_generics #where_clause {
431 #new
432
433 #config_methods
434 }
435 }
436 },
437
438 Data::Enum(_enum) => unimplemented!("enums are not supported"),
439 Data::Union(_union) => unimplemented!("unions are not supported"),
440 };
441
442 tokens.into()
443}