option_constructor_derive/
lib.rs1#![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}