enum_error_derive/
lib.rs

1
2extern crate proc_macro;
3extern crate syn;
4#[macro_use]
5extern crate quote;
6
7use proc_macro::TokenStream;
8
9#[proc_macro_derive(EnumError)]
10pub fn enum_error(input: TokenStream) -> TokenStream {
11    let s = input.to_string();
12    let ast = syn::parse_macro_input(&s).unwrap();
13    let gen = impl_enum_error(&ast);
14    gen.parse().unwrap()
15}
16
17fn impl_from_traits(name: &syn::Ident, variants: &Vec<syn::Variant>) -> quote::Tokens {
18    let impls = variants.iter()
19        .map(|var| {
20            let v = &var.ident;
21            let cont = match var.data {
22                syn::VariantData::Tuple(ref c) => c,
23                _ => unreachable!(),
24            };
25            assert!(cont.len() == 1, "Single Tuple is required");
26            let ctype = &cont[0].ty;
27            quote!{
28                impl From<#ctype> for #name {
29                    fn from(val: #ctype) -> Self {
30                        #name::#v(val)
31                    }
32                }
33            }
34        });
35    quote!{ #(#impls)* }
36}
37
38fn impl_error(name: &syn::Ident, variants: &Vec<syn::Variant>) -> quote::Tokens {
39    let snips = variants.iter()
40        .map(|var| {
41            let v = &var.ident;
42            quote!{ #name::#v(ref err) => err.description() }
43        });
44    quote!{
45        impl ::std::error::Error for #name {
46            fn description(&self) -> &str {
47                match *self {
48                    #(#snips), *
49                }
50            }
51        }
52    }
53}
54
55fn impl_display(name: &syn::Ident, variants: &Vec<syn::Variant>) -> quote::Tokens {
56    let snips = variants.iter()
57        .map(|var| {
58            let v = &var.ident;
59            quote!{ #name::#v(ref err) => err.fmt(f) }
60        });
61    quote!{
62        impl ::std::fmt::Display for #name {
63            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
64                match *self {
65                    #(#snips), *
66                }
67            }
68        }
69    }
70}
71
72fn impl_enum_error(ast: &syn::MacroInput) -> quote::Tokens {
73    let name = &ast.ident;
74    let ref variants = match ast.body {
75        syn::Body::Enum(ref variants) => variants,
76        syn::Body::Struct(_) => unreachable!(),
77    };
78    let mut token = quote::Tokens::new();
79    token.append_all(&[impl_from_traits(&name, &variants),
80                       impl_display(&name, &variants),
81                       impl_error(&name, &variants)]);
82    token
83}