1use darling::FromDeriveInput;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, parse_quote, DeriveInput, Type};
5
6extern crate proc_macro;
7
8#[derive(Default, FromDeriveInput)]
9#[darling(default, attributes(aide))]
10struct OperationIoOpts {
11 input: bool,
12 input_with: Option<Type>,
13 output: bool,
14 output_with: Option<Type>,
15 json_schema: bool,
16}
17
18#[proc_macro_derive(OperationIo, attributes(aide))]
67pub fn derive_operation_io(ts: TokenStream) -> TokenStream {
68 let mut derive_input = parse_macro_input!(ts as DeriveInput);
69
70 let OperationIoOpts {
71 input_with,
72 output_with,
73 input,
74 output,
75 json_schema,
76 } = OperationIoOpts::from_derive_input(&derive_input).unwrap();
77
78 let name = &derive_input.ident;
79
80 let generic_params = derive_input
81 .generics
82 .params
83 .iter()
84 .filter_map(|p| match p {
85 syn::GenericParam::Type(t) => Some(t.ident.clone()),
86 _ => None,
87 })
88 .collect::<Vec<_>>();
89
90 if json_schema {
91 let wh = derive_input.generics.make_where_clause();
92
93 for param in generic_params {
94 wh.predicates
95 .push(parse_quote!(#param: schemars::JsonSchema));
96 }
97 }
98
99 let (i_gen, t_gen, w_gen) = derive_input.generics.split_for_impl();
100
101 let mut ts = quote!();
102
103 if !input && !output && input_with.is_none() && output_with.is_none() {
104 ts.extend(quote! {
105 impl #i_gen aide::OperationInput for #name #t_gen #w_gen {}
106 impl #i_gen aide::OperationOutput for #name #t_gen #w_gen {
107 type Inner = Self;
108 }
109 });
110 } else {
111 if input {
112 ts.extend(quote! {
113 impl #i_gen aide::OperationInput for #name #t_gen #w_gen {}
114 });
115 }
116 if output {
117 ts.extend(quote! {
118 impl #i_gen aide::OperationOutput for #name #t_gen #w_gen {
119 type Inner = Self;
120 }
121 });
122 }
123
124 if let Some(input) = input_with {
125 ts.extend(quote! {
126 impl #i_gen aide::OperationInput for #name #t_gen #w_gen {
127 fn operation_input(
128 ctx: &mut aide::generate::GenContext,
129 operation: &mut aide::openapi::Operation
130 ) {
131 <#input as aide::OperationInput>::operation_input(
132 ctx,
133 operation
134 );
135 }
136 }
137 });
138 }
139
140 if let Some(output) = output_with {
141 ts.extend(quote! {
142 impl #i_gen aide::OperationOutput for #name #t_gen #w_gen {
143 type Inner = <#output as aide::OperationOutput>::Inner;
144 fn operation_response(
145 ctx: &mut aide::generate::GenContext,
146 operation: &mut aide::openapi::Operation
147 ) -> Option<aide::openapi::Response> {
148 <#output as aide::OperationOutput>::operation_response(
149 ctx,
150 operation
151 )
152 }
153 fn inferred_responses(
154 ctx: &mut aide::generate::GenContext,
155 operation: &mut aide::openapi::Operation
156 ) -> Vec<(Option<u16>, aide::openapi::Response)> {
157 <#output as aide::OperationOutput>::inferred_responses(
158 ctx,
159 operation
160 )
161 }
162 }
163 });
164 }
165 }
166
167 ts.into()
168}
169
170#[cfg(feature = "axum-extra-typed-routing")]
177#[proc_macro_attribute]
178pub fn axum_typed_path(_attr: TokenStream, item: TokenStream) -> TokenStream {
179 let input = proc_macro2::TokenStream::from(item);
180 quote! {
181 #[derive(
182 ::axum_extra::routing::TypedPath,
183 ::aide_macros::OperationIo,
184 ::schemars::JsonSchema,
185 ::serde::Deserialize,
186 )]
187 #[aide(input_with = "aide::axum::routing::typed::TypedPath<Self>")]
188 #input
189 }
190 .into()
191}