use axum::{ Router, extract::FromRequestParts, response::IntoResponse, routing::{MethodFilter, on}, }; use conduwuit::Result; use futures::{Future, TryFutureExt}; use http::Method; use ruma::api::IncomingRequest; use super::{Ruma, RumaResponse, State}; pub(in super::super) trait RumaHandler { fn add_route(&'static self, router: Router, path: &str) -> Router; fn add_routes(&'static self, router: Router) -> Router; } pub(in super::super) trait RouterExt { fn ruma_route(self, handler: &'static H) -> Self where H: RumaHandler; } impl RouterExt for Router { fn ruma_route(self, handler: &'static H) -> Self where H: RumaHandler, { handler.add_routes(self) } } macro_rules! ruma_handler { ( $($tx:ident),* $(,)? ) => { #[allow(non_snake_case)] impl RumaHandler<($($tx,)* Ruma,)> for Fun where Fun: Fn($($tx,)* Ruma,) -> Fut + Send + Sync + 'static, Fut: Future> + Send, Req: IncomingRequest + Send + Sync + 'static, Err: IntoResponse + Send, ::OutgoingResponse: Send, $( $tx: FromRequestParts + Send + Sync + 'static, )* { fn add_routes(&'static self, router: Router) -> Router { Req::METADATA .history .all_paths() .fold(router, |router, path| self.add_route(router, path)) } fn add_route(&'static self, router: Router, path: &str) -> Router { let action = |$($tx,)* req| self($($tx,)* req).map_ok(RumaResponse); let method = method_to_filter(&Req::METADATA.method); router.route(path, on(method, action)) } } } } ruma_handler!(); ruma_handler!(T1); ruma_handler!(T1, T2); ruma_handler!(T1, T2, T3); ruma_handler!(T1, T2, T3, T4); const fn method_to_filter(method: &Method) -> MethodFilter { match *method { | Method::DELETE => MethodFilter::DELETE, | Method::GET => MethodFilter::GET, | Method::HEAD => MethodFilter::HEAD, | Method::OPTIONS => MethodFilter::OPTIONS, | Method::PATCH => MethodFilter::PATCH, | Method::POST => MethodFilter::POST, | Method::PUT => MethodFilter::PUT, | Method::TRACE => MethodFilter::TRACE, | _ => panic!("Unsupported HTTP method"), } }