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, functions, 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//! (`function`, `handler`, `route`, `viewset`, `endpoint`, `view`,
38//! `with_route_middleware`)
39//! - `compile` — matchit compilation and `validate_*` helpers
40//! - `introspection` — read-only accessors, `get_all_routes`,
41//! `register_all_routes`, `reverse`
42//! - `dispatch` — `resolve`, `match_own_routes_*`, `path_exists_for_any_method`
43//! - `router_impls` — `Debug`, `Default`, `Handler`, `RegisterViewSet`
44//! - `handlers` — `FunctionHandler` and `ViewSetHandler` adapters
45//! - `matching` — `path_matches` and `extract_params` utilities
46//! - `global` — global router registry used by `showurls`
47
48use crate::routers::UrlReverser;
49use matchit::Router as MatchitRouter;
50use reinhardt_di::InjectionContext;
51use reinhardt_middleware::Middleware;
52#[cfg(feature = "viewsets")]
53use std::collections::HashMap;
54use std::sync::{Arc, RwLock};
55
56pub mod global;
57mod handlers;
58mod matching;
59
60mod builder;
61mod compile;
62mod dispatch;
63mod introspection;
64mod registration;
65mod router_impls;
66mod types;
67
68#[cfg(test)]
69mod tests;
70
71use self::types::{FunctionRoute, RouteHandler, ViewRoute};
72
73pub use self::global::{
74 clear_router, get_router, get_router_di_context, is_router_registered,
75 register_di_registrations, register_router, register_router_arc, take_di_registrations,
76};
77pub use self::handlers::FunctionHandler;
78pub use self::matching::{extract_params, path_matches};
79pub use self::types::{MiddlewareInfo, RouteInfo};
80
81/// Unified router with hierarchical routing support
82///
83/// Supports multiple API styles:
84/// - FastAPI-style: Function-based routes
85/// - DRF-style: ViewSets with automatic CRUD
86/// - Django-style: Class-based views
87///
88/// # Examples
89///
90/// ```
91/// use reinhardt_urls::routers::ServerRouter;
92/// use hyper::Method;
93/// # use reinhardt_http::{Request, Response, Result};
94///
95/// # async fn example() -> Result<()> {
96/// // Create a users sub-router
97/// let users_router = ServerRouter::new()
98/// .with_namespace("users")
99/// .function("/export/", Method::GET, |_req| async { Ok(Response::ok()) });
100///
101/// // Verify users router has namespace
102/// assert_eq!(users_router.namespace(), Some("users"));
103///
104/// // Create root router
105/// let router = ServerRouter::new()
106/// .with_prefix("/api/v1/")
107/// .with_namespace("v1")
108/// .function("/health/", Method::GET, |_req| async { Ok(Response::ok()) })
109/// .mount("/users/", users_router);
110///
111/// // Verify root router configuration
112/// assert_eq!(router.prefix(), "/api/v1/");
113/// assert_eq!(router.namespace(), Some("v1"));
114///
115/// // Generated URLs:
116/// // /api/v1/health/
117/// // /api/v1/users/export/
118/// # Ok(())
119/// # }
120/// # tokio::runtime::Runtime::new().unwrap().block_on(example()).unwrap();
121/// ```
122pub struct ServerRouter {
123 /// Router's prefix path
124 pub(crate) prefix: String,
125
126 /// Namespace for URL reversal
127 pub(crate) namespace: Option<String>,
128
129 /// Routes defined in this router
130 pub(crate) routes: Vec<crate::routers::Route>,
131
132 /// ViewSet registrations
133 #[cfg(feature = "viewsets")]
134 pub(crate) viewsets: HashMap<String, Arc<dyn reinhardt_views::viewsets::ViewSet>>,
135
136 /// Function-based routes
137 pub(crate) functions: Vec<FunctionRoute>,
138
139 /// Class-based view routes
140 pub(crate) views: Vec<ViewRoute>,
141
142 /// Child routers
143 pub(crate) children: Vec<ServerRouter>,
144
145 /// DI context
146 pub(crate) di_context: Option<Arc<InjectionContext>>,
147
148 /// Middleware-contributed DI singleton registrations that have been
149 /// harvested by [`Self::with_middleware`] but not yet applied. Filled
150 /// when [`Self::with_middleware`] runs before [`Self::with_di_context`];
151 /// drained either by a later [`Self::with_di_context`] call (into that
152 /// context's `SingletonScope`) or, if no context is ever attached, by
153 /// [`Self::register_all_routes`] (into the global deferred-registration
154 /// list). This avoids both the silent drop and the global-list leak
155 /// described in #4426.
156 pub(crate) pending_middleware_di: reinhardt_di::DiRegistrationList,
157
158 /// Middleware stack
159 pub(crate) middleware: Vec<Arc<dyn Middleware>>,
160
161 /// Middleware type information for runtime introspection
162 pub(crate) middleware_names: Vec<MiddlewareInfo>,
163
164 /// Per-middleware exclusion patterns, indexed parallel to `middleware` vec.
165 /// Each entry contains the exclusion path patterns for the corresponding middleware.
166 pub(crate) middleware_exclusions: Vec<Vec<String>>,
167
168 /// URL reverser
169 pub(crate) reverser: UrlReverser,
170
171 /// Matchit router for GET requests (uses RwLock for thread-safe lazy compilation)
172 pub(crate) get_router: RwLock<MatchitRouter<RouteHandler>>,
173
174 /// Matchit router for POST requests
175 pub(crate) post_router: RwLock<MatchitRouter<RouteHandler>>,
176
177 /// Matchit router for PUT requests
178 pub(crate) put_router: RwLock<MatchitRouter<RouteHandler>>,
179
180 /// Matchit router for DELETE requests
181 pub(crate) delete_router: RwLock<MatchitRouter<RouteHandler>>,
182
183 /// Matchit router for PATCH requests
184 pub(crate) patch_router: RwLock<MatchitRouter<RouteHandler>>,
185
186 /// Matchit router for HEAD requests
187 pub(crate) head_router: RwLock<MatchitRouter<RouteHandler>>,
188
189 /// Matchit router for OPTIONS requests
190 pub(crate) options_router: RwLock<MatchitRouter<RouteHandler>>,
191
192 /// Flag indicating if routes have been compiled (uses RwLock for thread-safety)
193 pub(crate) routes_compiled: RwLock<bool>,
194}