1use proc_macro2::TokenStream;
2
3use quote::quote;
4use syn::parse::Error;
5use syn::spanned::Spanned;
6use syn::DeriveInput;
7
8use crate::default_attr::{ConversionStrategy, DefaultAttr};
9use crate::util::find_only;
10
11pub 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.
67fn 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.
136fn 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