reinhardt_urls/routers/server_router.rs
1//! Unified Router with hierarchical routing support
2//!
3//! This module provides a unified router that supports:
4//! - **High-performance O(m) route matching** using matchit Radix Tree (m = path length)
5//! - Nested routers with automatic prefix inheritance
6//! - Namespace-based URL reversal
7//! - Middleware and DI context propagation
8//! - Integration with ViewSets, HTTP macro endpoints, and class-based views
9//!
10//! # Performance Characteristics
11//!
12//! The router uses [matchit](https://docs.rs/matchit) for O(m) route matching where m is the path length:
13//! - Route lookup: O(m) - Independent of the number of registered routes
14//! - Route compilation: O(n) - Done once at startup where n is the number of routes
15//! - Memory: Efficient through Radix Tree's prefix sharing
16//!
17//! With 1000+ routes, matchit provides 3-5x better performance compared to naive O(n×m) linear search.
18//!
19//! # Implementation Details
20//!
21//! Each HTTP method has its own matchit router for optimal performance:
22//! - `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`
23//! - Routes are compiled lazily on first access (thread-safe with RwLock)
24//! - Parameters are extracted directly from matchit's Params
25//!
26//! # Module Layout
27//!
28//! The implementation is split across focused submodules to keep each file
29//! small and reviewable:
30//!
31//! - `types` — `MiddlewareInfo`, `RouteInfo`, `FunctionRoute`, `ViewRoute`,
32//! `RouteHandler`, `RouteMatch`, and the `join_path` helper
33//! - `builder` — constructors and builder-style configuration
34//! (`new`, `with_prefix`, `with_namespace`, `with_di_context`,
35//! `with_middleware`, `exclude`, `mount`, `group`)
36//! - `registration` — route registration entry points
37//! (`endpoint`, `handler`, `viewset`, `view`, `with_route_middleware`)
38//! - `compile` — matchit compilation and `validate_*` helpers
39//! - `introspection` — read-only accessors, `get_all_routes`,
40//! `register_all_routes`, `reverse`
41//! - `dispatch` — `resolve`, `match_own_routes_*`, `path_exists_for_any_method`
42//! - `router_impls` — `Debug`, `Default`, `Handler`, `RegisterViewSet`
43//! - `handlers` — ViewSet handler adapters
44//! - `matching` — `path_matches` and `extract_params` utilities
45//! - `global` — global router registry used by `showurls`
46
47use crate::routers::UrlReverser;
48use matchit::Router as MatchitRouter;
49use reinhardt_di::InjectionContext;
50use reinhardt_middleware::Middleware;
51#[cfg(feature = "viewsets")]
52use std::collections::HashMap;
53use std::sync::{Arc, RwLock};
54
55pub mod global;
56mod handlers;
57mod matching;
58
59mod builder;
60mod compile;
61mod dispatch;
62mod introspection;
63mod registration;
64mod router_impls;
65mod types;
66
67#[cfg(test)]
68mod tests;
69
70use self::types::{FunctionRoute, RouteHandler, ViewRoute};
71
72pub use self::global::{
73 clear_router, get_router, get_router_di_context, is_router_registered,
74 register_di_registrations, register_router, register_router_arc, take_di_registrations,
75};
76pub use self::matching::{extract_params, path_matches};
77pub use self::types::{MiddlewareInfo, RouteInfo};
78
79/// Unified router with hierarchical routing support
80///
81/// Supports multiple API styles:
82/// - HTTP method macro endpoints
83/// - DRF-style: ViewSets with automatic CRUD
84/// - Django-style: Class-based views
85///
86/// # Examples
87///
88/// ```
89/// use reinhardt_urls::routers::ServerRouter;
90/// # use hyper::Method;
91/// # use reinhardt_core::endpoint::EndpointInfo;
92/// # use reinhardt_http::{Handler, Request, Response, Result};
93///
94/// # struct ExportUsers;
95/// # struct Health;
96/// # impl EndpointInfo for ExportUsers {
97/// # fn path() -> &'static str { "/export/" }
98/// # fn method() -> Method { Method::GET }
99/// # fn name() -> &'static str { "users-export" }
100/// # }
101/// # impl EndpointInfo for Health {
102/// # fn path() -> &'static str { "/health/" }
103/// # fn method() -> Method { Method::GET }
104/// # fn name() -> &'static str { "health" }
105/// # }
106/// # #[async_trait::async_trait]
107/// # impl Handler for ExportUsers {
108/// # async fn handle(&self, _req: Request) -> Result<Response> { Ok(Response::ok()) }
109/// # }
110/// # #[async_trait::async_trait]
111/// # impl Handler for Health {
112/// # async fn handle(&self, _req: Request) -> Result<Response> { Ok(Response::ok()) }
113/// # }
114///
115/// # async fn example() -> Result<()> {
116/// // Create a users sub-router
117/// let users_router = ServerRouter::new()
118/// .with_namespace("users")
119/// .endpoint(|| ExportUsers);
120///
121/// // Verify users router has namespace
122/// assert_eq!(users_router.namespace(), Some("users"));
123///
124/// // Create root router
125/// let router = ServerRouter::new()
126/// .with_prefix("/api/v1/")
127/// .with_namespace("v1")
128/// .endpoint(|| Health)
129/// .mount("/users/", users_router);
130///
131/// // Verify root router configuration
132/// assert_eq!(router.prefix(), "/api/v1/");
133/// assert_eq!(router.namespace(), Some("v1"));
134///
135/// // Generated URLs:
136/// // /api/v1/health/
137/// // /api/v1/users/export/
138/// # Ok(())
139/// # }
140/// # tokio::runtime::Runtime::new().unwrap().block_on(example()).unwrap();
141/// ```
142pub struct ServerRouter {
143 /// Router's prefix path
144 pub(crate) prefix: String,
145
146 /// Namespace for URL reversal
147 pub(crate) namespace: Option<String>,
148
149 /// Routes defined in this router
150 pub(crate) routes: Vec<crate::routers::Route>,
151
152 /// ViewSet registrations
153 #[cfg(feature = "viewsets")]
154 pub(crate) viewsets: HashMap<String, Arc<dyn reinhardt_views::viewsets::ViewSet>>,
155
156 /// Function-based routes
157 pub(crate) functions: Vec<FunctionRoute>,
158
159 /// Class-based view routes
160 pub(crate) views: Vec<ViewRoute>,
161
162 /// Child routers
163 pub(crate) children: Vec<ServerRouter>,
164
165 /// DI context
166 pub(crate) di_context: Option<Arc<InjectionContext>>,
167
168 /// Middleware-contributed DI singleton registrations that have been
169 /// harvested by [`Self::with_middleware`] but not yet applied. Filled
170 /// when [`Self::with_middleware`] runs before [`Self::with_di_context`];
171 /// drained either by a later [`Self::with_di_context`] call (into that
172 /// context's `SingletonScope`) or, if no context is ever attached, by
173 /// [`Self::register_all_routes`] (into the global deferred-registration
174 /// list). This avoids both the silent drop and the global-list leak
175 /// described in #4426.
176 pub(crate) pending_middleware_di: reinhardt_di::DiRegistrationList,
177
178 /// Middleware stack
179 pub(crate) middleware: Vec<Arc<dyn Middleware>>,
180
181 /// Middleware type information for runtime introspection
182 pub(crate) middleware_names: Vec<MiddlewareInfo>,
183
184 /// Per-middleware exclusion patterns, indexed parallel to `middleware` vec.
185 /// Each entry contains the exclusion path patterns for the corresponding middleware.
186 pub(crate) middleware_exclusions: Vec<Vec<String>>,
187
188 /// URL reverser
189 pub(crate) reverser: UrlReverser,
190
191 /// Matchit router for GET requests (uses RwLock for thread-safe lazy compilation)
192 pub(crate) get_router: RwLock<MatchitRouter<RouteHandler>>,
193
194 /// Matchit router for POST requests
195 pub(crate) post_router: RwLock<MatchitRouter<RouteHandler>>,
196
197 /// Matchit router for PUT requests
198 pub(crate) put_router: RwLock<MatchitRouter<RouteHandler>>,
199
200 /// Matchit router for DELETE requests
201 pub(crate) delete_router: RwLock<MatchitRouter<RouteHandler>>,
202
203 /// Matchit router for PATCH requests
204 pub(crate) patch_router: RwLock<MatchitRouter<RouteHandler>>,
205
206 /// Matchit router for HEAD requests
207 pub(crate) head_router: RwLock<MatchitRouter<RouteHandler>>,
208
209 /// Matchit router for OPTIONS requests
210 pub(crate) options_router: RwLock<MatchitRouter<RouteHandler>>,
211
212 /// Flag indicating if routes have been compiled (uses RwLock for thread-safety)
213 pub(crate) routes_compiled: RwLock<bool>,
214}