use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{Expr, FnArg, Ident, ItemFn, Lit};
#[proc_macro_attribute]
pub fn aidecomment(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut fn_def = syn::parse_macro_input!(item as ItemFn);
let doc_comments = fn_def
.attrs
.iter()
.filter_map(|attr| match &attr.meta {
syn::Meta::NameValue(nvmeta) => Some(nvmeta),
_ => None,
})
.filter(|nvmeta| nvmeta.path.get_ident().map(|i| i.to_string()) == Some("doc".to_owned()))
.filter_map(|nvmeta| match &nvmeta.value {
Expr::Lit(literal) => Some(literal),
_ => None,
})
.filter_map(|literal| match &literal.lit {
Lit::Str(string) => Some(string.value()),
_ => None,
})
.collect::<Vec<_>>();
let doc_comment = doc_comments.join("\n");
let mut lines = doc_comment.lines().collect::<Vec<_>>();
let first_empty_idx = lines
.iter()
.position(|line| line.trim().is_empty())
.unwrap_or(lines.len());
let summary = lines.drain(0..first_empty_idx).collect::<Vec<_>>().join("");
let summary = summary.trim();
let description = lines.join("\n");
let description = description.trim();
let struct_name = fn_def.sig.ident.to_string() + "_AideComment";
let struct_name = Ident::new(&struct_name, Span::mixed_site());
let vis = fn_def.vis.clone();
let arg = syn::parse_str::<FnArg>(&format!("_: {struct_name}")).unwrap();
fn_def.sig.inputs.insert(0, arg);
quote! {
#vis struct #struct_name;
impl ::aide::OperationInput for #struct_name {
fn operation_input(_ctx: &mut ::aide::gen::GenContext, operation: &mut ::aide::openapi::Operation) {
operation.summary = Some(#summary.to_owned());
operation.description = Some(#description.to_owned());
}
}
#[::axum::async_trait]
impl<S> ::axum::extract::FromRequestParts<S> for #struct_name {
type Rejection = ::std::convert::Infallible;
async fn from_request_parts(
_parts: &mut ::axum::http::request::Parts,
_state: &S,
) -> Result<Self, Self::Rejection> {
Ok(#struct_name)
}
}
#fn_def
}.into()
}