extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[doc(hidden)]
#[proc_macro_derive(SmartDefault, attributes(default))]
pub fn derive_smart_default(input: TokenStream) -> TokenStream {
let ast = syn::parse_derive_input(&input.to_string()).unwrap();
impl_my_derive(&ast).parse().unwrap()
}
fn impl_my_derive(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let (default_expr, doc) = match ast.body {
syn::Body::Struct(ref body) => {
let (body_assignment, doc) = default_body_tt(body);
(quote! {
#name #body_assignment
}, format!("Return `{}{}`", name, doc))
}
syn::Body::Enum(ref variants) => {
let default_variant = find_only(variants, |variant| {
if let Some(default_attr) =
find_only(&variant.attrs, |attr| attr.name() == "default") {
if let syn::MetaItem::Word(_) = default_attr.value {
true
} else {
panic!("Attribute #[default] on variants should have no value");
}
} else {
false
}
});
let default_variant = default_variant.expect("No default variant");
let default_variant_name = &default_variant.ident;
let (body_assignment, doc) = default_body_tt(&default_variant.data);
(quote! {
#name :: #default_variant_name #body_assignment
}, format!("Return `{}::{}{}`", name, default_variant_name, doc))
}
};
quote! {
impl #impl_generics Default for #name #ty_generics #where_clause {
#[doc = #doc]
fn default() -> Self {
#default_expr
}
}
}
}
fn default_body_tt(body: &syn::VariantData) -> (quote::Tokens, String) {
let mut doc = String::new();
use std::fmt::Write;
let body_tt = match body {
&syn::VariantData::Struct(ref fields) => {
doc.push_str(" {");
let result = {
let field_assignments = fields.iter().map(|field| {
let field_name = field.ident.as_ref();
let (default_value, default_doc) = field_default_expr_and_doc(field);
write!(&mut doc, "\n {}: {},", field_name.expect("field value in struct is empty"), default_doc).unwrap();
quote! { #field_name : #default_value }
});
quote!{
{
#( #field_assignments ),*
}
}
};
if (&mut doc).ends_with(",") {
doc.pop();
doc.push('\n');
};
doc.push('}');
result
}
&syn::VariantData::Tuple(ref fields) => {
doc.push('(');
let result = {
let field_assignments = fields.iter().map(|field| {
let (default_value, default_doc) = field_default_expr_and_doc(field);
write!(&mut doc, "{}, ", default_doc).unwrap();
default_value
});
quote! {
(
#( #field_assignments ),*
)
}
};
if (&mut doc).ends_with(", ") {
doc.pop();
doc.pop();
};
doc.push(')');
result
}
&syn::VariantData::Unit => quote!{},
};
(body_tt, doc)
}
fn field_default_expr_and_doc(field: &syn::Field) -> (quote::Tokens, &str) {
if let Some(default_attr) = find_only(&field.attrs, |attr| attr.name() == "default") {
if let syn::MetaItem::NameValue(_, syn::Lit::Str(ref lit, _)) = default_attr.value {
let field_value = syn::parse_token_trees(lit).unwrap();
return (quote! {
#( #field_value )*
}, lit);
} else {
panic!("Attribute #[default] on fields must have a value");
}
}
(quote! {
Default::default()
}, "Default::default()")
}
fn find_only<T, F>(iter: &[T], pred: F) -> Option<&T>
where
F: Fn(&T) -> bool,
{
let mut result = None;
for item in iter {
if pred(item) {
assert!(result.is_none(), "Multiple defaults");
result = Some(item);
}
}
result
}