1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 self,
7 DeriveInput,
8 TypeTuple,
9};
10
11#[proc_macro_attribute]
12pub fn err_gen(attr: TokenStream, input: TokenStream) -> TokenStream {
13 use syn::Data::*;
14
15 let attr_ast: TypeTuple = syn::parse(attr).unwrap();
16 let input_ast: DeriveInput = syn::parse(input).unwrap();
17
18 let raw_enum = match input_ast.data {
19 Enum(data_enum) => data_enum,
20 _ => panic!("input must be an enum"),
21 };
22
23 let name = &input_ast.ident;
24
25 let mut nested_variants = quote! {};
26
27 for variant in raw_enum.variants.iter() {
28 nested_variants = quote! {
29 #variant,
30 #nested_variants
31 };
32 }
33
34 let mut generated_nested_variants = quote! {};
35
36 for element in attr_ast.elems.iter() {
37 use syn::Type::*;
38 let element_name = match element {
39 Path(type_path) => type_path
40 .path
41 .segments
42 .last()
43 .expect("no variant name found")
44 .ident
45 .clone(),
46 _ => panic!("element must be a path-name"),
47 };
48
49 let gnv_display = format!("({}::{}) -> {{}}", name, element_name);
50
51 generated_nested_variants = quote! {
52 #[display(fmt = #gnv_display, _0)]
53 #element(#element),
54 #generated_nested_variants
55 }
56 }
57
58 let enum_declaration = quote! {
59 #[derive(derive_more::Display, Debug)]
60 pub enum #name {
61 #nested_variants
62 #generated_nested_variants
63 }
64
65 impl std::error::Error for #name {}
66 };
67
68 let mut impl_clauses = quote! {};
69
70 for element in attr_ast.elems.iter() {
71 impl_clauses = quote! {
72 impl From<#element> for #name {
73 fn from(e: #element) -> Self { Self::#element(e) }
74 }
75 #impl_clauses
76 };
77 }
78
79 let result = quote! {
80 #enum_declaration
81 #impl_clauses
82 };
83
84 result.into()
85}