faf_replay_parser_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn;
4
5mod attr;
6mod parse;
7
8#[proc_macro_derive(Display, attributes(display))]
9pub fn display_derive(input: TokenStream) -> TokenStream {
10    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
11
12    impl_display_derive(&ast)
13}
14
15fn impl_display_derive(ast: &syn::DeriveInput) -> TokenStream {
16    let name = &ast.ident;
17
18    let body = impl_body(name, &ast.data);
19
20    let gen = quote! {
21        impl ::core::fmt::Display for #name {
22            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
23                use crate::display::DisplayBuilders;
24
25                #body
26            }
27        }
28    };
29    gen.into()
30}
31
32fn impl_body(name: &syn::Ident, data: &syn::Data) -> proc_macro2::TokenStream {
33    use syn::{Data, Fields};
34
35    match *data {
36        Data::Struct(ref data) => match data.fields {
37            Fields::Named(ref fields) => {
38                let names = fields
39                    .named
40                    .iter()
41                    .map(|field| field.ident.as_ref().unwrap());
42                let field_calls = fields.named.iter().map(|field| {
43                    let (name, display) = get_field_display(field);
44                    quote! {
45                        .field(#name, #display)
46                    }
47                });
48                quote! {
49                    match *self {
50                        #name { #(ref #names),* } => {
51                            f.display_struct()
52                                .name(stringify!(#name))
53                                #(#field_calls)*
54                                .finish()
55                        }
56                    }
57                }
58            }
59            Fields::Unnamed(ref _fields) => {
60                quote!(Ok(()))
61            }
62            Fields::Unit => {
63                quote!(Ok(()))
64            }
65        },
66        Data::Enum(ref data) => {
67            let arms = data.variants.iter().map(|var| {
68                let params = attr::VariantParams::from_attrs(&var.attrs);
69                let variant_name = &var.ident;
70
71                match var.fields {
72                    Fields::Named(ref fields) => {
73                        let names = fields
74                            .named
75                            .iter()
76                            .map(|field| field.ident.as_ref().unwrap());
77                        let field_calls = fields.named.iter().map(|field| {
78                            let (name, display) = get_field_display(field);
79                            quote! {
80                                .field(#name, #display)
81                            }
82                        });
83                        quote! {
84                            #variant_name { #(ref #names),* } => {
85                                f.display_struct()
86                                    .name(stringify!(#variant_name))
87                                    #(#field_calls)*
88                                    .finish()
89                            }
90                        }
91                    }
92                    Fields::Unnamed(ref _fields) => {
93                        if params.transparent {
94                            quote! {
95                                #variant_name(ref inner) => ::core::fmt::Display::fmt(&inner, f)
96                            }
97                        } else {
98                            quote! {
99                                #variant_name(ref inner) => write!(f, concat!(stringify!(#variant_name), "({})"), inner)
100                            }
101                        }
102                    }
103                    Fields::Unit => quote! {
104                        #variant_name => {
105                            f.display_struct()
106                                .name(stringify!(#variant_name))
107                                .finish()
108                        }
109                    },
110                }
111            });
112            quote! {
113                match *self {
114                    #(#name::#arms),*
115                }
116            }
117        }
118        Data::Union(_) => unimplemented!(),
119    }
120}
121
122fn get_field_display(field: &syn::Field) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
123    let params = attr::FieldParams::from_attrs(&field.attrs);
124    let field_name = field.ident.as_ref().unwrap();
125
126    let name = match params.name {
127        Some(ref name) => quote!(#name),
128        None => quote!(stringify!(#field_name)),
129    };
130
131    let value = params
132        .fmt_with
133        .as_ref()
134        .map(|fmt_with| quote!(&#fmt_with(#field_name)))
135        .unwrap_or_else(|| match params.debug {
136            true => quote!(&crate::display::DisplayAsDebug(#field_name)),
137            false => quote!(#field_name),
138        });
139
140    (name, value)
141}