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