display_enum/
lib.rs

1extern crate proc_macro;
2
3use proc_macro2::{Ident, Span};
4use quote::quote;
5use syn::{ItemEnum, parse_macro_input, TypePath};
6use proc_macro::TokenStream;
7
8/// ignore_field
9#[proc_macro_derive(Display, attributes(ignore_field, to_vec))]
10pub fn enum_display_derive(input: TokenStream) -> TokenStream {
11    let input = parse_macro_input!(input as ItemEnum);
12    impl_display(input)
13}
14
15#[derive(Default)]
16struct Vars {
17    vars: Vec<proc_macro2::TokenStream>,
18    enum_type: EnumType,
19}
20
21#[derive(Default, PartialEq)]
22enum EnumType {
23    #[default]
24    None,
25    Tuple,
26    Struct,
27}
28
29fn impl_display(input: ItemEnum) -> TokenStream {
30    let name = &input.ident;
31    let mut tokens:Vec<proc_macro2::TokenStream> = vec![];
32    let mut global_ignore_field = false;
33    for attr in &input.attrs {
34        if attr.path().is_ident("ignore_field") {
35            global_ignore_field = true;
36        }
37    }
38    for variant in input.variants {
39        let mut vars = Vars::default();
40        let mut ignore_field = false;
41        for attr in variant.attrs {
42            if attr.path().is_ident("ignore_field") {
43                ignore_field = true;
44                break;
45            }
46        }
47        for (idx,field) in variant.fields.iter().enumerate() {
48            if let syn::Type::Path(TypePath{ path,.. }) =  &field.ty {
49                if let Some(ident) = &field.ident {
50                    vars.enum_type = EnumType::Struct;
51                    for _seg in path.segments.iter() {
52                        eprintln!("111:{}", field.ident.clone().unwrap().to_string());
53                        vars.vars.push(quote! { #ident });
54                    }
55                } else {
56                    for _seg in path.segments.iter() {
57                        eprintln!("111:");
58                        vars.enum_type = EnumType::Tuple;
59                        let data = Ident::new(format!("val{}", idx).as_str(), Span::call_site());
60                        vars.vars.push(quote! { #data });
61                    }
62                }
63            }
64        }
65        let ident = variant.ident;
66        if vars.vars.len() > 0 {
67            let fields = &vars.vars;
68            let fields = quote! { #(#fields),*};
69            if global_ignore_field || ignore_field{
70                if vars.enum_type == EnumType::Tuple {
71                    tokens.push(quote! {
72                        Self::#ident(#fields) => write!(f, "{}", stringify!(#ident))
73                    })
74                } else if vars.enum_type == EnumType::Struct {
75                    tokens.push(quote! {
76                        Self::#ident{#fields} => write!(f, "{}", stringify!(#ident))
77                    })
78                }
79            } else {
80                if vars.enum_type == EnumType::Tuple {
81                    tokens.push(quote! {
82                        Self::#ident(#fields) => write!(f, "{}:{:?}", stringify!(#ident), (#fields))
83                    })
84                } else if vars.enum_type == EnumType::Struct {
85                    tokens.push(quote! {
86                        Self::#ident{#fields} => write!(f, "{}:{:?}", stringify!(#ident), (#fields))
87                    })
88                }
89            }
90        } else {
91            tokens.push(quote! {
92                Self::#ident => write!(f, stringify!(#ident))
93            });
94        }
95    }
96
97    let token = quote! {
98        impl std::fmt::Display for #name {
99            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100                match self {
101                    #(#tokens),*
102                }
103            }
104        }
105    };
106    TokenStream::from(token)
107}