1use crate::pipeline::PipelineComponent;
2use crate::{
3 BootError, ExceptionFilter, Guard, HttpMethod, Interceptor, Middleware, OpenApiRouteMetadata,
4 Pipe, RequestValidator, Result, RouteVersioning, SerializationOptions, ValidationOptions,
5};
6use serde::de::DeserializeOwned;
7use serde::Serialize;
8use serde_json::Value;
9use std::collections::BTreeMap;
10use std::sync::Arc;
11#[cfg(feature = "cache")]
12use std::time::Duration;
13
14use crate::routing::handler::{ProviderRouteHandler, RequestScopedRouteHandler, RouteHandler};
15use crate::routing::host::{
16 host_param_names, host_shape_key, host_specificity, match_host_params, match_host_shape,
17 validate_host_pattern,
18};
19#[cfg(feature = "axum")]
20use crate::routing::path::has_catch_all;
21use crate::routing::path::{
22 match_path_params, match_path_shape, route_param_names, route_shape_key, validate_route_path,
23};
24use crate::ModuleRef;
25
26#[derive(Clone)]
28pub struct RouteDefinition {
29 pub(super) method: HttpMethod,
30 pub(super) path: String,
31 pub(super) host: Option<String>,
32 pub(crate) handler: Arc<dyn RouteHandler>,
33 pub(super) middleware: Vec<Arc<dyn Middleware>>,
34 pub(super) pipes: Vec<PipelineComponent<dyn Pipe>>,
35 pub(super) guards: Vec<PipelineComponent<dyn Guard>>,
36 pub(super) interceptors: Vec<PipelineComponent<dyn Interceptor>>,
37 pub(super) filters: Vec<PipelineComponent<dyn ExceptionFilter>>,
38 pub(super) validators: Vec<RequestValidator>,
39 pub(super) validation_enabled: bool,
40 pub(super) validation_disabled: bool,
41 pub(super) validation_options: ValidationOptions,
42 pub(super) module_name: Option<String>,
43 pub(super) controller_prefix: Option<String>,
44 pub(super) module_ref: Option<ModuleRef>,
45 pub(super) openapi: OpenApiRouteMetadata,
46 pub(super) versioning: RouteVersioning,
47 pub(super) serialization: SerializationOptions,
48 pub(super) metadata: BTreeMap<String, Value>,
49}
50
51impl RouteDefinition {
52 pub fn new<H>(method: HttpMethod, path: impl Into<String>, handler: H) -> Result<Self>
53 where
54 H: RouteHandler,
55 {
56 let path = path.into();
57 validate_route_path(&path)?;
58 Ok(Self {
59 method,
60 path,
61 host: None,
62 handler: Arc::new(handler),
63 middleware: Vec::new(),
64 pipes: Vec::new(),
65 guards: Vec::new(),
66 interceptors: Vec::new(),
67 filters: Vec::new(),
68 validators: Vec::new(),
69 validation_enabled: false,
70 validation_disabled: false,
71 validation_options: ValidationOptions::default(),
72 module_name: None,
73 controller_prefix: None,
74 module_ref: None,
75 openapi: OpenApiRouteMetadata::default(),
76 versioning: RouteVersioning::default(),
77 serialization: SerializationOptions::default(),
78 metadata: BTreeMap::new(),
79 })
80 }
81
82 pub fn new_scoped<F, H>(method: HttpMethod, path: impl Into<String>, factory: F) -> Result<Self>
83 where
84 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
85 H: RouteHandler,
86 {
87 Self::new(method, path, RequestScopedRouteHandler::new(factory))
88 }
89
90 pub fn new_provider<T, F>(
93 method: HttpMethod,
94 path: impl Into<String>,
95 factory: F,
96 ) -> Result<Self>
97 where
98 T: Send + Sync + 'static,
99 F: Fn(Arc<T>) -> Result<RouteDefinition> + Send + Sync + 'static,
100 {
101 Self::new(method, path, ProviderRouteHandler::<T, F>::new(factory))
102 }
103
104 pub fn method(&self) -> HttpMethod {
105 self.method
106 }
107
108 pub fn path(&self) -> &str {
109 &self.path
110 }
111
112 pub fn host(&self) -> Option<&str> {
113 self.host.as_deref()
114 }
115
116 pub fn path_shape(&self) -> String {
117 route_shape_key(&self.path)
118 }
119
120 pub fn host_shape(&self) -> Option<String> {
121 self.host.as_deref().map(host_shape_key)
122 }
123
124 pub fn path_param_names(&self) -> Vec<&str> {
125 route_param_names(&self.path)
126 }
127
128 pub fn host_param_names(&self) -> Vec<&str> {
129 self.host
130 .as_deref()
131 .map(host_param_names)
132 .unwrap_or_default()
133 }
134
135 pub fn matches_path(&self, path: &str) -> bool {
136 match_path_shape(&self.path, path)
137 }
138
139 #[cfg(feature = "axum")]
140 pub(crate) fn has_catch_all_path(&self) -> bool {
141 has_catch_all(&self.path)
142 }
143
144 pub fn matches_host(&self, host: Option<&str>) -> bool {
145 match &self.host {
146 Some(pattern) => host.is_some_and(|host| match_host_shape(pattern, host)),
147 None => true,
148 }
149 }
150
151 pub fn path_params(&self, path: &str) -> Result<Option<BTreeMap<String, String>>> {
152 match_path_params(&self.path, path)
153 }
154
155 pub fn host_params(&self, host: Option<&str>) -> Result<Option<BTreeMap<String, String>>> {
156 match (&self.host, host) {
157 (Some(pattern), Some(host)) => match_host_params(pattern, host),
158 (Some(_), None) => Ok(None),
159 (None, _) => Ok(Some(BTreeMap::new())),
160 }
161 }
162
163 pub fn module_name(&self) -> Option<&str> {
164 self.module_name.as_deref()
165 }
166
167 pub fn controller_prefix(&self) -> Option<&str> {
168 self.controller_prefix.as_deref()
169 }
170
171 pub fn handler(&self) -> Arc<dyn RouteHandler> {
172 Arc::clone(&self.handler)
173 }
174
175 pub fn openapi(&self) -> &OpenApiRouteMetadata {
176 &self.openapi
177 }
178
179 pub fn versioning(&self) -> &RouteVersioning {
180 &self.versioning
181 }
182
183 pub fn serialization(&self) -> &SerializationOptions {
184 &self.serialization
185 }
186
187 pub fn metadata(&self) -> &BTreeMap<String, Value> {
188 &self.metadata
189 }
190
191 pub fn metadata_value(&self, key: &str) -> Option<&Value> {
192 self.metadata.get(key)
193 }
194
195 pub fn metadata_as<T>(&self, key: &str) -> Result<Option<T>>
196 where
197 T: DeserializeOwned,
198 {
199 let Some(value) = self.metadata.get(key) else {
200 return Ok(None);
201 };
202
203 serde_json::from_value(value.clone())
204 .map(Some)
205 .map_err(|error| {
206 BootError::Internal(format!(
207 "failed to deserialize route metadata `{key}`: {error}"
208 ))
209 })
210 }
211
212 pub fn validation_enabled(&self) -> bool {
213 self.validation_enabled
214 }
215
216 pub fn with_host(mut self, pattern: impl Into<String>) -> Result<Self> {
217 let pattern = pattern.into();
218 validate_host_pattern(&pattern)?;
219 self.host = Some(pattern);
220 Ok(self)
221 }
222
223 pub fn without_host(mut self) -> Self {
224 self.host = None;
225 self
226 }
227
228 pub fn with_version(mut self, version: impl Into<String>) -> Self {
229 self.versioning = RouteVersioning::version(version);
230 self
231 }
232
233 pub fn with_versions<I, V>(mut self, versions: I) -> Self
234 where
235 I: IntoIterator<Item = V>,
236 V: Into<String>,
237 {
238 self.versioning = RouteVersioning::versions(versions);
239 self
240 }
241
242 pub fn version_neutral(mut self) -> Self {
243 self.versioning = RouteVersioning::neutral();
244 self
245 }
246
247 pub fn with_serialization(mut self, options: SerializationOptions) -> Self {
248 self.serialization = options;
249 self
250 }
251
252 pub fn with_metadata<V>(self, key: impl Into<String>, value: V) -> Result<Self>
253 where
254 V: Serialize,
255 {
256 let key = key.into();
257 let value = serde_json::to_value(value).map_err(|error| {
258 BootError::Internal(format!(
259 "failed to serialize route metadata `{key}`: {error}"
260 ))
261 })?;
262 Ok(self.with_metadata_value(key, value))
263 }
264
265 pub fn with_metadata_value(mut self, key: impl Into<String>, value: Value) -> Self {
266 self.metadata.insert(key.into(), value);
267 self
268 }
269
270 #[cfg(feature = "cache")]
271 pub fn with_cache_key(self, key: impl Into<String>) -> Self {
272 self.with_metadata_value(crate::CACHE_KEY_METADATA, Value::String(key.into()))
273 }
274
275 #[cfg(feature = "cache")]
276 pub fn with_cache_ttl(self, ttl: Duration) -> Self {
277 self.with_metadata_value(
278 crate::CACHE_TTL_METADATA,
279 Value::Number(serde_json::Number::from(ttl.as_millis() as u64)),
280 )
281 }
282
283 #[cfg(feature = "cache")]
284 pub fn without_cache(self) -> Self {
285 self.with_metadata_value(crate::CACHE_DISABLED_METADATA, Value::Bool(true))
286 }
287
288 pub(crate) fn with_metadata_defaults(mut self, metadata: &BTreeMap<String, Value>) -> Self {
289 for (key, value) in metadata {
290 self.metadata
291 .entry(key.clone())
292 .or_insert_with(|| value.clone());
293 }
294 self
295 }
296
297 pub(crate) fn with_metadata_default_value(
298 mut self,
299 key: impl Into<String>,
300 value: Value,
301 ) -> Self {
302 self.metadata.entry(key.into()).or_insert(value);
303 self
304 }
305
306 pub(crate) fn with_host_default(mut self, host: Option<&str>) -> Result<Self> {
307 if self.host.is_none() {
308 if let Some(host) = host {
309 self = self.with_host(host.to_string())?;
310 }
311 }
312 Ok(self)
313 }
314
315 pub(crate) fn host_shape_key(&self) -> Option<String> {
316 self.host.as_deref().map(host_shape_key)
317 }
318
319 pub(crate) fn host_specificity(&self) -> Vec<u8> {
320 host_specificity(self.host.as_deref())
321 }
322}