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
#![crate_type = "proc-macro"]
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;
use proc_macro::TokenStream;
use syn::{Data, DeriveInput, Field, Fields, Ident};
use quote::Tokens;
#[proc_macro_derive(Withers)]
pub fn withers_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).expect("Couldn't parse input");
let gen = match ast.data {
Data::Struct(ref data) => impl_withers(&ast, &data.fields),
_ => panic!("#[derive(Withers)] must be used on a struct"),
};
gen.into()
}
fn impl_withers(ast: &DeriveInput, fields: &Fields) -> Tokens {
let name = &ast.ident;
match fields {
&Fields::Named(ref fields) => {
let fields: Vec<&Field> = fields.named.iter().collect();
let idents: Vec<_> = fields
.iter()
.map(|f| {
let ident = f.ident.as_ref().unwrap();
(Ident::from(format!("with_{}", ident)), ident, &f.ty)
})
.map(|(with_name, name, ty)| (quote!(#with_name), quote!(#name), quote!(#ty)))
.collect();
let withers = idents.iter().map(|&(ref with_name, ref name, ref ty)| {
quote! {
fn #with_name(mut self, value: #ty) -> Self {
self.#name = value;
self
}
}
});
quote! {
#[allow(dead_code)]
impl #name {
#(#withers)*
}
}
}
_ => panic!(
"#[derive(Withers)] cannot be used on tuple struct: {}",
name
),
}
}