1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4use utils::parse_status_code::parse_status_code;
5
6mod utils;
7
8#[proc_macro_derive(IntoResponse, attributes(into_response))]
49pub fn into_response_derive(input: TokenStream) -> TokenStream {
50 let input = parse_macro_input!(input as DeriveInput);
51 let name = &input.ident;
52 let generics = &input.generics;
53 let attrs = &input.attrs;
54
55 let custom_status_code = match parse_status_code(attrs) {
56 Ok(code) => code,
57 Err(e) => return e.to_compile_error().into(),
58 };
59
60 let status_code_tokens = if let Some(code) = custom_status_code {
61 quote! { Some(axum::http::StatusCode::from_u16(#code).unwrap())}
62 } else {
63 quote! { None }
64 };
65
66 let mut generics_with_bounds = generics.clone();
67 for type_param in &mut generics_with_bounds.type_params_mut() {
68 type_param.bounds.push(syn::parse_quote!(serde::Serialize));
69 }
70
71 let (impl_generics, ty_generics, where_clause) = generics_with_bounds.split_for_impl();
72
73 let expanded = quote! {
74 impl #impl_generics axum::response::IntoResponse for #name #ty_generics #where_clause {
75 fn into_response(self) -> axum::response::Response {
76 let json = axum::Json(self);
77 match #status_code_tokens {
78 Some(status_code) => {
79 let response: (axum::http::StatusCode, axum::Json<Self>) = (status_code, json);
80 response.into_response()
81 },
82 None => json.into_response(),
83 }
84 }
85 }
86 };
87
88 expanded.into()
89}