1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Support for `#[derive(accesors)]`.  Based on the [example code][] for
//! syn.
//!
//! [example code]: https://github.com/dtolnay/syn

#![feature(proc_macro, proc_macro_lib)]

#[macro_use]
extern crate log;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use std::collections::BTreeMap;

#[proc_macro_derive(getters)]
pub fn derive_getters(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input(&input.to_string()).unwrap();
    let expanded = expand_getters(ast);
    expanded.to_string().parse().unwrap()
}

fn expand_getters(mut ast: syn::MacroInput) -> quote::Tokens {
    // println!("Defining getters for: {:#?}", ast);

    extract_attrs(&mut ast.attrs, "getters");

    let fields: Vec<_> = match ast.body {
        syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
            fields.iter().map(|f| (f.ident.as_ref().unwrap(), &f.ty)).collect()
        }
        _ => panic!("#[derive(getters)] can only be used with braced structs"),
    };

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics
        .split_for_impl();
    let getter: Vec<_> = fields.iter().map(|f| f.0).collect();
    let field: Vec<_> = fields.iter().map(|f| f.0).collect();
    let ty: Vec<_> = fields.iter().map(|f| f.1).collect();

    quote! {
        #ast

        impl #impl_generics #name #ty_generics #where_clause {
            #(
                pub fn #getter(&self) -> &#ty {
                    &self.#field
                }
            )*
        }
    }
}

#[proc_macro_derive(setters)]
pub fn derive_setters(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input(&input.to_string()).unwrap();
    let expanded = expand_setters(ast);
    // println!("Expanded: {}", expanded.to_string());
    expanded.to_string().parse().unwrap()
}

fn expand_setters(mut ast: syn::MacroInput) -> quote::Tokens {
    // println!("Defining setters for: {:#?}", ast);

    let setters_attrs = extract_attrs(&mut ast.attrs, "setters");
    let config = config_from(&setters_attrs, &["into"]);
    // println!("Config: {:#?}", &config);
    let into_default = syn::Lit::Bool(false);
    let into = match *config.get("into").unwrap_or(&into_default) {
        syn::Lit::Bool(b) => b,
        ref val => panic!("'into' must be a boolean value, not {:?}", val),
    };

    let fields: Vec<_> = match ast.body {
        syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
            fields.iter().map(|f| (f.ident.as_ref().unwrap(), &f.ty)).collect()
        }
        _ => panic!("#[derive(setters)] can only be used with braced structs"),
    };

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics
        .split_for_impl();
    let setters: Vec<_> = fields.iter()
        .map(|&(ref field_name, ref ty)| {
            let set_fn_name: syn::Ident = format!("set_{}", field_name).into();
            if into {
                quote! {
                    pub fn #set_fn_name<T>(&mut self, value: T)
                        where T: Into<#ty>
                    {
                        self.#field_name = value.into();
                    }
                }
            } else {
                quote! {
                    pub fn #set_fn_name(&mut self, value: #ty) {
                        self.#field_name = value;
                    }
                }
            }
        })
        .collect();

    quote! {
        #ast

        impl #impl_generics #name #ty_generics #where_clause {
            #(#setters)*
        }
    }
}

fn extract_attrs(attrs: &mut Vec<syn::Attribute>,
                 name: &str)
                 -> Vec<syn::Attribute> {
    let extracted =
        attrs.iter().filter(|a| a.name() == name).cloned().collect();
    attrs.retain(|a| a.name() != name);
    extracted
}

fn config_from(attrs: &[syn::Attribute],
               keys: &[&str])
               -> BTreeMap<String, syn::Lit> {
    let mut result = BTreeMap::new();
    for attr in attrs {
        if let syn::MetaItem::List(_, ref args) = attr.value {
            for arg in args {
                let name = arg.name();
                if !keys.contains(&name) {
                    panic!("'{}' in {:?} is not a known attribute", name, attr);
                }
                if let syn::MetaItem::NameValue(_, ref value) = *arg {
                    result.insert(name.to_owned(), value.to_owned());
                } else {
                    panic!("'{:?}' must be a key-vaue pair", &arg);
                }
            }
        } else {
            panic!("{:?} must be a key-value attribute", attr);
        }
    }
    result
}