use axum::{ extract::FromRequestParts, response::IntoResponse, routing::{on, MethodFilter}, Router, }; use conduit::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, 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"), } }