use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::parse;
fn is_app_state_type(ty: &syn::Type) -> bool {
if let syn::Type::Path(type_path) = ty
&& type_path.qself.is_none()
{
let segments: Vec<_> = type_path.path.segments.iter().collect();
if let Some(last) = segments.last() {
return last.ident == "AppState" && last.arguments.is_none();
}
}
false
}
pub fn ws_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
let path = match parse::parse_route_path(attr) {
Ok(p) => p,
Err(err) => return err,
};
let input_fn = match parse::parse_async_handler(item) {
Ok(f) => f,
Err(err) => return err,
};
let fn_name = &input_fn.sig.ident;
let vis = &input_fn.vis;
let upgrade_name = format_ident!("__autumn_ws_upgrade_{}", fn_name);
let route_info_name = format_ident!("__autumn_route_info_{}", fn_name);
let mut extractor_params = Vec::new();
let mut call_args = Vec::new();
for arg in &input_fn.sig.inputs {
if let syn::FnArg::Typed(pat_type) = arg {
if is_app_state_type(&pat_type.ty) {
call_args.push(quote! { __autumn_state.clone() });
} else {
let pat = &pat_type.pat;
extractor_params.push(arg.clone());
call_args.push(quote! { #pat });
}
}
}
let upgrade_handler = if extractor_params.is_empty() {
quote! {
#[doc(hidden)]
#vis async fn #upgrade_name(
__autumn_ws: ::autumn_web::ws::WebSocketUpgrade,
::autumn_web::reexports::axum::extract::State(__autumn_state): ::autumn_web::reexports::axum::extract::State<::autumn_web::AppState>,
) -> impl ::autumn_web::reexports::axum::response::IntoResponse {
let __autumn_shutdown = __autumn_state.shutdown_token();
let handler = #fn_name(#(#call_args),*).await;
__autumn_ws.on_upgrade(move |socket| async move {
::autumn_web::ws::WsHandler::handle(handler, socket, __autumn_shutdown).await;
})
}
}
} else {
quote! {
#[doc(hidden)]
#vis async fn #upgrade_name(
__autumn_ws: ::autumn_web::ws::WebSocketUpgrade,
::autumn_web::reexports::axum::extract::State(__autumn_state): ::autumn_web::reexports::axum::extract::State<::autumn_web::AppState>,
#(#extractor_params),*
) -> impl ::autumn_web::reexports::axum::response::IntoResponse {
let __autumn_shutdown = __autumn_state.shutdown_token();
let handler = #fn_name(#(#call_args),*).await;
__autumn_ws.on_upgrade(move |socket| async move {
::autumn_web::ws::WsHandler::handle(handler, socket, __autumn_shutdown).await;
})
}
}
};
let path_value = path.value();
let path_params = crate::api_doc::extract_path_params(&path_value);
let path_params_tokens = crate::api_doc::emit_path_param_slice(&path_params);
quote! {
#input_fn
#upgrade_handler
#[doc(hidden)]
#vis fn #route_info_name() -> ::autumn_web::Route {
::autumn_web::Route {
method: ::autumn_web::reexports::http::Method::from_bytes(b"WS")
.expect("WS is a valid method token"),
path: #path,
handler: ::autumn_web::reexports::axum::routing::get(#upgrade_name),
name: ::core::stringify!(#fn_name),
api_doc: ::autumn_web::openapi::ApiDoc {
method: "GET",
path: #path,
operation_id: ::core::stringify!(#fn_name),
summary: ::core::option::Option::None,
description: ::core::option::Option::None,
tags: &[],
path_params: #path_params_tokens,
request_body: ::core::option::Option::None,
response: ::core::option::Option::None,
success_status: 101,
hidden: true,
query_schema: ::core::option::Option::None,
secured: false,
required_roles: &[],
register_schemas: ::core::option::Option::None,
},
repository: ::core::option::Option::None,
}
}
}
}