use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use openapi_trait_shared::codegen::operations::OperationInfo;
use openapi_trait_shared::codegen::security::{auth_enum_ident, resolve_alternatives, SchemeInfo};
pub fn generate_trait(
mod_ident: &syn::Ident,
ops: &[OperationInfo],
schemes: &[SchemeInfo],
) -> TokenStream {
let trait_name = format_ident!("{}Api", mod_ident.to_string().to_pascal_case());
let methods: Vec<TokenStream> = ops
.iter()
.map(|op| generate_trait_method(op, schemes))
.collect();
quote! {
#[derive(::core::fmt::Debug, ::core::clone::Clone, ::core::marker::Copy)]
pub struct NotImplemented;
impl ::axum::response::IntoResponse for NotImplemented {
fn into_response(self) -> ::axum::response::Response {
(
::axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"not implemented",
)
.into_response()
}
}
pub trait #trait_name<S = ()>: ::core::marker::Send + ::core::marker::Sync {
type Error: ::axum::response::IntoResponse
+ ::core::convert::From<NotImplemented>
+ ::core::marker::Send;
#(#methods)*
fn router(self) -> ::axum::Router<S>
where
Self: Sized + 'static,
S: ::core::clone::Clone + ::core::marker::Send + ::core::marker::Sync + 'static,
{
make_router(::std::sync::Arc::new(self))
}
}
}
}
fn generate_trait_method(op: &OperationInfo, schemes: &[SchemeInfo]) -> TokenStream {
let method_ident = &op.method_ident;
let req_ident = format_ident!("{}Request", op.operation_id.to_pascal_case());
let resp_ident = format_ident!("{}Response", op.operation_id.to_pascal_case());
let doc = match (&op.summary, &op.description) {
(Some(s), Some(d)) if s != d => quote! { #[doc = #s] #[doc = ""] #[doc = #d] },
(Some(s), _) => quote! { #[doc = #s] },
(None, Some(d)) => quote! { #[doc = #d] },
(None, None) => quote! {},
};
let alts = resolve_alternatives(&op.auth, schemes);
let (auth_param, auth_discard) = match alts.len() {
0 => (quote! {}, quote! {}),
1 => {
let ty = &alts[0].ident;
(quote! { auth: #ty, }, quote! { let _ = auth; })
}
_ => {
let ty = auth_enum_ident(&op.operation_id);
(quote! { auth: #ty, }, quote! { let _ = auth; })
}
};
quote! {
#doc
fn #method_ident(
&self,
req: #req_ident,
#auth_param
state: ::axum::extract::State<S>,
headers: ::axum::http::HeaderMap,
) -> impl ::std::future::Future<Output = ::core::result::Result<#resp_ident, Self::Error>> + Send {
let _ = req;
#auth_discard
let _ = state;
let _ = headers;
async {
::core::result::Result::Err(
<Self::Error as ::core::convert::From<NotImplemented>>::from(NotImplemented),
)
}
}
}
}