actix_response_macro/
lib.rs

1use std::collections::HashMap;
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{Ident, ItemEnum};
6
7#[proc_macro_derive(ResponseError, attributes(response_code))]
8pub fn derive_response_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
9    let item: TokenStream = item.into();
10    let item: ItemEnum = syn::parse2(item).unwrap();
11    let name = item.ident;
12    let mut variants: Vec<(Ident, Ident)> = Vec::new();
13    for variant in item.variants {
14        let Some(attr) = variant
15            .attrs
16            .iter()
17            .find(|a| a.path().is_ident("response_code"))
18        else {
19            panic!("missing response_code for variant {}", variant.ident);
20        };
21
22        let code: Ident = attr.parse_args().unwrap();
23        variants.push((variant.ident, code));
24    }
25
26    let matches = variants
27        .iter()
28        .map(|(k, v)| {
29            quote! {
30                Self::#k => ::actix_web::http::StatusCode::#v
31            }
32        })
33        .collect::<Vec<_>>();
34
35    let res = quote! {
36        impl ::actix_web::ResponseError for #name {
37            fn error_response(&self) -> ::actix_web::HttpResponse {
38                let status = match self {
39                    #(#matches),*
40                };
41
42                ::actix_web::HttpResponse::build(status).json(::actix_response::ApiResponse::<()>::Error(self.to_string()))
43            }
44        }
45    };
46
47    eprintln!("{}", res.to_string());
48
49    res.into()
50}