Skip to main content

a3s_boot/routing/route/
builders.rs

1use super::definition::RouteDefinition;
2use crate::routing::handler::RouteHandler;
3use crate::{
4    BootRequest, BootResponse, HttpMethod, ModuleRef, OpenApiResponse, Result, SseEvent, Validate,
5    ViewRenderer,
6};
7use futures_core::Stream;
8use serde::de::DeserializeOwned;
9use serde::Serialize;
10use std::future::Future;
11
12impl RouteDefinition {
13    pub fn all<H>(path: impl Into<String>, handler: H) -> Result<Self>
14    where
15        H: RouteHandler,
16    {
17        Self::new(HttpMethod::All, path, handler)
18    }
19
20    pub fn all_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
21    where
22        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
23        H: RouteHandler,
24    {
25        Self::new_scoped(HttpMethod::All, path, factory)
26    }
27
28    pub fn all_json<H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
29    where
30        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
31        Fut: Future<Output = Result<R>> + Send + 'static,
32        R: Serialize + Send + 'static,
33    {
34        Self::all_json_with_status(path, 200, handler)
35    }
36
37    pub fn all_json_with_status<H, Fut, R>(
38        path: impl Into<String>,
39        status: u16,
40        handler: H,
41    ) -> Result<Self>
42    where
43        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
44        Fut: Future<Output = Result<R>> + Send + 'static,
45        R: Serialize + Send + 'static,
46    {
47        Self::json_response_with_status(HttpMethod::All, path, status, handler)
48    }
49
50    pub fn get<H>(path: impl Into<String>, handler: H) -> Result<Self>
51    where
52        H: RouteHandler,
53    {
54        Self::new(HttpMethod::Get, path, handler)
55    }
56
57    pub fn get_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
58    where
59        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
60        H: RouteHandler,
61    {
62        Self::new_scoped(HttpMethod::Get, path, factory)
63    }
64
65    pub fn get_json<H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
66    where
67        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
68        Fut: Future<Output = Result<R>> + Send + 'static,
69        R: Serialize + Send + 'static,
70    {
71        Self::get_json_with_status(path, 200, handler)
72    }
73
74    pub fn get_json_with_status<H, Fut, R>(
75        path: impl Into<String>,
76        status: u16,
77        handler: H,
78    ) -> Result<Self>
79    where
80        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
81        Fut: Future<Output = Result<R>> + Send + 'static,
82        R: Serialize + Send + 'static,
83    {
84        Self::json_response_with_status(HttpMethod::Get, path, status, handler)
85    }
86
87    pub fn get_view<H, Fut, R>(
88        path: impl Into<String>,
89        view: impl Into<String>,
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::get_view_with_status(path, view, 200, handler)
98    }
99
100    pub fn get_view_with_status<H, Fut, R>(
101        path: impl Into<String>,
102        view: impl Into<String>,
103        status: u16,
104        handler: H,
105    ) -> Result<Self>
106    where
107        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
108        Fut: Future<Output = Result<R>> + Send + 'static,
109        R: Serialize + Send + 'static,
110    {
111        Self::view_with_status(HttpMethod::Get, path, view, status, handler)
112    }
113
114    pub fn sse<H, Fut, S>(path: impl Into<String>, handler: H) -> Result<Self>
115    where
116        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
117        Fut: Future<Output = Result<S>> + Send + 'static,
118        S: Stream<Item = Result<SseEvent>> + Send + 'static,
119    {
120        Self::new(HttpMethod::Get, path, move |request: BootRequest| {
121            let future = request
122                .require_accepts_event_stream()
123                .map(|()| handler(request));
124            async move {
125                let stream = future?.await?;
126                Ok(BootResponse::sse(stream))
127            }
128        })
129    }
130
131    pub fn post<H>(path: impl Into<String>, handler: H) -> Result<Self>
132    where
133        H: RouteHandler,
134    {
135        Self::new(HttpMethod::Post, path, handler)
136    }
137
138    pub fn post_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
139    where
140        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
141        H: RouteHandler,
142    {
143        Self::new_scoped(HttpMethod::Post, path, factory)
144    }
145
146    pub fn post_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
147    where
148        T: DeserializeOwned + Send + 'static,
149        H: Fn(T) -> Fut + Send + Sync + 'static,
150        Fut: Future<Output = Result<R>> + Send + 'static,
151        R: Serialize + Send + 'static,
152    {
153        Self::post_json_with_status(path, 200, handler)
154    }
155
156    pub fn post_json_with_status<T, H, Fut, R>(
157        path: impl Into<String>,
158        status: u16,
159        handler: H,
160    ) -> Result<Self>
161    where
162        T: DeserializeOwned + Send + 'static,
163        H: Fn(T) -> Fut + Send + Sync + 'static,
164        Fut: Future<Output = Result<R>> + Send + 'static,
165        R: Serialize + Send + 'static,
166    {
167        Self::json_with_status(HttpMethod::Post, path, status, handler)
168    }
169
170    pub fn post_view<H, Fut, R>(
171        path: impl Into<String>,
172        view: impl Into<String>,
173        handler: H,
174    ) -> Result<Self>
175    where
176        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
177        Fut: Future<Output = Result<R>> + Send + 'static,
178        R: Serialize + Send + 'static,
179    {
180        Self::post_view_with_status(path, view, 200, handler)
181    }
182
183    pub fn post_view_with_status<H, Fut, R>(
184        path: impl Into<String>,
185        view: impl Into<String>,
186        status: u16,
187        handler: H,
188    ) -> Result<Self>
189    where
190        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
191        Fut: Future<Output = Result<R>> + Send + 'static,
192        R: Serialize + Send + 'static,
193    {
194        Self::view_with_status(HttpMethod::Post, path, view, status, handler)
195    }
196
197    pub fn post_validated_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
198    where
199        T: DeserializeOwned + Validate + Send + 'static,
200        H: Fn(T) -> Fut + Send + Sync + 'static,
201        Fut: Future<Output = Result<R>> + Send + 'static,
202        R: Serialize + Send + 'static,
203    {
204        Self::post_validated_json_with_status(path, 200, handler)
205    }
206
207    pub fn post_validated_json_with_status<T, H, Fut, R>(
208        path: impl Into<String>,
209        status: u16,
210        handler: H,
211    ) -> Result<Self>
212    where
213        T: DeserializeOwned + Validate + Send + 'static,
214        H: Fn(T) -> Fut + Send + Sync + 'static,
215        Fut: Future<Output = Result<R>> + Send + 'static,
216        R: Serialize + Send + 'static,
217    {
218        Self::validated_json_with_status(HttpMethod::Post, path, status, handler)
219    }
220
221    pub fn put<H>(path: impl Into<String>, handler: H) -> Result<Self>
222    where
223        H: RouteHandler,
224    {
225        Self::new(HttpMethod::Put, path, handler)
226    }
227
228    pub fn put_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
229    where
230        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
231        H: RouteHandler,
232    {
233        Self::new_scoped(HttpMethod::Put, path, factory)
234    }
235
236    pub fn put_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
237    where
238        T: DeserializeOwned + Send + 'static,
239        H: Fn(T) -> Fut + Send + Sync + 'static,
240        Fut: Future<Output = Result<R>> + Send + 'static,
241        R: Serialize + Send + 'static,
242    {
243        Self::put_json_with_status(path, 200, handler)
244    }
245
246    pub fn put_json_with_status<T, H, Fut, R>(
247        path: impl Into<String>,
248        status: u16,
249        handler: H,
250    ) -> Result<Self>
251    where
252        T: DeserializeOwned + Send + 'static,
253        H: Fn(T) -> Fut + Send + Sync + 'static,
254        Fut: Future<Output = Result<R>> + Send + 'static,
255        R: Serialize + Send + 'static,
256    {
257        Self::json_with_status(HttpMethod::Put, path, status, handler)
258    }
259
260    pub fn put_validated_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
261    where
262        T: DeserializeOwned + Validate + Send + 'static,
263        H: Fn(T) -> Fut + Send + Sync + 'static,
264        Fut: Future<Output = Result<R>> + Send + 'static,
265        R: Serialize + Send + 'static,
266    {
267        Self::put_validated_json_with_status(path, 200, handler)
268    }
269
270    pub fn put_validated_json_with_status<T, H, Fut, R>(
271        path: impl Into<String>,
272        status: u16,
273        handler: H,
274    ) -> Result<Self>
275    where
276        T: DeserializeOwned + Validate + Send + 'static,
277        H: Fn(T) -> Fut + Send + Sync + 'static,
278        Fut: Future<Output = Result<R>> + Send + 'static,
279        R: Serialize + Send + 'static,
280    {
281        Self::validated_json_with_status(HttpMethod::Put, path, status, handler)
282    }
283
284    pub fn patch<H>(path: impl Into<String>, handler: H) -> Result<Self>
285    where
286        H: RouteHandler,
287    {
288        Self::new(HttpMethod::Patch, path, handler)
289    }
290
291    pub fn patch_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
292    where
293        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
294        H: RouteHandler,
295    {
296        Self::new_scoped(HttpMethod::Patch, path, factory)
297    }
298
299    pub fn patch_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
300    where
301        T: DeserializeOwned + Send + 'static,
302        H: Fn(T) -> Fut + Send + Sync + 'static,
303        Fut: Future<Output = Result<R>> + Send + 'static,
304        R: Serialize + Send + 'static,
305    {
306        Self::patch_json_with_status(path, 200, handler)
307    }
308
309    pub fn patch_json_with_status<T, H, Fut, R>(
310        path: impl Into<String>,
311        status: u16,
312        handler: H,
313    ) -> Result<Self>
314    where
315        T: DeserializeOwned + Send + 'static,
316        H: Fn(T) -> Fut + Send + Sync + 'static,
317        Fut: Future<Output = Result<R>> + Send + 'static,
318        R: Serialize + Send + 'static,
319    {
320        Self::json_with_status(HttpMethod::Patch, path, status, handler)
321    }
322
323    pub fn patch_validated_json<T, H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
324    where
325        T: DeserializeOwned + Validate + Send + 'static,
326        H: Fn(T) -> Fut + Send + Sync + 'static,
327        Fut: Future<Output = Result<R>> + Send + 'static,
328        R: Serialize + Send + 'static,
329    {
330        Self::patch_validated_json_with_status(path, 200, handler)
331    }
332
333    pub fn patch_validated_json_with_status<T, H, Fut, R>(
334        path: impl Into<String>,
335        status: u16,
336        handler: H,
337    ) -> Result<Self>
338    where
339        T: DeserializeOwned + Validate + Send + 'static,
340        H: Fn(T) -> Fut + Send + Sync + 'static,
341        Fut: Future<Output = Result<R>> + Send + 'static,
342        R: Serialize + Send + 'static,
343    {
344        Self::validated_json_with_status(HttpMethod::Patch, path, status, handler)
345    }
346
347    pub fn delete<H>(path: impl Into<String>, handler: H) -> Result<Self>
348    where
349        H: RouteHandler,
350    {
351        Self::new(HttpMethod::Delete, path, handler)
352    }
353
354    pub fn delete_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
355    where
356        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
357        H: RouteHandler,
358    {
359        Self::new_scoped(HttpMethod::Delete, path, factory)
360    }
361
362    pub fn delete_json<H, Fut, R>(path: impl Into<String>, handler: H) -> Result<Self>
363    where
364        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
365        Fut: Future<Output = Result<R>> + Send + 'static,
366        R: Serialize + Send + 'static,
367    {
368        Self::delete_json_with_status(path, 200, handler)
369    }
370
371    pub fn delete_json_with_status<H, Fut, R>(
372        path: impl Into<String>,
373        status: u16,
374        handler: H,
375    ) -> Result<Self>
376    where
377        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
378        Fut: Future<Output = Result<R>> + Send + 'static,
379        R: Serialize + Send + 'static,
380    {
381        Self::json_response_with_status(HttpMethod::Delete, path, status, handler)
382    }
383
384    pub fn options<H>(path: impl Into<String>, handler: H) -> Result<Self>
385    where
386        H: RouteHandler,
387    {
388        Self::new(HttpMethod::Options, path, handler)
389    }
390
391    pub fn options_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
392    where
393        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
394        H: RouteHandler,
395    {
396        Self::new_scoped(HttpMethod::Options, path, factory)
397    }
398
399    pub fn head<H>(path: impl Into<String>, handler: H) -> Result<Self>
400    where
401        H: RouteHandler,
402    {
403        Self::new(HttpMethod::Head, path, handler)
404    }
405
406    pub fn head_scoped<F, H>(path: impl Into<String>, factory: F) -> Result<Self>
407    where
408        F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
409        H: RouteHandler,
410    {
411        Self::new_scoped(HttpMethod::Head, path, factory)
412    }
413
414    fn json_with_status<T, H, Fut, R>(
415        method: HttpMethod,
416        path: impl Into<String>,
417        status: u16,
418        handler: H,
419    ) -> Result<Self>
420    where
421        T: DeserializeOwned + Send + 'static,
422        H: Fn(T) -> Fut + Send + Sync + 'static,
423        Fut: Future<Output = Result<R>> + Send + 'static,
424        R: Serialize + Send + 'static,
425    {
426        Self::new(method, path, move |request: BootRequest| {
427            let future = request
428                .require_json_content_type()
429                .and_then(|()| request.require_accepts_json())
430                .and_then(|()| request.json::<T>())
431                .map(&handler);
432            async move {
433                let body = future?.await?;
434                BootResponse::json_with_status(status, &body)
435            }
436        })
437        .map(|route| route.with_response(status, OpenApiResponse::description("Success")))
438    }
439
440    fn json_response_with_status<H, Fut, R>(
441        method: HttpMethod,
442        path: impl Into<String>,
443        status: u16,
444        handler: H,
445    ) -> Result<Self>
446    where
447        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
448        Fut: Future<Output = Result<R>> + Send + 'static,
449        R: Serialize + Send + 'static,
450    {
451        Self::new(method, path, move |request: BootRequest| {
452            let future = request.require_accepts_json().map(|()| handler(request));
453            async move {
454                let body = future?.await?;
455                BootResponse::json_with_status(status, &body)
456            }
457        })
458        .map(|route| route.with_response(status, OpenApiResponse::description("Success")))
459    }
460
461    pub fn view<H, Fut, R>(
462        method: HttpMethod,
463        path: impl Into<String>,
464        view: impl Into<String>,
465        handler: H,
466    ) -> Result<Self>
467    where
468        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
469        Fut: Future<Output = Result<R>> + Send + 'static,
470        R: Serialize + Send + 'static,
471    {
472        Self::view_with_status(method, path, view, 200, handler)
473    }
474
475    pub fn view_with_status<H, Fut, R>(
476        method: HttpMethod,
477        path: impl Into<String>,
478        view: impl Into<String>,
479        status: u16,
480        handler: H,
481    ) -> Result<Self>
482    where
483        H: Fn(BootRequest) -> Fut + Send + Sync + 'static,
484        Fut: Future<Output = Result<R>> + Send + 'static,
485        R: Serialize + Send + 'static,
486    {
487        let view = view.into();
488        Self::new(method, path, move |request: BootRequest| {
489            let view = view.clone();
490            let future = request
491                .get::<ViewRenderer>()
492                .map(|renderer| (renderer, handler(request)));
493            async move {
494                let (renderer, future) = future?;
495                let context = future.await?;
496                let context = serde_json::to_value(&context).map_err(|error| {
497                    crate::BootError::Internal(format!(
498                        "failed to serialize view context `{view}`: {error}"
499                    ))
500                })?;
501                renderer
502                    .render_value_response_with_status(status, view, context)
503                    .await
504            }
505        })
506        .map(|route| route.with_response(status, OpenApiResponse::description("Success")))
507    }
508
509    fn validated_json_with_status<T, H, Fut, R>(
510        method: HttpMethod,
511        path: impl Into<String>,
512        status: u16,
513        handler: H,
514    ) -> Result<Self>
515    where
516        T: DeserializeOwned + Validate + Send + 'static,
517        H: Fn(T) -> Fut + Send + Sync + 'static,
518        Fut: Future<Output = Result<R>> + Send + 'static,
519        R: Serialize + Send + 'static,
520    {
521        Self::json_with_status(method, path, status, handler)
522            .map(|route| route.with_body_validation::<T>().with_validation())
523    }
524}