1- use crate :: body:: { Body , HttpBody } ;
1+ use crate :: body:: HttpBody ;
22use axum_core:: response:: IntoResponse ;
33use http:: Request ;
44use matchit:: MatchError ;
5- use std:: { borrow:: Cow , collections:: HashMap , convert:: Infallible , fmt, sync:: Arc } ;
5+ use std:: {
6+ borrow:: Cow , collections:: HashMap , convert:: Infallible , fmt, marker:: PhantomData , sync:: Arc ,
7+ } ;
68use tower_layer:: Layer ;
79use tower_service:: Service ;
810
@@ -11,14 +13,16 @@ use super::{
1113 RouteId , NEST_TAIL_PARAM ,
1214} ;
1315
14- pub ( super ) struct PathRouter < S = ( ) , B = Body > {
16+ pub ( super ) struct PathRouter < F , S , B > {
1517 routes : HashMap < RouteId , Endpoint < S , B > > ,
1618 node : Arc < Node > ,
1719 prev_route_id : RouteId ,
20+ _is_it_a_fallback : PhantomData < F > ,
1821}
1922
20- impl < S , B > PathRouter < S , B >
23+ impl < F , S , B > PathRouter < F , S , B >
2124where
25+ F : IsItAFallback ,
2226 B : HttpBody + Send + ' static ,
2327 S : Clone + Send + Sync + ' static ,
2428{
@@ -107,11 +111,12 @@ where
107111 Ok ( ( ) )
108112 }
109113
110- pub ( super ) fn merge ( & mut self , other : PathRouter < S , B > ) -> Result < ( ) , Cow < ' static , str > > {
114+ pub ( super ) fn merge ( & mut self , other : PathRouter < F , S , B > ) -> Result < ( ) , Cow < ' static , str > > {
111115 let PathRouter {
112116 routes,
113117 node,
114118 prev_route_id : _,
119+ _is_it_a_fallback : _,
115120 } = other;
116121
117122 for ( id, route) in routes {
@@ -131,14 +136,15 @@ where
131136 pub ( super ) fn nest (
132137 & mut self ,
133138 path : & str ,
134- router : PathRouter < S , B > ,
139+ router : PathRouter < F , S , B > ,
135140 ) -> Result < ( ) , Cow < ' static , str > > {
136141 let prefix = validate_nest_path ( path) ;
137142
138143 let PathRouter {
139144 routes,
140145 node,
141146 prev_route_id : _,
147+ _is_it_a_fallback : _,
142148 } = router;
143149
144150 for ( id, endpoint) in routes {
@@ -193,7 +199,7 @@ where
193199 Ok ( ( ) )
194200 }
195201
196- pub ( super ) fn layer < L , NewReqBody > ( self , layer : L ) -> PathRouter < S , NewReqBody >
202+ pub ( super ) fn layer < L , NewReqBody > ( self , layer : L ) -> PathRouter < F , S , NewReqBody >
197203 where
198204 L : Layer < Route < B > > + Clone + Send + ' static ,
199205 L :: Service : Service < Request < NewReqBody > > + Clone + Send + ' static ,
@@ -215,6 +221,7 @@ where
215221 routes,
216222 node : self . node ,
217223 prev_route_id : self . prev_route_id ,
224+ _is_it_a_fallback : self . _is_it_a_fallback ,
218225 }
219226 }
220227
@@ -247,10 +254,11 @@ where
247254 routes,
248255 node : self . node ,
249256 prev_route_id : self . prev_route_id ,
257+ _is_it_a_fallback : self . _is_it_a_fallback ,
250258 }
251259 }
252260
253- pub ( super ) fn with_state < S2 > ( self , state : S ) -> PathRouter < S2 , B > {
261+ pub ( super ) fn with_state < S2 > ( self , state : S ) -> PathRouter < F , S2 , B > {
254262 let routes = self
255263 . routes
256264 . into_iter ( )
@@ -269,6 +277,7 @@ where
269277 routes,
270278 node : self . node ,
271279 prev_route_id : self . prev_route_id ,
280+ _is_it_a_fallback : self . _is_it_a_fallback ,
272281 }
273282 }
274283
@@ -293,12 +302,14 @@ where
293302 Ok ( match_) => {
294303 let id = * match_. value ;
295304
296- #[ cfg( feature = "matched-path" ) ]
297- crate :: extract:: matched_path:: set_matched_path_for_request (
298- id,
299- & self . node . route_id_to_path ,
300- req. extensions_mut ( ) ,
301- ) ;
305+ if !F :: FALLBACK {
306+ #[ cfg( feature = "matched-path" ) ]
307+ crate :: extract:: matched_path:: set_matched_path_for_request (
308+ id,
309+ & self . node . route_id_to_path ,
310+ req. extensions_mut ( ) ,
311+ ) ;
312+ }
302313
303314 url_params:: insert_url_params ( req. extensions_mut ( ) , match_. params ) ;
304315
@@ -347,17 +358,18 @@ where
347358 }
348359}
349360
350- impl < B , S > Default for PathRouter < S , B > {
361+ impl < F , B , S > Default for PathRouter < F , S , B > {
351362 fn default ( ) -> Self {
352363 Self {
353364 routes : Default :: default ( ) ,
354365 node : Default :: default ( ) ,
355366 prev_route_id : RouteId ( 0 ) ,
367+ _is_it_a_fallback : PhantomData ,
356368 }
357369 }
358370}
359371
360- impl < S , B > fmt:: Debug for PathRouter < S , B > {
372+ impl < F , S , B > fmt:: Debug for PathRouter < F , S , B > {
361373 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
362374 f. debug_struct ( "PathRouter" )
363375 . field ( "routes" , & self . routes )
@@ -366,12 +378,13 @@ impl<S, B> fmt::Debug for PathRouter<S, B> {
366378 }
367379}
368380
369- impl < S , B > Clone for PathRouter < S , B > {
381+ impl < F , S , B > Clone for PathRouter < F , S , B > {
370382 fn clone ( & self ) -> Self {
371383 Self {
372384 routes : self . routes . clone ( ) ,
373385 node : self . node . clone ( ) ,
374386 prev_route_id : self . prev_route_id ,
387+ _is_it_a_fallback : self . _is_it_a_fallback ,
375388 }
376389 }
377390}
@@ -443,3 +456,23 @@ pub(crate) fn path_for_nested_route<'a>(prefix: &'a str, path: &'a str) -> Cow<'
443456 format ! ( "{prefix}{path}" ) . into ( )
444457 }
445458}
459+
460+ /// Used to statically enforce that we don't merge/nest a fallback `PathRouter` into a non-fallback
461+ /// `PathRouter`.
462+ pub ( super ) trait IsItAFallback {
463+ const FALLBACK : bool ;
464+ }
465+
466+ #[ derive( Copy , Clone ) ]
467+ pub ( super ) struct IsFallback ;
468+
469+ #[ derive( Copy , Clone ) ]
470+ pub ( super ) struct IsNotFallback ;
471+
472+ impl IsItAFallback for IsFallback {
473+ const FALLBACK : bool = true ;
474+ }
475+
476+ impl IsItAFallback for IsNotFallback {
477+ const FALLBACK : bool = false ;
478+ }
0 commit comments