procedurals/
lib.rs

1
2extern crate proc_macro;
3extern crate syn;
4#[macro_use]
5extern crate quote;
6
7use proc_macro::TokenStream;
8use quote::ToTokens;
9use syn::{Data, DeriveInput, Field, Fields, Variant};
10
11#[proc_macro_derive(EnumError)]
12pub fn enum_error(input: TokenStream) -> TokenStream {
13    let ast = into_ast(input);
14    let froms = impl_into_enum(&ast);
15    let display = impl_display(&ast);
16    let error = impl_error(&ast);
17    let tokens = quote!{ #froms #display #error };
18    tokens.into()
19}
20
21#[proc_macro_derive(IntoEnum)]
22pub fn into_enum(input: TokenStream) -> TokenStream {
23    let ast = into_ast(input);
24    let froms = impl_into_enum(&ast);
25    let tokens = quote!{ #froms };
26    tokens.into()
27}
28
29#[proc_macro_derive(NewType)]
30pub fn newtype(input: TokenStream) -> TokenStream {
31    let ast = into_ast(input);
32    let from = impl_newtype_from(&ast);
33    let deref = impl_newtype_deref(&ast);
34    let tokens = quote!{ #from #deref };
35    tokens.into()
36}
37
38fn into_ast(input: TokenStream) -> DeriveInput {
39    syn::parse(input).unwrap()
40}
41
42fn get_enum_variants(ast: &DeriveInput) -> Vec<&Variant> {
43    match ast.data {
44        Data::Enum(ref inner) => inner.variants.iter().collect(),
45        Data::Struct(_) => unreachable!("Structs are not supported"),
46        Data::Union(_) => unreachable!("Unions are not supported"),
47    }
48}
49
50fn get_basetype(ast: &DeriveInput) -> &Field {
51    match ast.data {
52        Data::Enum(_) => unreachable!("Enums are not supported"),
53        Data::Union(_) => unreachable!("Unions are not supported"),
54        Data::Struct(ref ds) => {
55            match ds.fields {
56                Fields::Named(_) => unreachable!("Must be tuple struct"),
57                Fields::Unnamed(ref t) => {
58                    if t.unnamed.len() > 1 {
59                        unreachable!("Must be one type");
60                    }
61                    &t.unnamed[0]
62                }
63                Fields::Unit => unreachable!("Must be tuple struct"),
64            }
65        }
66    }
67}
68
69fn impl_into_enum(ast: &DeriveInput) -> Box<ToTokens> {
70    let name = &ast.ident;
71    let variants = get_enum_variants(&ast);
72    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
73    let impls = variants.iter().map(|var| {
74        let v = &var.ident;
75        let cont = match var.fields {
76            Fields::Unnamed(ref c) => &c.unnamed,
77            _ => unreachable!(),
78        };
79        assert!(cont.len() == 1, "Single tuple is required");
80        let ctype = &cont[0].ty;
81        quote!{
82            impl #impl_generics From<#ctype> for #name #ty_generics #where_clause {
83                fn from(val: #ctype) -> Self {
84                    #name::#v(val)
85                }
86            }
87        }
88    });
89    Box::new(quote!{ #(#impls)* })
90}
91
92fn impl_newtype_from(ast: &DeriveInput) -> Box<ToTokens> {
93    let name = &ast.ident;
94    let field = get_basetype(&ast);
95    let base = &field.ty;
96    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
97
98    Box::new(
99        quote!{
100            impl #impl_generics From<#base> for #name #ty_generics #where_clause {
101                fn from(val: #base) -> Self {
102                    #name(val)
103                }
104            }
105
106            impl #impl_generics Into<#base> for #name #ty_generics #where_clause {
107                fn into(self) -> #base {
108                    self.0
109                }
110            }
111        }
112    )
113}
114
115fn impl_newtype_deref(ast: &DeriveInput) -> Box<ToTokens> {
116    let name = &ast.ident;
117    let field = get_basetype(&ast);
118    let base = &field.ty;
119    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
120    Box::new(
121        quote!{
122            impl #impl_generics ::std::ops::Deref for #name #ty_generics #where_clause {
123                type Target = #base;
124                fn deref(&self) -> &Self::Target { &self.0 }
125            }
126            impl #impl_generics ::std::ops::DerefMut for #name #ty_generics #where_clause {
127                fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
128            }
129        }
130    )
131}
132
133fn impl_error(ast: &DeriveInput) -> Box<ToTokens> {
134    let name = &ast.ident;
135    let variants = get_enum_variants(&ast);
136    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
137    let snips = variants.iter().map(|var| {
138        let v = &var.ident;
139        quote!{ #name::#v(ref err) => err.description() }
140    });
141    Box::new(
142        quote!{
143            impl #impl_generics ::std::error::Error for #name #ty_generics #where_clause {
144                fn description(&self) -> &str {
145                    match *self {
146                        #(#snips), *
147                    }
148                }
149            }
150        }
151    )
152}
153
154fn impl_display(ast: &DeriveInput) -> Box<ToTokens> {
155    let name = &ast.ident;
156    let variants = get_enum_variants(&ast);
157    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
158    let snips = variants.iter().map(|var| {
159        let v = &var.ident;
160        quote!{ #name::#v(ref err) => err.fmt(f) }
161    });
162    Box::new(
163        quote!{
164            impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause {
165                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
166                    match *self {
167                        #(#snips), *
168                    }
169                }
170            }
171        }
172    )
173}