use darling::FromDeriveInput;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput, Type};
extern crate proc_macro;
#[derive(Default, FromDeriveInput)]
#[darling(default, attributes(aide))]
struct OperationIoOpts {
input: bool,
input_with: Option<Type>,
output: bool,
output_with: Option<Type>,
json_schema: bool,
}
#[proc_macro_derive(OperationIo, attributes(aide))]
pub fn derive_operation_io(ts: TokenStream) -> TokenStream {
let mut derive_input = parse_macro_input!(ts as DeriveInput);
let OperationIoOpts {
input_with,
output_with,
input,
output,
json_schema,
} = OperationIoOpts::from_derive_input(&derive_input).unwrap();
let name = &derive_input.ident;
let generic_params = derive_input
.generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(t) => Some(t.ident.clone()),
_ => None,
})
.collect::<Vec<_>>();
if json_schema {
let wh = derive_input.generics.make_where_clause();
for param in generic_params {
wh.predicates
.push(parse_quote!(#param: schemars::JsonSchema));
}
}
let (i_gen, t_gen, w_gen) = derive_input.generics.split_for_impl();
let mut ts = quote!();
if !input && !output && input_with.is_none() && output_with.is_none() {
ts.extend(quote! {
impl #i_gen brk_aide::OperationInput for #name #t_gen #w_gen {}
impl #i_gen brk_aide::OperationOutput for #name #t_gen #w_gen {
type Inner = Self;
}
});
} else {
if input {
ts.extend(quote! {
impl #i_gen brk_aide::OperationInput for #name #t_gen #w_gen {}
});
}
if output {
ts.extend(quote! {
impl #i_gen brk_aide::OperationOutput for #name #t_gen #w_gen {
type Inner = Self;
}
});
}
if let Some(input) = input_with {
ts.extend(quote! {
impl #i_gen brk_aide::OperationInput for #name #t_gen #w_gen {
fn operation_input(
ctx: &mut brk_aide::generate::GenContext,
operation: &mut brk_aide::openapi::Operation
) {
<#input as brk_aide::OperationInput>::operation_input(
ctx,
operation
);
}
}
});
}
if let Some(output) = output_with {
ts.extend(quote! {
impl #i_gen brk_aide::OperationOutput for #name #t_gen #w_gen {
type Inner = <#output as brk_aide::OperationOutput>::Inner;
fn operation_response(
ctx: &mut brk_aide::generate::GenContext,
operation: &mut brk_aide::openapi::Operation
) -> Option<brk_aide::openapi::Response> {
<#output as brk_aide::OperationOutput>::operation_response(
ctx,
operation
)
}
fn inferred_responses(
ctx: &mut brk_aide::generate::GenContext,
operation: &mut brk_aide::openapi::Operation
) -> Vec<(Option<u16>, brk_aide::openapi::Response)> {
<#output as brk_aide::OperationOutput>::inferred_responses(
ctx,
operation
)
}
}
});
}
}
ts.into()
}
#[cfg(feature = "axum-extra-typed-routing")]
#[proc_macro_attribute]
pub fn axum_typed_path(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = proc_macro2::TokenStream::from(item);
quote! {
#[derive(
::axum_extra::routing::TypedPath,
::aide_macros::OperationIo,
::schemars::JsonSchema,
::serde::Deserialize,
)]
#[aide(input_with = "brk_aide::axum::routing::typed::TypedPath<Self>")]
#input
}
.into()
}