option_constructor_derive/
lib.rs

1//! Example:
2//!
3//! ```
4//! #[macro_use]
5//! extern crate option_constructor_derive;
6//!
7//! #[derive(OptionConstructor, Debug, PartialEq)]
8//! struct Example {
9//!     field1: bool,
10//!     field2: Option<bool>,
11//!     field3: Option<bool>,
12//! }
13//!
14//! fn main() {
15//!     let x = Example::new(true).field2(false);
16//!     assert_eq!(x, Example {
17//!         field1: true,
18//!         field2: Some(false),
19//!         field3: None,
20//!     });
21//! }
22//! ```
23
24#![feature(proc_macro)]
25
26extern crate proc_macro;
27#[macro_use]
28extern crate quote;
29extern crate syn;
30
31use proc_macro::TokenStream;
32
33#[doc(hidden)]
34#[proc_macro_derive(OptionConstructor)]
35pub fn derive_option_constructor(input: TokenStream) -> TokenStream {
36    let ast = syn::parse_macro_input(&input.to_string()).unwrap();
37    let expanded = expand_function(ast);
38    expanded.to_string().parse().unwrap()
39}
40
41fn is_option_ident(f: &(&syn::Ident, &syn::Ty)) -> bool {
42    match *f.1 {
43        syn::Ty::Path(_, ref path) => {
44            match path.segments.first().unwrap().ident.as_ref() {
45                "Option" => true,
46                _ => false,
47            }
48        }
49        _ => false,
50    }
51}
52
53fn expand_function(ast: syn::MacroInput) -> quote::Tokens {
54    let fields: Vec<_> = match ast.body {
55        syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
56            fields.iter().map(|f| (f.ident.as_ref().unwrap(), &f.ty)).collect()
57        }
58        syn::Body::Struct(syn::VariantData::Unit) => vec![],
59        _ => panic!("#[derive(OptionConstructor)] can only be used with braced structs"),
60    };
61
62    let field_compulsory: Vec<_> = fields.iter()
63        .filter(|f| !is_option_ident(&f))
64        .map(|f| f.0)
65        .collect();
66
67    let field_optional: Vec<_> =
68        fields.iter().filter(|f| is_option_ident(&f)).map(|f| f.0).collect();
69    let field_optional2 = field_optional.clone();
70
71    let none = syn::Ident::from("None");
72    let field_all: Vec<_> = fields.iter().map(|f| f.0).collect();
73    let values: Vec<_> = fields.iter()
74        .map(|f| if is_option_ident(f) { &none } else { f.0 })
75        .collect();
76
77    let ty_compulsory: Vec<_> = fields.iter().map(|f| f.1).collect();
78    let ty_optional: Vec<_> = fields.iter()
79        .filter(|f| is_option_ident(&f))
80        .map(|f| {
81            if let syn::Ty::Path(_, ref path) = *f.1 {
82                if let syn::PathParameters::AngleBracketed(ref param) =
83                    path.segments
84                        .first()
85                        .unwrap()
86                        .parameters {
87                    return param.types.first().unwrap();
88                }
89            }
90
91            panic!("no sane type!");
92        })
93        .collect();
94
95    let struct_name = &ast.ident;
96    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
97    quote! {
98        impl #impl_generics #struct_name #ty_generics #where_clause {
99            pub fn new(#( #field_compulsory: #ty_compulsory, )*) -> #struct_name #ty_generics {
100                #struct_name {
101                    #( #field_all: #values, )*
102                }
103            }
104            #(
105                pub fn #field_optional(mut self, val: #ty_optional) -> Self {
106                    self.#field_optional2 = Some(val);
107                    self
108                }
109            )*
110        }
111    }
112}