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;
match ast.body {
syn::Body::Struct(ref body) => {
let body_assignment = default_body_tt(body);
quote! {
impl Default for #name {
fn default() -> Self {
#name #body_assignment
}
}
}
}
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 = default_body_tt(&default_variant.data);
quote! {
impl Default for #name {
fn default() -> Self {
#name :: #default_variant_name #body_assignment
}
}
}
}
}
}
fn default_body_tt(body: &syn::VariantData) -> quote::Tokens {
match body {
&syn::VariantData::Struct(ref fields) => {
let field_assignments = fields.iter().map(|field| {
let field_name = field.ident.as_ref();
let default = field_default_expr(field);
quote! { #field_name : #default }
});
quote!{
{
#( #field_assignments ),*
}
}
}
&syn::VariantData::Tuple(ref fields) => {
let field_assignments = fields.iter().map(|field| field_default_expr(field));
quote! {
(
#( #field_assignments ),*
)
}
}
&syn::VariantData::Unit => quote!{},
}
}
fn field_default_expr(field: &syn::Field) -> quote::Tokens {
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 )*
};
} else {
panic!("Attribute #[default] on fields must have a value");
}
}
quote! {
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
}