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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
    self,
    DeriveInput,
    TypeTuple,
};

#[proc_macro_attribute]
pub fn err_gen(attr: TokenStream, input: TokenStream) -> TokenStream {
    use syn::Data::*;

    let attr_ast: TypeTuple = syn::parse(attr).unwrap();
    let input_ast: DeriveInput = syn::parse(input).unwrap();

    let raw_enum = match input_ast.data {
        Enum(data_enum) => data_enum,
        _ => panic!("input must be an enum"),
    };

    let name = &input_ast.ident;

    let mut nested_variants = quote! {};

    for variant in raw_enum.variants.iter() {
        nested_variants = quote! {
            #variant,
            #nested_variants
        };
    }

    let mut generated_nested_variants = quote! {};

    for element in attr_ast.elems.iter() {
        use syn::Type::*;
        let element_name = match element {
            Path(type_path) => type_path
                .path
                .segments
                .last()
                .expect("no variant name found")
                .ident
                .clone(),
            _ => panic!("element must be a path-name"),
        };

        let gnv_display = format!("({}::{}) -> {{}}", name, element_name);

        generated_nested_variants = quote! {
            #[display(fmt = #gnv_display, _0)]
            #element(#element),
            #generated_nested_variants
        }
    }

    let enum_declaration = quote! {
        #[derive(derive_more::Display, Debug)]
        pub enum #name {
            #nested_variants
            #generated_nested_variants
        }

        impl std::error::Error for #name {}
    };

    let mut impl_clauses = quote! {};

    for element in attr_ast.elems.iter() {
        impl_clauses = quote! {
            impl From<#element> for #name {
                fn from(e: #element) -> Self { Self::#element(e) }
            }
            #impl_clauses
        };
    }

    let result = quote! {
        #enum_declaration
        #impl_clauses
    };

    result.into()
}