diff --git a/Cargo.toml b/Cargo.toml index 3355a22..48cf57f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,5 @@ proc-macro = true test = false [dependencies] -log = "0.3.6" -syn = "0.9.0" -quote = "0.3.3" +syn = "^0.10.0" +quote = "^0.3.3" diff --git a/examples/simple.rs b/examples/simple.rs index 3e15d20..f8ad937 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -6,11 +6,30 @@ extern crate accessors; #[derive(getters, setters)] #[setters(into)] struct Simple { - field: String, + normal_field: String, + + #[getter(ignore)] + ignored_field: String, + + #[getter(return_type = "&str")] + custom_return_type_field: String, +} + +impl Simple { + fn ignored_field(&self) -> &str { + &self.ignored_field + } } fn main() { - let mut s = Simple { field: "hello".to_owned() }; - println!("{}", s.field()); - s.set_field("there"); + let mut s = Simple { + normal_field: "hello".to_owned(), + ignored_field: "".to_string(), + custom_return_type_field: "World".to_string() + }; + + println!("{}", s.normal_field()); + s.set_normal_field("there"); + + let _: &str = s.custom_return_type_field(); } diff --git a/src/lib.rs b/src/lib.rs index a1f90f7..625ef4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,6 @@ // good refactoring once I figure out the basic ideas. Do not use use this // as an example of good style. -#[macro_use] -extern crate log; extern crate proc_macro; #[macro_use] extern crate quote; @@ -19,46 +17,62 @@ extern crate syn; use proc_macro::TokenStream; use std::collections::BTreeMap; -#[proc_macro_derive(getters)] +#[proc_macro_derive(getters, attributes(getter, 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 { +fn expand_getters(ast: syn::MacroInput) -> quote::Tokens { // println!("Defining getters for: {:#?}", ast); - extract_attrs(&mut ast.attrs, "getters"); + let name = &ast.ident; + let (impl_generics, ty_generics, where_clause) = ast.generics + .split_for_impl(); - let fields: Vec<_> = match ast.body { + let getters = match ast.body { syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { - fields.iter().map(|f| (f.ident.as_ref().unwrap(), &f.ty)).collect() + fields.iter().filter_map(|f| { + let field_name = f.ident.as_ref().unwrap(); + let field_ty = &f.ty; + + let mut field_attrs = f.attrs.iter().filter(|a| a.name() == "getter"); + let config = config_from(&mut field_attrs, &["ignore", "return_type"]); + + let ignore_default = syn::Lit::Bool(false); + let ignore = match *config.get("ignore").unwrap_or(&ignore_default) { + syn::Lit::Bool(b) => b, + ref val => panic!("'ignore' must be a boolean value, not {:?}", val), + }; + if ignore { + return None; + } + + let return_type = match config.get("return_type") { + Some(&syn::Lit::Str(ref v, _)) => syn::parse_type(v).unwrap(), + None => syn::parse_type("e!(&#field_ty).to_string()).unwrap(), + ref val => panic!("'return_type' must be a string value, not {:?}", val), + }; + + Some(quote! { + pub fn #field_name(&self) -> #return_type { + &self.#field_name + } + }) + }) } _ => 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 - } - )* + #(#getters)* } } } -#[proc_macro_derive(setters)] +#[proc_macro_derive(setters, attributes(setter, setters))] pub fn derive_setters(input: TokenStream) -> TokenStream { let ast = syn::parse_macro_input(&input.to_string()).unwrap(); let expanded = expand_setters(ast); @@ -66,11 +80,11 @@ pub fn derive_setters(input: TokenStream) -> TokenStream { expanded.to_string().parse().unwrap() } -fn expand_setters(mut ast: syn::MacroInput) -> quote::Tokens { +fn expand_setters(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"]); + let mut setters_attrs = ast.attrs.iter().filter(|a| a.name() == "setters"); + let config = config_from(&mut setters_attrs, &["into"]); // println!("Config: {:#?}", &config); let into_default = syn::Lit::Bool(false); let into = match *config.get("into").unwrap_or(&into_default) { @@ -78,74 +92,79 @@ fn expand_setters(mut ast: syn::MacroInput) -> quote::Tokens { 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(&mut self, value: T) - where T: Into<#ty> - { - self.#field_name = value.into(); - } + + let setters = match ast.body { + syn::Body::Struct(syn::VariantData::Struct(ref fields)) => { + fields.iter().filter_map(|f| { + let field_name = f.ident.as_ref().unwrap(); + let field_ty = &f.ty; + + let mut field_attrs = f.attrs.iter().filter(|a| a.name() == "setter"); + let config = config_from(&mut field_attrs, &["ignore"]); + + let ignore_default = syn::Lit::Bool(false); + let ignore = match *config.get("ignore").unwrap_or(&ignore_default) { + syn::Lit::Bool(b) => b, + ref val => panic!("'ignore' must be a boolean value, not {:?}", val), + }; + if ignore { + return None; } - } else { - quote! { - pub fn #set_fn_name(&mut self, value: #ty) { - self.#field_name = value; - } + + let set_fn_name: syn::Ident = format!("set_{}", field_name).into(); + if into { + Some(quote! { + pub fn #set_fn_name(&mut self, value: T) + where T: Into<#field_ty> + { + self.#field_name = value.into(); + } + }) + } else { + Some(quote! { + pub fn #set_fn_name(&mut self, value: #field_ty) { + self.#field_name = value; + } + }) } - } - }) - .collect(); + }) + } + _ => panic!("#[derive(setters)] can only be used with braced structs"), + }; quote! { - #ast - impl #impl_generics #name #ty_generics #where_clause { #(#setters)* } } } -fn extract_attrs(attrs: &mut Vec, - name: &str) - -> Vec { - let extracted = - attrs.iter().filter(|a| a.name() == name).cloned().collect(); - attrs.retain(|a| a.name() != name); - extracted -} - -fn config_from(attrs: &[syn::Attribute], +fn config_from(attrs: &mut Iterator, keys: &[&str]) -> BTreeMap { let mut result = BTreeMap::new(); - for attr in attrs { + while let Some(attr) = attrs.next() { 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); - } - match *arg { - syn::MetaItem::Word(_) => { - result.insert(name.to_owned(), syn::Lit::Bool(true)); + if let syn::NestedMetaItem::MetaItem(ref meta_item) = *arg { + let name = meta_item.name(); + if !keys.contains(&name) { + panic!("'{}' in {:?} is not a known attribute", name, attr); } - syn::MetaItem::NameValue(_, ref value) => { - result.insert(name.to_owned(), value.to_owned()); + match *meta_item { + syn::MetaItem::Word(_) => { + result.insert(name.to_owned(), syn::Lit::Bool(true)); + } + syn::MetaItem::NameValue(_, ref value) => { + result.insert(name.to_owned(), value.to_owned()); + } + _ => panic!("can't parse '{:?}'", &arg), } - _ => panic!("can't parse '{:?}'", &arg), + } else { + panic!("'{:?}' in {:?} is not a known attribute", arg, attr); } } } else {