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}