Skip to main content

a3s_boot/routing/
controller.rs

1use super::handler::RouteHandler;
2use super::host::validate_host_pattern;
3use super::path::normalize_prefix;
4use super::route::RouteDefinition;
5use crate::pipeline::{PipelineComponent, PipelineComponents};
6use crate::{
7    BootErrorKind, BootRequest, ExceptionFilter, ExecutionInterceptor, Guard, Interceptor,
8    Middleware, ModuleRef, OpenApiExample, OpenApiHeader, OpenApiParameter, OpenApiRequestBody,
9    OpenApiResponse, OpenApiSchema, Pipe, Result, RouteVersioning, SerializationOptions, SseEvent,
10    Validate, ValidationOptions,
11};
12use futures_core::Stream;
13use serde::de::DeserializeOwned;
14use serde::Serialize;
15use serde_json::Value;
16use std::collections::BTreeMap;
17use std::future::Future;
18use std::sync::Arc;
19#[cfg(feature = "cache")]
20use std::time::Duration;
21
22/// Group routes under a common HTTP prefix, similar to a Nest controller.
23#[derive(Clone)]
24pub struct ControllerDefinition {
25    prefix: String,
26    host: Option<String>,
27    routes: Vec<RouteDefinition>,
28    pipeline: PipelineComponents,
29    openapi_tags: Vec<String>,
30    openapi_schema_components: BTreeMap<String, OpenApiSchema>,
31    openapi_response_components: BTreeMap<String, OpenApiResponse>,
32    openapi_parameter_components: BTreeMap<String, OpenApiParameter>,
33    openapi_example_components: BTreeMap<String, OpenApiExample>,
34    openapi_request_body_components: BTreeMap<String, OpenApiRequestBody>,
35    openapi_header_components: BTreeMap<String, OpenApiHeader>,
36    openapi_extensions: BTreeMap<String, Value>,
37    openapi_hidden: bool,
38    versioning: RouteVersioning,
39    serialization: Option<SerializationOptions>,
40    metadata: BTreeMap<String, Value>,
41}
42
43impl ControllerDefinition {
44    pub fn new(prefix: impl Into<String>) -> Result<Self> {
45        let prefix = normalize_prefix(&prefix.into())?;
46        Ok(Self {
47            prefix,
48            host: None,
49            routes: Vec::new(),
50            pipeline: PipelineComponents::default(),
51            openapi_tags: Vec::new(),
52            openapi_schema_components: BTreeMap::new(),
53            openapi_response_components: BTreeMap::new(),
54            openapi_parameter_components: BTreeMap::new(),
55            openapi_example_components: BTreeMap::new(),
56            openapi_request_body_components: BTreeMap::new(),
57            openapi_header_components: BTreeMap::new(),
58            openapi_extensions: BTreeMap::new(),
59            openapi_hidden: false,
60            versioning: RouteVersioning::default(),
61            serialization: None,
62            metadata: BTreeMap::new(),
63        })
64    }
65
66    pub fn route(mut self, route: RouteDefinition) -> Result<Self> {
67        let mut route = route;
68        route = route.with_host_default(self.host.as_deref())?;
69        if route.versioning().is_unspecified() && !self.versioning.is_unspecified() {
70            route = match &self.versioning {
71                RouteVersioning::Unspecified => route,
72                RouteVersioning::Versions(versions) => route.with_versions(versions.clone()),
73                RouteVersioning::Neutral => route.version_neutral(),
74            };
75        }
76        if route.serialization().is_empty() {
77            if let Some(serialization) = &self.serialization {
78                route = route.with_serialization(serialization.clone());
79            }
80        }
81        for tag in &self.openapi_tags {
82            route = route.with_tag(tag.clone());
83        }
84        for (name, schema) in &self.openapi_schema_components {
85            route = route.with_schema_component(name.clone(), schema.clone());
86        }
87        for (name, response) in &self.openapi_response_components {
88            route = route.with_response_component(name.clone(), response.clone());
89        }
90        for (name, parameter) in &self.openapi_parameter_components {
91            route = route.with_parameter_component(name.clone(), parameter.clone());
92        }
93        for (name, example) in &self.openapi_example_components {
94            route = route.with_example_component(name.clone(), example.clone());
95        }
96        for (name, request_body) in &self.openapi_request_body_components {
97            route = route.with_request_body_component(name.clone(), request_body.clone());
98        }
99        for (name, header) in &self.openapi_header_components {
100            route = route.with_header_component(name.clone(), header.clone());
101        }
102        for (name, value) in &self.openapi_extensions {
103            route = route.with_openapi_extension_default_value(name.clone(), value.clone());
104        }
105        if self.openapi_hidden {
106            route = route.hide_from_openapi();
107        }
108        route = route.with_metadata_defaults(&self.metadata);
109        self.routes.push(
110            route
111                .with_prefix(&self.prefix)?
112                .with_pipeline_prefix(&self.pipeline),
113        );
114        Ok(self)
115    }
116
117    pub fn with_pipe<P>(mut self, pipe: P) -> Self
118    where
119        P: Pipe,
120    {
121        let index = self.pipeline.pipes.len();
122        let component = PipelineComponent::<dyn Pipe>::new(pipe);
123        self.pipeline.pipes.push(component.clone());
124        self.routes = self
125            .routes
126            .into_iter()
127            .map(|route| route.insert_pipe_prefix_at(index, component.clone()))
128            .collect();
129        self
130    }
131
132    pub fn with_middleware<M>(mut self, middleware: M) -> Self
133    where
134        M: Middleware,
135    {
136        let index = self.pipeline.middleware.len();
137        let middleware = Arc::new(middleware);
138        self.pipeline.middleware.push(middleware.clone());
139        self.routes = self
140            .routes
141            .into_iter()
142            .map(|route| route.insert_middleware_prefix_at(index, middleware.clone()))
143            .collect();
144        self
145    }
146
147    pub fn with_guard<G>(mut self, guard: G) -> Self
148    where
149        G: Guard,
150    {
151        let index = self.pipeline.guards.len();
152        let component = PipelineComponent::<dyn Guard>::new(guard);
153        self.pipeline.guards.push(component.clone());
154        self.routes = self
155            .routes
156            .into_iter()
157            .map(|route| route.insert_guard_prefix_at(index, component.clone()))
158            .collect();
159        self
160    }
161
162    pub fn with_interceptor<I>(mut self, interceptor: I) -> Self
163    where
164        I: Interceptor,
165    {
166        let index = self.pipeline.interceptors.len();
167        let component = PipelineComponent::<dyn Interceptor>::new(interceptor);
168        self.pipeline.interceptors.push(component.clone());
169        self.routes = self
170            .routes
171            .into_iter()
172            .map(|route| route.insert_interceptor_prefix_at(index, component.clone()))
173            .collect();
174        self
175    }
176
177    pub fn with_execution_interceptor<I>(mut self, interceptor: I) -> Self
178    where
179        I: ExecutionInterceptor,
180    {
181        self = self.with_interceptor(crate::pipeline::ExecutionInterceptorAdapter::new(
182            interceptor,
183        ));
184        self
185    }
186
187    pub fn with_filter<F>(mut self, filter: F) -> Self
188    where
189        F: ExceptionFilter,
190    {
191        let index = self.pipeline.filters.len();
192        let component = PipelineComponent::<dyn ExceptionFilter>::new(filter);
193        self.pipeline.filters.push(component.clone());
194        self.routes = self
195            .routes
196            .into_iter()
197            .map(|route| route.insert_filter_prefix_at(index, component.clone()))
198            .collect();
199        self
200    }
201
202    pub fn with_catch_filter<I, F>(mut self, kinds: I, filter: F) -> Self
203    where
204        I: IntoIterator<Item = BootErrorKind>,
205        F: ExceptionFilter,
206    {
207        self = self.with_filter(crate::catch_errors(kinds, filter));
208        self
209    }
210
211    pub fn with_validation(mut self) -> Self {
212        self.pipeline.enable_validation();
213        let prefix = PipelineComponents {
214            validation_enabled: true,
215            ..PipelineComponents::default()
216        };
217        self.apply_pipeline_prefix(prefix);
218        self
219    }
220
221    pub fn with_validation_options(mut self, options: ValidationOptions) -> Self {
222        self.pipeline.enable_validation_with_options(options);
223        let prefix = PipelineComponents {
224            validation_enabled: true,
225            validation_options: options,
226            ..PipelineComponents::default()
227        };
228        self.apply_pipeline_prefix(prefix);
229        self
230    }
231
232    fn apply_pipeline_prefix(&mut self, prefix: PipelineComponents) {
233        self.routes = self
234            .routes
235            .drain(..)
236            .map(|route| route.with_pipeline_prefix(&prefix))
237            .collect();
238    }
239
240    pub fn with_host(mut self, pattern: impl Into<String>) -> Result<Self> {
241        let pattern = pattern.into();
242        validate_host_pattern(&pattern)?;
243        self.host = Some(pattern.clone());
244        self.routes = self
245            .routes
246            .into_iter()
247            .map(|route| route.with_host_default(Some(&pattern)))
248            .collect::<Result<Vec<_>>>()?;
249        Ok(self)
250    }
251
252    pub fn without_host(mut self) -> Self {
253        self.host = None;
254        self.routes = self
255            .routes
256            .into_iter()
257            .map(RouteDefinition::without_host)
258            .collect();
259        self
260    }
261
262    pub fn host(&self) -> Option<&str> {
263        self.host.as_deref()
264    }
265
266    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
267        let tag = tag.into();
268        if !self.openapi_tags.contains(&tag) {
269            self.openapi_tags.push(tag.clone());
270        }
271        self.routes = self
272            .routes
273            .into_iter()
274            .map(|route| route.with_tag(tag.clone()))
275            .collect();
276        self
277    }
278
279    pub fn with_schema_component(mut self, name: impl Into<String>, schema: OpenApiSchema) -> Self {
280        let name = name.into();
281        self.openapi_schema_components
282            .insert(name.clone(), schema.clone());
283        self.routes = self
284            .routes
285            .into_iter()
286            .map(|route| route.with_schema_component(name.clone(), schema.clone()))
287            .collect();
288        self
289    }
290
291    pub fn with_response_component(
292        mut self,
293        name: impl Into<String>,
294        response: OpenApiResponse,
295    ) -> Self {
296        let name = name.into();
297        self.openapi_response_components
298            .insert(name.clone(), response.clone());
299        self.routes = self
300            .routes
301            .into_iter()
302            .map(|route| route.with_response_component(name.clone(), response.clone()))
303            .collect();
304        self
305    }
306
307    pub fn with_parameter_component(
308        mut self,
309        name: impl Into<String>,
310        parameter: OpenApiParameter,
311    ) -> Self {
312        let name = name.into();
313        self.openapi_parameter_components
314            .insert(name.clone(), parameter.clone());
315        self.routes = self
316            .routes
317            .into_iter()
318            .map(|route| route.with_parameter_component(name.clone(), parameter.clone()))
319            .collect();
320        self
321    }
322
323    pub fn with_example_component(
324        mut self,
325        name: impl Into<String>,
326        example: OpenApiExample,
327    ) -> Self {
328        let name = name.into();
329        self.openapi_example_components
330            .insert(name.clone(), example.clone());
331        self.routes = self
332            .routes
333            .into_iter()
334            .map(|route| route.with_example_component(name.clone(), example.clone()))
335            .collect();
336        self
337    }
338
339    pub fn try_with_example_component<T>(self, name: impl Into<String>, value: T) -> Result<Self>
340    where
341        T: Serialize,
342    {
343        Ok(self.with_example_component(name, OpenApiExample::try_value(value)?))
344    }
345
346    pub fn with_request_body_component(
347        mut self,
348        name: impl Into<String>,
349        request_body: OpenApiRequestBody,
350    ) -> Self {
351        let name = name.into();
352        self.openapi_request_body_components
353            .insert(name.clone(), request_body.clone());
354        self.routes = self
355            .routes
356            .into_iter()
357            .map(|route| route.with_request_body_component(name.clone(), request_body.clone()))
358            .collect();
359        self
360    }
361
362    pub fn with_header_component(mut self, name: impl Into<String>, header: OpenApiHeader) -> Self {
363        let name = name.into();
364        self.openapi_header_components
365            .insert(name.clone(), header.clone());
366        self.routes = self
367            .routes
368            .into_iter()
369            .map(|route| route.with_header_component(name.clone(), header.clone()))
370            .collect();
371        self
372    }
373
374    pub fn with_openapi_extension_value(mut self, name: impl Into<String>, value: Value) -> Self {
375        let name = name.into();
376        self.openapi_extensions.insert(name.clone(), value.clone());
377        self.routes = self
378            .routes
379            .into_iter()
380            .map(|route| route.with_openapi_extension_default_value(name.clone(), value.clone()))
381            .collect();
382        self
383    }
384
385    pub fn try_with_openapi_extension<T>(self, name: impl Into<String>, value: T) -> Result<Self>
386    where
387        T: Serialize,
388    {
389        let name = name.into();
390        let value = serde_json::to_value(value).map_err(|error| {
391            crate::BootError::Internal(format!(
392                "OpenAPI extension `{name}` could not be serialized: {error}"
393            ))
394        })?;
395        Ok(self.with_openapi_extension_value(name, value))
396    }
397
398    pub fn hide_from_openapi(mut self) -> Self {
399        self.openapi_hidden = true;
400        self.routes = self
401            .routes
402            .into_iter()
403            .map(RouteDefinition::hide_from_openapi)
404            .collect();
405        self
406    }
407
408    #[cfg(feature = "openapi-schemas")]
409    pub fn try_with_json_schema_component<T>(self) -> Result<Self>
410    where
411        T: schemars::JsonSchema,
412    {
413        let schema = OpenApiSchema::json_schema::<T>()
414            .map_err(|error| crate::BootError::Internal(error.to_string()))?;
415        Ok(self.with_schema_component(crate::openapi_schema_name::<T>(), schema))
416    }
417
418    pub fn with_version(mut self, version: impl Into<String>) -> Self {
419        self.versioning = RouteVersioning::version(version);
420        self
421    }
422
423    pub fn with_versions<I, V>(mut self, versions: I) -> Self
424    where
425        I: IntoIterator<Item = V>,
426        V: Into<String>,
427    {
428        self.versioning = RouteVersioning::versions(versions);
429        self
430    }
431
432    pub fn version_neutral(mut self) -> Self {
433        self.versioning = RouteVersioning::neutral();
434        self
435    }
436
437    pub fn with_serialization(mut self, options: SerializationOptions) -> Self {
438        self.serialization = Some(options);
439        self
440    }
441
442    pub fn with_metadata<V>(self, key: impl Into<String>, value: V) -> Result<Self>
443    where
444        V: Serialize,
445    {
446        let key = key.into();
447        let value = serde_json::to_value(value).map_err(|error| {
448            crate::BootError::Internal(format!(
449                "failed to serialize controller metadata `{key}`: {error}"
450            ))
451        })?;
452        Ok(self.with_metadata_value(key, value))
453    }
454
455    pub fn with_metadata_value(mut self, key: impl Into<String>, value: Value) -> Self {
456        let key = key.into();
457        self.metadata.insert(key.clone(), value.clone());
458        self.routes = self
459            .routes
460            .into_iter()
461            .map(|route| route.with_metadata_default_value(key.clone(), value.clone()))
462            .collect();
463        self
464    }
465
466    #[cfg(feature = "cache")]
467    pub fn with_cache_key(self, key: impl Into<String>) -> Self {
468        self.with_metadata_value(crate::CACHE_KEY_METADATA, Value::String(key.into()))
469    }
470
471    #[cfg(feature = "cache")]
472    pub fn with_cache_ttl(self, ttl: Duration) -> Self {
473        self.with_metadata_value(
474            crate::CACHE_TTL_METADATA,
475            Value::Number(serde_json::Number::from(ttl.as_millis() as u64)),
476        )
477    }
478
479    #[cfg(feature = "cache")]
480    pub fn without_cache(self) -> Self {
481        self.with_metadata_value(crate::CACHE_DISABLED_METADATA, Value::Bool(true))
482    }
483
484    pub fn metadata(&self) -> &BTreeMap<String, Value> {
485        &self.metadata
486    }
487
488    pub fn metadata_value(&self, key: &str) -> Option<&Value> {
489        self.metadata.get(key)
490    }
491
492    pub fn all<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
493    where
494        H: RouteHandler,
495    {
496        self.route(RouteDefinition::all(path, handler)?)
497    }
498
499    pub fn all_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
500    where
501        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
502        H: RouteHandler,
503    {
504        self.route(RouteDefinition::all_scoped(path, factory)?)
505    }
506
507    pub fn all_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
508    where
509        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
510        Fut: Future<Output = Result<R>> + Send + 'static,
511        R: Serialize + Send + 'static,
512    {
513        self.all_json_with_status(path, 200, handler)
514    }
515
516    pub fn all_json_with_status<H, Fut, R>(
517        self,
518        path: impl Into<String>,
519        status: u16,
520        handler: H,
521    ) -> Result<Self>
522    where
523        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
524        Fut: Future<Output = Result<R>> + Send + 'static,
525        R: Serialize + Send + 'static,
526    {
527        self.route(RouteDefinition::all_json_with_status(
528            path, status, handler,
529        )?)
530    }
531
532    pub fn view<H, Fut, R>(
533        self,
534        method: crate::HttpMethod,
535        path: impl Into<String>,
536        view: impl Into<String>,
537        handler: H,
538    ) -> Result<Self>
539    where
540        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
541        Fut: Future<Output = Result<R>> + Send + 'static,
542        R: Serialize + Send + 'static,
543    {
544        self.view_with_status(method, path, view, 200, handler)
545    }
546
547    pub fn view_with_status<H, Fut, R>(
548        self,
549        method: crate::HttpMethod,
550        path: impl Into<String>,
551        view: impl Into<String>,
552        status: u16,
553        handler: H,
554    ) -> Result<Self>
555    where
556        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
557        Fut: Future<Output = Result<R>> + Send + 'static,
558        R: Serialize + Send + 'static,
559    {
560        self.route(RouteDefinition::view_with_status(
561            method, path, view, status, handler,
562        )?)
563    }
564
565    pub fn get<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
566    where
567        H: RouteHandler,
568    {
569        self.route(RouteDefinition::get(path, handler)?)
570    }
571
572    pub fn get_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
573    where
574        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
575        H: RouteHandler,
576    {
577        self.route(RouteDefinition::get_scoped(path, factory)?)
578    }
579
580    pub fn get_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
581    where
582        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
583        Fut: Future<Output = Result<R>> + Send + 'static,
584        R: Serialize + Send + 'static,
585    {
586        self.get_json_with_status(path, 200, handler)
587    }
588
589    pub fn get_json_with_status<H, Fut, R>(
590        self,
591        path: impl Into<String>,
592        status: u16,
593        handler: H,
594    ) -> Result<Self>
595    where
596        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
597        Fut: Future<Output = Result<R>> + Send + 'static,
598        R: Serialize + Send + 'static,
599    {
600        self.route(RouteDefinition::get_json_with_status(
601            path, status, handler,
602        )?)
603    }
604
605    pub fn get_view<H, Fut, R>(
606        self,
607        path: impl Into<String>,
608        view: impl Into<String>,
609        handler: H,
610    ) -> Result<Self>
611    where
612        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
613        Fut: Future<Output = Result<R>> + Send + 'static,
614        R: Serialize + Send + 'static,
615    {
616        self.get_view_with_status(path, view, 200, handler)
617    }
618
619    pub fn get_view_with_status<H, Fut, R>(
620        self,
621        path: impl Into<String>,
622        view: impl Into<String>,
623        status: u16,
624        handler: H,
625    ) -> Result<Self>
626    where
627        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
628        Fut: Future<Output = Result<R>> + Send + 'static,
629        R: Serialize + Send + 'static,
630    {
631        self.route(RouteDefinition::get_view_with_status(
632            path, view, status, handler,
633        )?)
634    }
635
636    pub fn sse<H, Fut, S>(self, path: impl Into<String>, handler: H) -> Result<Self>
637    where
638        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
639        Fut: Future<Output = Result<S>> + Send + 'static,
640        S: Stream<Item = Result<SseEvent>> + Send + 'static,
641    {
642        self.route(RouteDefinition::sse(path, handler)?)
643    }
644
645    pub fn post<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
646    where
647        H: RouteHandler,
648    {
649        self.route(RouteDefinition::post(path, handler)?)
650    }
651
652    pub fn post_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
653    where
654        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
655        H: RouteHandler,
656    {
657        self.route(RouteDefinition::post_scoped(path, factory)?)
658    }
659
660    pub fn post_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
661    where
662        T: DeserializeOwned + Send + 'static,
663        H: Fn(T) -> Fut + Send + Sync + 'static,
664        Fut: Future<Output = Result<R>> + Send + 'static,
665        R: Serialize + Send + 'static,
666    {
667        self.post_json_with_status(path, 200, handler)
668    }
669
670    pub fn post_json_with_status<T, H, Fut, R>(
671        self,
672        path: impl Into<String>,
673        status: u16,
674        handler: H,
675    ) -> Result<Self>
676    where
677        T: DeserializeOwned + Send + 'static,
678        H: Fn(T) -> Fut + Send + Sync + 'static,
679        Fut: Future<Output = Result<R>> + Send + 'static,
680        R: Serialize + Send + 'static,
681    {
682        self.route(RouteDefinition::post_json_with_status(
683            path, status, handler,
684        )?)
685    }
686
687    pub fn post_view<H, Fut, R>(
688        self,
689        path: impl Into<String>,
690        view: impl Into<String>,
691        handler: H,
692    ) -> Result<Self>
693    where
694        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
695        Fut: Future<Output = Result<R>> + Send + 'static,
696        R: Serialize + Send + 'static,
697    {
698        self.post_view_with_status(path, view, 200, handler)
699    }
700
701    pub fn post_view_with_status<H, Fut, R>(
702        self,
703        path: impl Into<String>,
704        view: impl Into<String>,
705        status: u16,
706        handler: H,
707    ) -> Result<Self>
708    where
709        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
710        Fut: Future<Output = Result<R>> + Send + 'static,
711        R: Serialize + Send + 'static,
712    {
713        self.route(RouteDefinition::post_view_with_status(
714            path, view, status, handler,
715        )?)
716    }
717
718    pub fn post_validated_json<T, H, Fut, R>(
719        self,
720        path: impl Into<String>,
721        handler: H,
722    ) -> Result<Self>
723    where
724        T: DeserializeOwned + Validate + Send + 'static,
725        H: Fn(T) -> Fut + Send + Sync + 'static,
726        Fut: Future<Output = Result<R>> + Send + 'static,
727        R: Serialize + Send + 'static,
728    {
729        self.post_validated_json_with_status(path, 200, handler)
730    }
731
732    pub fn post_validated_json_with_status<T, H, Fut, R>(
733        self,
734        path: impl Into<String>,
735        status: u16,
736        handler: H,
737    ) -> Result<Self>
738    where
739        T: DeserializeOwned + Validate + Send + 'static,
740        H: Fn(T) -> Fut + Send + Sync + 'static,
741        Fut: Future<Output = Result<R>> + Send + 'static,
742        R: Serialize + Send + 'static,
743    {
744        self.route(RouteDefinition::post_validated_json_with_status(
745            path, status, handler,
746        )?)
747    }
748
749    pub fn put<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
750    where
751        H: RouteHandler,
752    {
753        self.route(RouteDefinition::put(path, handler)?)
754    }
755
756    pub fn put_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
757    where
758        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
759        H: RouteHandler,
760    {
761        self.route(RouteDefinition::put_scoped(path, factory)?)
762    }
763
764    pub fn put_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
765    where
766        T: DeserializeOwned + Send + 'static,
767        H: Fn(T) -> Fut + Send + Sync + 'static,
768        Fut: Future<Output = Result<R>> + Send + 'static,
769        R: Serialize + Send + 'static,
770    {
771        self.put_json_with_status(path, 200, handler)
772    }
773
774    pub fn put_json_with_status<T, H, Fut, R>(
775        self,
776        path: impl Into<String>,
777        status: u16,
778        handler: H,
779    ) -> Result<Self>
780    where
781        T: DeserializeOwned + Send + 'static,
782        H: Fn(T) -> Fut + Send + Sync + 'static,
783        Fut: Future<Output = Result<R>> + Send + 'static,
784        R: Serialize + Send + 'static,
785    {
786        self.route(RouteDefinition::put_json_with_status(
787            path, status, handler,
788        )?)
789    }
790
791    pub fn put_validated_json<T, H, Fut, R>(
792        self,
793        path: impl Into<String>,
794        handler: H,
795    ) -> Result<Self>
796    where
797        T: DeserializeOwned + Validate + Send + 'static,
798        H: Fn(T) -> Fut + Send + Sync + 'static,
799        Fut: Future<Output = Result<R>> + Send + 'static,
800        R: Serialize + Send + 'static,
801    {
802        self.put_validated_json_with_status(path, 200, handler)
803    }
804
805    pub fn put_validated_json_with_status<T, H, Fut, R>(
806        self,
807        path: impl Into<String>,
808        status: u16,
809        handler: H,
810    ) -> Result<Self>
811    where
812        T: DeserializeOwned + Validate + Send + 'static,
813        H: Fn(T) -> Fut + Send + Sync + 'static,
814        Fut: Future<Output = Result<R>> + Send + 'static,
815        R: Serialize + Send + 'static,
816    {
817        self.route(RouteDefinition::put_validated_json_with_status(
818            path, status, handler,
819        )?)
820    }
821
822    pub fn patch_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
823    where
824        T: DeserializeOwned + Send + 'static,
825        H: Fn(T) -> Fut + Send + Sync + 'static,
826        Fut: Future<Output = Result<R>> + Send + 'static,
827        R: Serialize + Send + 'static,
828    {
829        self.patch_json_with_status(path, 200, handler)
830    }
831
832    pub fn patch_json_with_status<T, H, Fut, R>(
833        self,
834        path: impl Into<String>,
835        status: u16,
836        handler: H,
837    ) -> Result<Self>
838    where
839        T: DeserializeOwned + Send + 'static,
840        H: Fn(T) -> Fut + Send + Sync + 'static,
841        Fut: Future<Output = Result<R>> + Send + 'static,
842        R: Serialize + Send + 'static,
843    {
844        self.route(RouteDefinition::patch_json_with_status(
845            path, status, handler,
846        )?)
847    }
848
849    pub fn patch_validated_json<T, H, Fut, R>(
850        self,
851        path: impl Into<String>,
852        handler: H,
853    ) -> Result<Self>
854    where
855        T: DeserializeOwned + Validate + Send + 'static,
856        H: Fn(T) -> Fut + Send + Sync + 'static,
857        Fut: Future<Output = Result<R>> + Send + 'static,
858        R: Serialize + Send + 'static,
859    {
860        self.patch_validated_json_with_status(path, 200, handler)
861    }
862
863    pub fn patch_validated_json_with_status<T, H, Fut, R>(
864        self,
865        path: impl Into<String>,
866        status: u16,
867        handler: H,
868    ) -> Result<Self>
869    where
870        T: DeserializeOwned + Validate + Send + 'static,
871        H: Fn(T) -> Fut + Send + Sync + 'static,
872        Fut: Future<Output = Result<R>> + Send + 'static,
873        R: Serialize + Send + 'static,
874    {
875        self.route(RouteDefinition::patch_validated_json_with_status(
876            path, status, handler,
877        )?)
878    }
879
880    pub fn patch<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
881    where
882        H: RouteHandler,
883    {
884        self.route(RouteDefinition::patch(path, handler)?)
885    }
886
887    pub fn patch_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
888    where
889        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
890        H: RouteHandler,
891    {
892        self.route(RouteDefinition::patch_scoped(path, factory)?)
893    }
894
895    pub fn delete<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
896    where
897        H: RouteHandler,
898    {
899        self.route(RouteDefinition::delete(path, handler)?)
900    }
901
902    pub fn delete_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
903    where
904        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
905        H: RouteHandler,
906    {
907        self.route(RouteDefinition::delete_scoped(path, factory)?)
908    }
909
910    pub fn delete_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
911    where
912        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
913        Fut: Future<Output = Result<R>> + Send + 'static,
914        R: Serialize + Send + 'static,
915    {
916        self.delete_json_with_status(path, 200, handler)
917    }
918
919    pub fn delete_json_with_status<H, Fut, R>(
920        self,
921        path: impl Into<String>,
922        status: u16,
923        handler: H,
924    ) -> Result<Self>
925    where
926        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
927        Fut: Future<Output = Result<R>> + Send + 'static,
928        R: Serialize + Send + 'static,
929    {
930        self.route(RouteDefinition::delete_json_with_status(
931            path, status, handler,
932        )?)
933    }
934
935    pub fn options<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
936    where
937        H: RouteHandler,
938    {
939        self.route(RouteDefinition::options(path, handler)?)
940    }
941
942    pub fn options_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
943    where
944        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
945        H: RouteHandler,
946    {
947        self.route(RouteDefinition::options_scoped(path, factory)?)
948    }
949
950    pub fn head<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
951    where
952        H: RouteHandler,
953    {
954        self.route(RouteDefinition::head(path, handler)?)
955    }
956
957    pub fn head_scoped<F, H>(self, path: impl Into<String>, factory: F) -> Result<Self>
958    where
959        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
960        H: RouteHandler,
961    {
962        self.route(RouteDefinition::head_scoped(path, factory)?)
963    }
964
965    pub fn prefix(&self) -> &str {
966        &self.prefix
967    }
968
969    pub fn routes(&self) -> &[RouteDefinition] {
970        &self.routes
971    }
972
973    pub(crate) fn into_routes(self) -> Vec<RouteDefinition> {
974        self.routes
975    }
976}