@@ -16,6 +16,7 @@ use std::{
1616 collections:: HashMap ,
1717 convert:: Infallible ,
1818 fmt,
19+ marker:: PhantomData ,
1920 sync:: Arc ,
2021 task:: { Context , Poll } ,
2122} ;
@@ -498,6 +499,66 @@ where
498499 Endpoint :: NestedRouter ( router) => router. call_with_state ( req, state) ,
499500 }
500501 }
502+
503+ /// Convert the router into a [`Service`] with a fixed request body type, to aid type
504+ /// inference.
505+ ///
506+ /// In some cases when calling methods from [`tower::ServiceExt`] on a [`Router`] you might get
507+ /// type inference errors along the lines of
508+ ///
509+ /// ```not_rust
510+ /// let response = router.ready().await?.call(request).await?;
511+ /// ^^^^^ cannot infer type for type parameter `B`
512+ /// ```
513+ ///
514+ /// This happens because `Router` implements [`Service`] with `impl<B> Service<Request<B>> for Router<()>`.
515+ ///
516+ /// For example:
517+ ///
518+ /// ```compile_fail
519+ /// use axum::{
520+ /// Router,
521+ /// routing::get,
522+ /// http::Request,
523+ /// body::Body,
524+ /// };
525+ /// use tower::{Service, ServiceExt};
526+ ///
527+ /// # async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
528+ /// let mut router = Router::new().route("/", get(|| async {}));
529+ /// let request = Request::new(Body::empty());
530+ /// let response = router.ready().await?.call(request).await?;
531+ /// # Ok(())
532+ /// # }
533+ /// ```
534+ ///
535+ /// Calling `Router::as_service` fixes that:
536+ ///
537+ /// ```
538+ /// use axum::{
539+ /// Router,
540+ /// routing::get,
541+ /// http::Request,
542+ /// body::Body,
543+ /// };
544+ /// use tower::{Service, ServiceExt};
545+ ///
546+ /// # async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
547+ /// let mut router = Router::new().route("/", get(|| async {}));
548+ /// let request = Request::new(Body::empty());
549+ /// let response = router.as_service().ready().await?.call(request).await?;
550+ /// # Ok(())
551+ /// # }
552+ /// ```
553+ ///
554+ /// This is mainly used when calling `Router` in tests. It shouldn't be necessary when running
555+ /// the `Router` normally via [`Router::into_make_service`].
556+ pub fn as_service < B > ( & mut self ) -> RouterAsService < ' _ , B , S > {
557+ RouterAsService {
558+ router : self ,
559+ _marker : PhantomData ,
560+ }
561+ }
501562}
502563
503564impl Router {
@@ -560,6 +621,45 @@ where
560621 }
561622}
562623
624+ /// A [`Router`] converted into a service with a fixed body type.
625+ ///
626+ /// See [`Router::as_service`] for more details.
627+ pub struct RouterAsService < ' a , B , S = ( ) > {
628+ router : & ' a mut Router < S > ,
629+ _marker : PhantomData < B > ,
630+ }
631+
632+ impl < ' a , B > Service < Request < B > > for RouterAsService < ' a , B , ( ) >
633+ where
634+ B : HttpBody < Data = bytes:: Bytes > + Send + ' static ,
635+ B :: Error : Into < axum_core:: BoxError > ,
636+ {
637+ type Response = Response ;
638+ type Error = Infallible ;
639+ type Future = RouteFuture < Infallible > ;
640+
641+ #[ inline]
642+ fn poll_ready ( & mut self , cx : & mut Context < ' _ > ) -> Poll < Result < ( ) , Self :: Error > > {
643+ <Router as Service < Request < B > > >:: poll_ready ( self . router , cx)
644+ }
645+
646+ #[ inline]
647+ fn call ( & mut self , req : Request < B > ) -> Self :: Future {
648+ self . router . call ( req)
649+ }
650+ }
651+
652+ impl < ' a , B , S > fmt:: Debug for RouterAsService < ' a , B , S >
653+ where
654+ S : fmt:: Debug ,
655+ {
656+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
657+ f. debug_struct ( "RouterAsService" )
658+ . field ( "router" , & self . router )
659+ . finish ( )
660+ }
661+ }
662+
563663/// Wrapper around `matchit::Router` that supports merging two `Router`s.
564664#[ derive( Clone , Default ) ]
565665struct Node {
0 commit comments