1 | use proc_macro2::TokenStream; |
2 | |
3 | use quote::quote; |
4 | use syn::parse::Error; |
5 | use syn::spanned::Spanned; |
6 | use syn::DeriveInput; |
7 | |
8 | use crate::default_attr::{ConversionStrategy, DefaultAttr}; |
9 | use crate::util::find_only; |
10 | |
11 | pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> { |
12 | let name = &input.ident; |
13 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); |
14 | |
15 | let (default_expr, doc) = match input.data { |
16 | syn::Data::Struct(ref body) => { |
17 | let (body_assignment, doc) = default_body_tt(&body.fields)?; |
18 | ( |
19 | quote! { |
20 | #name #body_assignment |
21 | }, |
22 | format!("Return ` {}{}`" , name, doc), |
23 | ) |
24 | } |
25 | syn::Data::Enum(ref body) => { |
26 | let default_variant = find_only(body.variants.iter(), |variant| { |
27 | if let Some(meta) = DefaultAttr::find_in_attributes(&variant.attrs)? { |
28 | if meta.code.is_none() { |
29 | Ok(true) |
30 | } else { |
31 | Err(Error::new( |
32 | meta.code.span(), |
33 | "Attribute #[default] on variants should have no value" , |
34 | )) |
35 | } |
36 | } else { |
37 | Ok(false) |
38 | } |
39 | })? |
40 | .ok_or_else(|| Error::new(input.span(), "No default variant" ))?; |
41 | let default_variant_name = &default_variant.ident; |
42 | let (body_assignment, doc) = default_body_tt(&default_variant.fields)?; |
43 | ( |
44 | quote! { |
45 | #name :: #default_variant_name #body_assignment |
46 | }, |
47 | format!("Return ` {}:: {}{}`" , name, default_variant_name, doc), |
48 | ) |
49 | } |
50 | syn::Data::Union(_) => { |
51 | panic!() |
52 | } |
53 | }; |
54 | Ok(quote! { |
55 | #[automatically_derived] |
56 | impl #impl_generics Default for #name #ty_generics #where_clause { |
57 | #[doc = #doc] |
58 | fn default() -> Self { |
59 | #default_expr |
60 | } |
61 | } |
62 | }) |
63 | } |
64 | |
65 | /// Return a token-tree for the default "body" - the part after the name that contains the values. |
66 | /// That is, the `{ ... }` part for structs, the `(...)` part for tuples, and nothing for units. |
67 | fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> { |
68 | let mut doc = String::new(); |
69 | use std::fmt::Write; |
70 | let body_tt = match body { |
71 | syn::Fields::Named(ref fields) => { |
72 | doc.push_str(" {" ); |
73 | let result = { |
74 | let field_assignments = fields |
75 | .named |
76 | .iter() |
77 | .map(|field| { |
78 | let field_name = field.ident.as_ref(); |
79 | let (default_value, default_doc) = field_default_expr_and_doc(field)?; |
80 | write!( |
81 | &mut doc, |
82 | " \n {}: {}," , |
83 | field_name.expect("field value in struct is empty" ), |
84 | default_doc |
85 | ) |
86 | .unwrap(); |
87 | // let default_value = default_value.into_token_stream(); |
88 | Ok(quote! { #field_name : #default_value }) |
89 | }) |
90 | .collect::<Result<Vec<_>, Error>>()?; |
91 | quote! { |
92 | { |
93 | #( #field_assignments ),* |
94 | } |
95 | } |
96 | }; |
97 | if doc.ends_with(',' ) { |
98 | doc.pop(); |
99 | doc.push(' \n' ); |
100 | }; |
101 | doc.push('}' ); |
102 | result |
103 | } |
104 | syn::Fields::Unnamed(ref fields) => { |
105 | doc.push('(' ); |
106 | let result = { |
107 | let field_assignments = fields |
108 | .unnamed |
109 | .iter() |
110 | .map(|field| { |
111 | let (default_value, default_doc) = field_default_expr_and_doc(field)?; |
112 | write!(&mut doc, " {}, " , default_doc).unwrap(); |
113 | Ok(default_value) |
114 | }) |
115 | .collect::<Result<Vec<TokenStream>, Error>>()?; |
116 | quote! { |
117 | ( |
118 | #( #field_assignments ),* |
119 | ) |
120 | } |
121 | }; |
122 | if doc.ends_with(", " ) { |
123 | doc.pop(); |
124 | doc.pop(); |
125 | }; |
126 | doc.push(')' ); |
127 | result |
128 | } |
129 | &syn::Fields::Unit => quote! {}, |
130 | }; |
131 | Ok((body_tt, doc)) |
132 | } |
133 | |
134 | /// Return a default expression for a field based on it's `#[default = "..."]` attribute. Panic |
135 | /// if there is more than one, of if there is a `#[default]` attribute without value. |
136 | fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String), Error> { |
137 | if let Some(default_attr: DefaultAttr) = DefaultAttr::find_in_attributes(&field.attrs)? { |
138 | let conversion_strategy: ConversionStrategy = default_attr.conversion_strategy(); |
139 | let field_value: TokenStream = default_attr.code.ok_or_else(|| { |
140 | Error::new(field.span(), message:"Expected #[default = ...] or #[default(...)]" ) |
141 | })?; |
142 | |
143 | let field_value: TokenStream = match conversion_strategy { |
144 | ConversionStrategy::NoConversion => field_value, |
145 | ConversionStrategy::Into => quote!((#field_value).into()), |
146 | }; |
147 | |
148 | let field_doc: String = format!(" {}" , field_value); |
149 | Ok((field_value, field_doc)) |
150 | } else { |
151 | Ok(( |
152 | quote! { |
153 | Default::default() |
154 | }, |
155 | "Default::default()" .to_owned(), |
156 | )) |
157 | } |
158 | } |
159 | |