1use super::handler::RouteHandler;
2use super::host::validate_host_pattern;
3use super::path::normalize_prefix;
4use super::route::RouteDefinition;
5use crate::pipeline::PipelineComponents;
6use crate::{
7 BootErrorKind, BootRequest, ExceptionFilter, ExecutionInterceptor, Guard, Interceptor,
8 Middleware, ModuleRef, Pipe, Result, RouteVersioning, SerializationOptions, SseEvent, Validate,
9};
10use futures_core::Stream;
11use serde::de::DeserializeOwned;
12use serde::Serialize;
13use serde_json::Value;
14use std::collections::BTreeMap;
15use std::future::Future;
16
17#[derive(Clone)]
19pub struct ControllerDefinition {
20 prefix: String,
21 host: Option<String>,
22 routes: Vec<RouteDefinition>,
23 pipeline: PipelineComponents,
24 openapi_tags: Vec<String>,
25 versioning: RouteVersioning,
26 serialization: Option<SerializationOptions>,
27 metadata: BTreeMap<String, Value>,
28}
29
30impl ControllerDefinition {
31 pub fn new(prefix: impl Into<String>) -> Result<Self> {
32 let prefix = normalize_prefix(&prefix.into())?;
33 Ok(Self {
34 prefix,
35 host: None,
36 routes: Vec::new(),
37 pipeline: PipelineComponents::default(),
38 openapi_tags: Vec::new(),
39 versioning: RouteVersioning::default(),
40 serialization: None,
41 metadata: BTreeMap::new(),
42 })
43 }
44
45 pub fn route(mut self, route: RouteDefinition) -> Result<Self> {
46 let mut route = route;
47 route = route.with_host_default(self.host.as_deref())?;
48 if route.versioning().is_unspecified() && !self.versioning.is_unspecified() {
49 route = match &self.versioning {
50 RouteVersioning::Unspecified => route,
51 RouteVersioning::Versions(versions) => route.with_versions(versions.clone()),
52 RouteVersioning::Neutral => route.version_neutral(),
53 };
54 }
55 if route.serialization().is_empty() {
56 if let Some(serialization) = &self.serialization {
57 route = route.with_serialization(serialization.clone());
58 }
59 }
60 for tag in &self.openapi_tags {
61 route = route.with_tag(tag.clone());
62 }
63 route = route.with_metadata_defaults(&self.metadata);
64 self.routes.push(
65 route
66 .with_prefix(&self.prefix)?
67 .with_pipeline_prefix(&self.pipeline),
68 );
69 Ok(self)
70 }
71
72 pub fn with_pipe<P>(mut self, pipe: P) -> Self
73 where
74 P: Pipe,
75 {
76 self.pipeline.push_pipe(pipe);
77 self
78 }
79
80 pub fn with_middleware<M>(mut self, middleware: M) -> Self
81 where
82 M: Middleware,
83 {
84 self.pipeline.push_middleware(middleware);
85 self
86 }
87
88 pub fn with_guard<G>(mut self, guard: G) -> Self
89 where
90 G: Guard,
91 {
92 self.pipeline.push_guard(guard);
93 self
94 }
95
96 pub fn with_interceptor<I>(mut self, interceptor: I) -> Self
97 where
98 I: Interceptor,
99 {
100 self.pipeline.push_interceptor(interceptor);
101 self
102 }
103
104 pub fn with_execution_interceptor<I>(mut self, interceptor: I) -> Self
105 where
106 I: ExecutionInterceptor,
107 {
108 self.pipeline.push_execution_interceptor(interceptor);
109 self
110 }
111
112 pub fn with_filter<F>(mut self, filter: F) -> Self
113 where
114 F: ExceptionFilter,
115 {
116 self.pipeline.push_filter(filter);
117 self
118 }
119
120 pub fn with_catch_filter<I, F>(mut self, kinds: I, filter: F) -> Self
121 where
122 I: IntoIterator<Item = BootErrorKind>,
123 F: ExceptionFilter,
124 {
125 self.pipeline.push_catch_filter(kinds, filter);
126 self
127 }
128
129 pub fn with_validation(mut self) -> Self {
130 self.pipeline.enable_validation();
131 self
132 }
133
134 pub fn with_host(mut self, pattern: impl Into<String>) -> Result<Self> {
135 let pattern = pattern.into();
136 validate_host_pattern(&pattern)?;
137 self.host = Some(pattern.clone());
138 self.routes = self
139 .routes
140 .into_iter()
141 .map(|route| route.with_host_default(Some(&pattern)))
142 .collect::<Result<Vec<_>>>()?;
143 Ok(self)
144 }
145
146 pub fn without_host(mut self) -> Self {
147 self.host = None;
148 self.routes = self
149 .routes
150 .into_iter()
151 .map(RouteDefinition::without_host)
152 .collect();
153 self
154 }
155
156 pub fn host(&self) -> Option<&str> {
157 self.host.as_deref()
158 }
159
160 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
161 let tag = tag.into();
162 if !self.openapi_tags.contains(&tag) {
163 self.openapi_tags.push(tag.clone());
164 }
165 self.routes = self
166 .routes
167 .into_iter()
168 .map(|route| route.with_tag(tag.clone()))
169 .collect();
170 self
171 }
172
173 pub fn with_version(mut self, version: impl Into<String>) -> Self {
174 self.versioning = RouteVersioning::version(version);
175 self
176 }
177
178 pub fn with_versions<I, V>(mut self, versions: I) -> Self
179 where
180 I: IntoIterator<Item = V>,
181 V: Into<String>,
182 {
183 self.versioning = RouteVersioning::versions(versions);
184 self
185 }
186
187 pub fn version_neutral(mut self) -> Self {
188 self.versioning = RouteVersioning::neutral();
189 self
190 }
191
192 pub fn with_serialization(mut self, options: SerializationOptions) -> Self {
193 self.serialization = Some(options);
194 self
195 }
196
197 pub fn with_metadata<V>(self, key: impl Into<String>, value: V) -> Result<Self>
198 where
199 V: Serialize,
200 {
201 let key = key.into();
202 let value = serde_json::to_value(value).map_err(|error| {
203 crate::BootError::Internal(format!(
204 "failed to serialize controller metadata `{key}`: {error}"
205 ))
206 })?;
207 Ok(self.with_metadata_value(key, value))
208 }
209
210 pub fn with_metadata_value(mut self, key: impl Into<String>, value: Value) -> Self {
211 let key = key.into();
212 self.metadata.insert(key.clone(), value.clone());
213 self.routes = self
214 .routes
215 .into_iter()
216 .map(|route| route.with_metadata_default_value(key.clone(), value.clone()))
217 .collect();
218 self
219 }
220
221 pub fn metadata(&self) -> &BTreeMap<String, Value> {
222 &self.metadata
223 }
224
225 pub fn metadata_value(&self, key: &str) -> Option<&Value> {
226 self.metadata.get(key)
227 }
228
229 pub fn all<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
230 where
231 H: RouteHandler,
232 {
233 self.route(RouteDefinition::all(path, handler)?)
234 }
235
236 pub fn all_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
237 where
238 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
239 H: RouteHandler,
240 {
241 self.route(RouteDefinition::all_scoped(path, factory)?)
242 }
243
244 pub fn all_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
245 where
246 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
247 Fut: Future<Output = Result<R>> + Send + 'static,
248 R: Serialize + Send + 'static,
249 {
250 self.all_json_with_status(path, 200, handler)
251 }
252
253 pub fn all_json_with_status<H, Fut, R>(
254 self,
255 path: impl Into<String>,
256 status: u16,
257 handler: H,
258 ) -> Result<Self>
259 where
260 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
261 Fut: Future<Output = Result<R>> + Send + 'static,
262 R: Serialize + Send + 'static,
263 {
264 self.route(RouteDefinition::all_json_with_status(
265 path, status, handler,
266 )?)
267 }
268
269 pub fn get<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
270 where
271 H: RouteHandler,
272 {
273 self.route(RouteDefinition::get(path, handler)?)
274 }
275
276 pub fn get_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
277 where
278 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
279 H: RouteHandler,
280 {
281 self.route(RouteDefinition::get_scoped(path, factory)?)
282 }
283
284 pub fn get_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
285 where
286 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
287 Fut: Future<Output = Result<R>> + Send + 'static,
288 R: Serialize + Send + 'static,
289 {
290 self.get_json_with_status(path, 200, handler)
291 }
292
293 pub fn get_json_with_status<H, Fut, R>(
294 self,
295 path: impl Into<String>,
296 status: u16,
297 handler: H,
298 ) -> Result<Self>
299 where
300 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
301 Fut: Future<Output = Result<R>> + Send + 'static,
302 R: Serialize + Send + 'static,
303 {
304 self.route(RouteDefinition::get_json_with_status(
305 path, status, handler,
306 )?)
307 }
308
309 pub fn sse<H, Fut, S>(self, path: impl Into<String>, handler: H) -> Result<Self>
310 where
311 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
312 Fut: Future<Output = Result<S>> + Send + 'static,
313 S: Stream<Item = Result<SseEvent>> + Send + 'static,
314 {
315 self.route(RouteDefinition::sse(path, handler)?)
316 }
317
318 pub fn post<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
319 where
320 H: RouteHandler,
321 {
322 self.route(RouteDefinition::post(path, handler)?)
323 }
324
325 pub fn post_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
326 where
327 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
328 H: RouteHandler,
329 {
330 self.route(RouteDefinition::post_scoped(path, factory)?)
331 }
332
333 pub fn post_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
334 where
335 T: DeserializeOwned + Send + 'static,
336 H: Fn(T) -> Fut + Send + Sync + 'static,
337 Fut: Future<Output = Result<R>> + Send + 'static,
338 R: Serialize + Send + 'static,
339 {
340 self.post_json_with_status(path, 200, handler)
341 }
342
343 pub fn post_json_with_status<T, H, Fut, R>(
344 self,
345 path: impl Into<String>,
346 status: u16,
347 handler: H,
348 ) -> Result<Self>
349 where
350 T: DeserializeOwned + Send + 'static,
351 H: Fn(T) -> Fut + Send + Sync + 'static,
352 Fut: Future<Output = Result<R>> + Send + 'static,
353 R: Serialize + Send + 'static,
354 {
355 self.route(RouteDefinition::post_json_with_status(
356 path, status, handler,
357 )?)
358 }
359
360 pub fn post_validated_json<T, H, Fut, R>(
361 self,
362 path: impl Into<String>,
363 handler: H,
364 ) -> Result<Self>
365 where
366 T: DeserializeOwned + Validate + Send + 'static,
367 H: Fn(T) -> Fut + Send + Sync + 'static,
368 Fut: Future<Output = Result<R>> + Send + 'static,
369 R: Serialize + Send + 'static,
370 {
371 self.post_validated_json_with_status(path, 200, handler)
372 }
373
374 pub fn post_validated_json_with_status<T, H, Fut, R>(
375 self,
376 path: impl Into<String>,
377 status: u16,
378 handler: H,
379 ) -> Result<Self>
380 where
381 T: DeserializeOwned + Validate + Send + 'static,
382 H: Fn(T) -> Fut + Send + Sync + 'static,
383 Fut: Future<Output = Result<R>> + Send + 'static,
384 R: Serialize + Send + 'static,
385 {
386 self.route(RouteDefinition::post_validated_json_with_status(
387 path, status, handler,
388 )?)
389 }
390
391 pub fn put<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
392 where
393 H: RouteHandler,
394 {
395 self.route(RouteDefinition::put(path, handler)?)
396 }
397
398 pub fn put_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
399 where
400 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
401 H: RouteHandler,
402 {
403 self.route(RouteDefinition::put_scoped(path, factory)?)
404 }
405
406 pub fn put_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
407 where
408 T: DeserializeOwned + Send + 'static,
409 H: Fn(T) -> Fut + Send + Sync + 'static,
410 Fut: Future<Output = Result<R>> + Send + 'static,
411 R: Serialize + Send + 'static,
412 {
413 self.put_json_with_status(path, 200, handler)
414 }
415
416 pub fn put_json_with_status<T, H, Fut, R>(
417 self,
418 path: impl Into<String>,
419 status: u16,
420 handler: H,
421 ) -> Result<Self>
422 where
423 T: DeserializeOwned + Send + 'static,
424 H: Fn(T) -> Fut + Send + Sync + 'static,
425 Fut: Future<Output = Result<R>> + Send + 'static,
426 R: Serialize + Send + 'static,
427 {
428 self.route(RouteDefinition::put_json_with_status(
429 path, status, handler,
430 )?)
431 }
432
433 pub fn put_validated_json<T, H, Fut, R>(
434 self,
435 path: impl Into<String>,
436 handler: H,
437 ) -> Result<Self>
438 where
439 T: DeserializeOwned + Validate + Send + 'static,
440 H: Fn(T) -> Fut + Send + Sync + 'static,
441 Fut: Future<Output = Result<R>> + Send + 'static,
442 R: Serialize + Send + 'static,
443 {
444 self.put_validated_json_with_status(path, 200, handler)
445 }
446
447 pub fn put_validated_json_with_status<T, H, Fut, R>(
448 self,
449 path: impl Into<String>,
450 status: u16,
451 handler: H,
452 ) -> Result<Self>
453 where
454 T: DeserializeOwned + Validate + Send + 'static,
455 H: Fn(T) -> Fut + Send + Sync + 'static,
456 Fut: Future<Output = Result<R>> + Send + 'static,
457 R: Serialize + Send + 'static,
458 {
459 self.route(RouteDefinition::put_validated_json_with_status(
460 path, status, handler,
461 )?)
462 }
463
464 pub fn patch_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
465 where
466 T: DeserializeOwned + Send + 'static,
467 H: Fn(T) -> Fut + Send + Sync + 'static,
468 Fut: Future<Output = Result<R>> + Send + 'static,
469 R: Serialize + Send + 'static,
470 {
471 self.patch_json_with_status(path, 200, handler)
472 }
473
474 pub fn patch_json_with_status<T, H, Fut, R>(
475 self,
476 path: impl Into<String>,
477 status: u16,
478 handler: H,
479 ) -> Result<Self>
480 where
481 T: DeserializeOwned + Send + 'static,
482 H: Fn(T) -> Fut + Send + Sync + 'static,
483 Fut: Future<Output = Result<R>> + Send + 'static,
484 R: Serialize + Send + 'static,
485 {
486 self.route(RouteDefinition::patch_json_with_status(
487 path, status, handler,
488 )?)
489 }
490
491 pub fn patch_validated_json<T, H, Fut, R>(
492 self,
493 path: impl Into<String>,
494 handler: H,
495 ) -> Result<Self>
496 where
497 T: DeserializeOwned + Validate + Send + 'static,
498 H: Fn(T) -> Fut + Send + Sync + 'static,
499 Fut: Future<Output = Result<R>> + Send + 'static,
500 R: Serialize + Send + 'static,
501 {
502 self.patch_validated_json_with_status(path, 200, handler)
503 }
504
505 pub fn patch_validated_json_with_status<T, H, Fut, R>(
506 self,
507 path: impl Into<String>,
508 status: u16,
509 handler: H,
510 ) -> Result<Self>
511 where
512 T: DeserializeOwned + Validate + Send + 'static,
513 H: Fn(T) -> Fut + Send + Sync + 'static,
514 Fut: Future<Output = Result<R>> + Send + 'static,
515 R: Serialize + Send + 'static,
516 {
517 self.route(RouteDefinition::patch_validated_json_with_status(
518 path, status, handler,
519 )?)
520 }
521
522 pub fn patch<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
523 where
524 H: RouteHandler,
525 {
526 self.route(RouteDefinition::patch(path, handler)?)
527 }
528
529 pub fn patch_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
530 where
531 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
532 H: RouteHandler,
533 {
534 self.route(RouteDefinition::patch_scoped(path, factory)?)
535 }
536
537 pub fn delete<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
538 where
539 H: RouteHandler,
540 {
541 self.route(RouteDefinition::delete(path, handler)?)
542 }
543
544 pub fn delete_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
545 where
546 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
547 H: RouteHandler,
548 {
549 self.route(RouteDefinition::delete_scoped(path, factory)?)
550 }
551
552 pub fn delete_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
553 where
554 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
555 Fut: Future<Output = Result<R>> + Send + 'static,
556 R: Serialize + Send + 'static,
557 {
558 self.delete_json_with_status(path, 200, handler)
559 }
560
561 pub fn delete_json_with_status<H, Fut, R>(
562 self,
563 path: impl Into<String>,
564 status: u16,
565 handler: H,
566 ) -> Result<Self>
567 where
568 H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
569 Fut: Future<Output = Result<R>> + Send + 'static,
570 R: Serialize + Send + 'static,
571 {
572 self.route(RouteDefinition::delete_json_with_status(
573 path, status, handler,
574 )?)
575 }
576
577 pub fn options<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
578 where
579 H: RouteHandler,
580 {
581 self.route(RouteDefinition::options(path, handler)?)
582 }
583
584 pub fn options_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
585 where
586 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
587 H: RouteHandler,
588 {
589 self.route(RouteDefinition::options_scoped(path, factory)?)
590 }
591
592 pub fn head<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
593 where
594 H: RouteHandler,
595 {
596 self.route(RouteDefinition::head(path, handler)?)
597 }
598
599 pub fn head_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
600 where
601 F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
602 H: RouteHandler,
603 {
604 self.route(RouteDefinition::head_scoped(path, factory)?)
605 }
606
607 pub fn prefix(&self) -> &str {
608 &self.prefix
609 }
610
611 pub fn routes(&self) -> &[RouteDefinition] {
612 &self.routes
613 }
614
615 pub(crate) fn into_routes(self) -> Vec<RouteDefinition> {
616 self.routes
617 }
618}