1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::convert::Infallible;
use axum::{extract::Request, response::IntoResponse, routing::Route};
use tower::{Layer, Service};
use super::describe;
use crate::app::AppContext;
#[derive(Clone, Default)]
pub struct Routes {
pub prefix: Option<String>,
pub handlers: Vec<Handler>,
// pub version: Option<String>,
}
#[derive(Clone, Default)]
pub struct Handler {
pub uri: String,
pub method: axum::routing::MethodRouter<AppContext>,
pub actions: Vec<axum::http::Method>,
}
impl Routes {
/// Creates a new [`Routes`] instance with default settings.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Set a prefix for the routes. this prefix will be a prefix for all the
/// routes.
///
/// # Example
///
/// In the following example the we are adding `status` as a prefix to the
/// _ping endpoint HOST/status/_ping.
///
/// ```rust
/// use loco_rs::prelude::*;
/// use serde::Serialize;;
///
/// #[derive(Serialize)]
/// struct Health {
/// pub ok: bool,
/// }
///
/// async fn ping() -> Result<Response> {
/// format::json(Health { ok: true })
/// }
/// Routes::at("status").add("/_ping", get(ping));
///
/// ````
#[must_use]
pub fn at(prefix: &str) -> Self {
Self {
prefix: Some(prefix.to_string()),
..Self::default()
}
}
/// Adding new router
///
/// # Example
///
/// This example preset how to add a get endpoint int the Router.
///
/// ```rust
/// use loco_rs::prelude::*;
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct Health {
/// pub ok: bool,
/// }
///
/// async fn ping() -> Result<Response> {
/// format::json(Health { ok: true })
/// }
/// Routes::new().add("/_ping", get(ping));
/// ````
#[must_use]
pub fn add(mut self, uri: &str, method: axum::routing::MethodRouter<AppContext>) -> Self {
describe::method_action(&method);
self.handlers.push(Handler {
uri: uri.to_owned(),
actions: describe::method_action(&method),
method,
});
self
}
/// Set a prefix for the routes. this prefix will be a prefix for all the
/// routes.
///
/// # Example
///
/// In the following example the we are adding `status` as a prefix to the
/// _ping endpoint HOST/status/_ping.
///
/// ```rust
/// use loco_rs::prelude::*;
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct Health {
/// pub ok: bool,
/// }
///
/// async fn ping() -> Result<Response> {
/// format::json(Health { ok: true })
/// }
/// Routes::new().prefix("status").add("/_ping", get(ping));
/// ````
#[must_use]
pub fn prefix(mut self, uri: &str) -> Self {
self.prefix = Some(uri.to_owned());
self
}
/// Set a layer for the routes. this layer will be a layer for all the
/// routes.
///
/// # Example
///
/// In the following example, we are adding a layer to the routes.
///
/// ```rust
/// use loco_rs::prelude::*;
/// use tower::{Layer, Service};
/// use tower_http::timeout::TimeoutLayer;
/// async fn ping() -> Result<Response> {
/// format::json("Ok")
/// }
/// Routes::new().prefix("status").add("/_ping", get(ping)).layer(TimeoutLayer::new(std::time::Duration::from_secs(5)));
/// ```
#[allow(clippy::needless_pass_by_value)]
#[must_use]
pub fn layer<L>(self, layer: L) -> Self
where
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request> + Clone + Send + 'static,
<L::Service as Service<Request>>::Response: IntoResponse + 'static,
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request>>::Future: Send + 'static,
{
Self {
prefix: self.prefix,
handlers: self
.handlers
.iter()
.map(|handler| Handler {
uri: handler.uri.clone(),
actions: handler.actions.clone(),
method: handler.method.clone().layer(layer.clone()),
})
.collect(),
}
}
}