| 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 | |