http_error_derive/
lib.rs

1use darling::{ast, FromDeriveInput, FromField, FromVariant};
2use proc_macro2::TokenStream;
3use quote::{quote, ToTokens};
4
5#[proc_macro_derive(HttpError, attributes(http))]
6pub fn parser(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7    let ast = syn::parse_macro_input!(input);
8    let gen = BaseReceiver::from_derive_input(&ast).unwrap();
9
10    quote!(#gen).into()
11}
12
13#[derive(Debug, FromDeriveInput)]
14#[darling(attributes(http), supports(enum_any))]
15struct BaseReceiver {
16    ident: syn::Ident,
17    generics: syn::Generics,
18    data: ast::Data<FieldReceiver, ()>,
19}
20
21impl ToTokens for BaseReceiver {
22    fn to_tokens(&self, tokens: &mut TokenStream) {
23        let BaseReceiver {
24            ref ident,
25            ref generics,
26            ref data,
27        } = *self;
28
29        let (imp, ty, wher) = generics.split_for_impl();
30        let fields = data.as_ref().take_enum().expect("Should never be enum");
31        let mut code_tokens = Vec::<TokenStream>::new();
32        let mut message_tokens = Vec::<TokenStream>::new();
33        let mut error_tokens = Vec::<TokenStream>::new();
34
35        fields.into_iter().for_each(|f| {
36            let field_ident = &f.ident;
37
38            let field_variant = match &f.fields.style {
39                ast::Style::Tuple => {
40                    quote! { (_) }
41                }
42                ast::Style::Struct => {
43                    quote! { { .. } }
44                }
45                _ => quote! {},
46            };
47
48            if let Some(code) = f.code {
49                code_tokens.push(quote! {
50                    Self::#field_ident #field_variant => Some(#code),
51                });
52            }
53
54            if let Some(error) = f.error {
55                error_tokens.push(quote! {
56                    Self::#field_ident #field_variant => Some(#error),
57                });
58            }
59
60            if f.message.is_some() {
61                let message = f.message.clone().unwrap();
62
63                message_tokens.push(quote! {
64                    Self::#field_ident #field_variant => Some(#message),
65                })
66            }
67        });
68
69        tokens.extend(quote! {
70            impl #imp #ident #ty #wher {
71                pub fn http_code(&self) -> Option<u16> {
72                    match &self {
73                        #(#code_tokens)*
74                        _ => None
75                    }
76                }
77                pub fn http_message(&self) -> Option<&'static str> {
78                    match &self {
79                        #(#message_tokens)*
80                        _ => None
81                    }
82                }
83                pub fn http_error(&self) -> Option<u16> {
84                    match &self {
85                        #(#error_tokens)*
86                        _ => None
87                    }
88                }
89            }
90        })
91    }
92}
93
94#[derive(Debug, FromVariant)]
95#[darling(attributes(http))]
96struct FieldReceiver {
97    ident: syn::Ident,
98    fields: ast::Fields<FieldFieldReceiver>,
99    code: Option<u16>,
100    message: Option<String>,
101    error: Option<u16>,
102}
103
104#[derive(Debug, FromField)]
105struct FieldFieldReceiver {}