#![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 {
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);
expanded.to_string().parse().unwrap()
}
fn expand_setters(mut ast: syn::MacroInput) -> quote::Tokens {
let setters_attrs = extract_attrs(&mut ast.attrs, "setters");
let config = config_from(&setters_attrs, &["into"]);
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
}