Skip to main content

a3s_boot/routing/
controller.rs

1use super::handler::RouteHandler;
2use super::path::normalize_prefix;
3use super::route::RouteDefinition;
4use crate::pipeline::PipelineComponents;
5use crate::{BootRequest, ExceptionFilter, Guard, Interceptor, Pipe, Result, SseEvent};
6use futures_core::Stream;
7use serde::de::DeserializeOwned;
8use serde::Serialize;
9use std::future::Future;
10
11/// Group routes under a common HTTP prefix, similar to a Nest controller.
12#[derive(Clone)]
13pub struct ControllerDefinition {
14    prefix: String,
15    routes: Vec<RouteDefinition>,
16    pipeline: PipelineComponents,
17}
18
19impl ControllerDefinition {
20    pub fn new(prefix: impl Into<String>) -> Result<Self> {
21        let prefix = normalize_prefix(&prefix.into())?;
22        Ok(Self {
23            prefix,
24            routes: Vec::new(),
25            pipeline: PipelineComponents::default(),
26        })
27    }
28
29    pub fn route(mut self, route: RouteDefinition) -> Result<Self> {
30        self.routes.push(
31            route
32                .with_prefix(&self.prefix)?
33                .with_pipeline_prefix(&self.pipeline),
34        );
35        Ok(self)
36    }
37
38    pub fn with_pipe<P>(mut self, pipe: P) -> Self
39    where
40        P: Pipe,
41    {
42        self.pipeline.push_pipe(pipe);
43        self
44    }
45
46    pub fn with_guard<G>(mut self, guard: G) -> Self
47    where
48        G: Guard,
49    {
50        self.pipeline.push_guard(guard);
51        self
52    }
53
54    pub fn with_interceptor<I>(mut self, interceptor: I) -> Self
55    where
56        I: Interceptor,
57    {
58        self.pipeline.push_interceptor(interceptor);
59        self
60    }
61
62    pub fn with_filter<F>(mut self, filter: F) -> Self
63    where
64        F: ExceptionFilter,
65    {
66        self.pipeline.push_filter(filter);
67        self
68    }
69
70    pub fn get<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
71    where
72        H: RouteHandler,
73    {
74        self.route(RouteDefinition::get(path, handler)?)
75    }
76
77    pub fn get_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
78    where
79        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
80        Fut: Future<Output = Result<R>> + Send + 'static,
81        R: Serialize + Send + 'static,
82    {
83        self.get_json_with_status(path, 200, handler)
84    }
85
86    pub fn get_json_with_status<H, Fut, R>(
87        self,
88        path: impl Into<String>,
89        status: u16,
90        handler: H,
91    ) -> Result<Self>
92    where
93        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
94        Fut: Future<Output = Result<R>> + Send + 'static,
95        R: Serialize + Send + 'static,
96    {
97        self.route(RouteDefinition::get_json_with_status(
98            path, status, handler,
99        )?)
100    }
101
102    pub fn sse<H, Fut, S>(self, path: impl Into<String>, handler: H) -> Result<Self>
103    where
104        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
105        Fut: Future<Output = Result<S>> + Send + 'static,
106        S: Stream<Item = Result<SseEvent>> + Send + 'static,
107    {
108        self.route(RouteDefinition::sse(path, handler)?)
109    }
110
111    pub fn post<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
112    where
113        H: RouteHandler,
114    {
115        self.route(RouteDefinition::post(path, handler)?)
116    }
117
118    pub fn post_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
119    where
120        T: DeserializeOwned + Send + 'static,
121        H: Fn(T) -> Fut + Send + Sync + 'static,
122        Fut: Future<Output = Result<R>> + Send + 'static,
123        R: Serialize + Send + 'static,
124    {
125        self.post_json_with_status(path, 200, handler)
126    }
127
128    pub fn post_json_with_status<T, H, Fut, R>(
129        self,
130        path: impl Into<String>,
131        status: u16,
132        handler: H,
133    ) -> Result<Self>
134    where
135        T: DeserializeOwned + Send + 'static,
136        H: Fn(T) -> Fut + Send + Sync + 'static,
137        Fut: Future<Output = Result<R>> + Send + 'static,
138        R: Serialize + Send + 'static,
139    {
140        self.route(RouteDefinition::post_json_with_status(
141            path, status, handler,
142        )?)
143    }
144
145    pub fn put<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
146    where
147        H: RouteHandler,
148    {
149        self.route(RouteDefinition::put(path, handler)?)
150    }
151
152    pub fn put_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
153    where
154        T: DeserializeOwned + Send + 'static,
155        H: Fn(T) -> Fut + Send + Sync + 'static,
156        Fut: Future<Output = Result<R>> + Send + 'static,
157        R: Serialize + Send + 'static,
158    {
159        self.put_json_with_status(path, 200, handler)
160    }
161
162    pub fn put_json_with_status<T, H, Fut, R>(
163        self,
164        path: impl Into<String>,
165        status: u16,
166        handler: H,
167    ) -> Result<Self>
168    where
169        T: DeserializeOwned + Send + 'static,
170        H: Fn(T) -> Fut + Send + Sync + 'static,
171        Fut: Future<Output = Result<R>> + Send + 'static,
172        R: Serialize + Send + 'static,
173    {
174        self.route(RouteDefinition::put_json_with_status(
175            path, status, handler,
176        )?)
177    }
178
179    pub fn patch_json<T, H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
180    where
181        T: DeserializeOwned + Send + 'static,
182        H: Fn(T) -> Fut + Send + Sync + 'static,
183        Fut: Future<Output = Result<R>> + Send + 'static,
184        R: Serialize + Send + 'static,
185    {
186        self.patch_json_with_status(path, 200, handler)
187    }
188
189    pub fn patch_json_with_status<T, H, Fut, R>(
190        self,
191        path: impl Into<String>,
192        status: u16,
193        handler: H,
194    ) -> Result<Self>
195    where
196        T: DeserializeOwned + Send + 'static,
197        H: Fn(T) -> Fut + Send + Sync + 'static,
198        Fut: Future<Output = Result<R>> + Send + 'static,
199        R: Serialize + Send + 'static,
200    {
201        self.route(RouteDefinition::patch_json_with_status(
202            path, status, handler,
203        )?)
204    }
205
206    pub fn patch<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
207    where
208        H: RouteHandler,
209    {
210        self.route(RouteDefinition::patch(path, handler)?)
211    }
212
213    pub fn delete<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
214    where
215        H: RouteHandler,
216    {
217        self.route(RouteDefinition::delete(path, handler)?)
218    }
219
220    pub fn delete_json<H, Fut, R>(self, path: impl Into<String>, handler: H) -> Result<Self>
221    where
222        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
223        Fut: Future<Output = Result<R>> + Send + 'static,
224        R: Serialize + Send + 'static,
225    {
226        self.delete_json_with_status(path, 200, handler)
227    }
228
229    pub fn delete_json_with_status<H, Fut, R>(
230        self,
231        path: impl Into<String>,
232        status: u16,
233        handler: H,
234    ) -> Result<Self>
235    where
236        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
237        Fut: Future<Output = Result<R>> + Send + 'static,
238        R: Serialize + Send + 'static,
239    {
240        self.route(RouteDefinition::delete_json_with_status(
241            path, status, handler,
242        )?)
243    }
244
245    pub fn options<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
246    where
247        H: RouteHandler,
248    {
249        self.route(RouteDefinition::options(path, handler)?)
250    }
251
252    pub fn head<H>(self, path: impl Into<String>, handler: H) -> Result<Self>
253    where
254        H: RouteHandler,
255    {
256        self.route(RouteDefinition::head(path, handler)?)
257    }
258
259    pub fn prefix(&self) -> &str {
260        &self.prefix
261    }
262
263    pub fn routes(&self) -> &[RouteDefinition] {
264        &self.routes
265    }
266
267    pub(crate) fn into_routes(self) -> Vec<RouteDefinition> {
268        self.routes
269    }
270}