1use crate::body::Body;
16use crate::extract::Request;
17use crate::handler::Handler;
18use crate::middleware::BoxService;
19use crate::response::{Response, SUCCESS};
20use http::{Method, StatusCode};
21use siderite_openapi::{HttpMethod, Operation, Schema, SchemaRegistry};
22use std::convert::Infallible;
23use std::future::Future;
24use std::pin::Pin;
25use std::sync::Arc;
26use tower::{Layer, Service, ServiceExt};
27
28type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
29
30pub(crate) type BoxedHandler = Arc<dyn Fn(Request) -> BoxFuture<Response> + Send + Sync>;
32
33pub(crate) type DescribeFn = fn(&mut Operation, &mut SchemaRegistry);
35
36#[derive(Debug, Clone, Default)]
38pub struct OperationMeta {
39 pub summary: Option<String>,
41 pub description: Option<String>,
43 pub tags: Vec<String>,
45 pub operation_id: Option<String>,
47 pub deprecated: bool,
49 pub status: Option<StatusCode>,
51 pub hidden: bool,
53 pub(crate) response_model: Option<DescribeFn>,
54}
55
56impl OperationMeta {
57 pub(crate) fn apply(&self, op: &mut Operation, registry: &mut SchemaRegistry) {
59 if let Some(model) = self.response_model {
60 model(op, registry);
61 }
62 if let Some(status) = self.status
63 && status.as_u16() != 200
64 {
65 op.remap_response(SUCCESS, status.as_str());
66 }
67 op.summary.clone_from(&self.summary);
68 op.description.clone_from(&self.description);
69 op.tags.clone_from(&self.tags);
70 op.operation_id.clone_from(&self.operation_id);
71 op.deprecated = self.deprecated;
72 }
73}
74
75#[derive(Clone)]
77pub(crate) struct Endpoint {
78 pub(crate) method: Method,
79 pub(crate) handler: BoxedHandler,
80 pub(crate) describe: DescribeFn,
81 pub(crate) meta: OperationMeta,
82 pub(crate) background_tasks: bool,
83}
84
85impl std::fmt::Debug for Endpoint {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("Endpoint")
88 .field("method", &self.method)
89 .field("meta", &self.meta)
90 .finish_non_exhaustive()
91 }
92}
93
94impl Endpoint {
95 fn new<H: Handler<T>, T: 'static>(method: Method, handler: H) -> Self {
96 let boxed: BoxedHandler = Arc::new(move |req| Box::pin(handler.clone().call(req)));
97 Self {
98 method,
99 handler: boxed,
100 describe: H::describe,
101 meta: OperationMeta::default(),
102 background_tasks: H::BACKGROUND_TASKS,
103 }
104 }
105
106 pub(crate) fn operation(&self, registry: &mut SchemaRegistry) -> Operation {
108 let mut op = Operation::default();
109 (self.describe)(&mut op, registry);
110 self.meta.apply(&mut op, registry);
111 op
112 }
113
114 pub(crate) fn openapi_method(&self) -> Option<HttpMethod> {
116 Some(match self.method {
117 Method::GET => HttpMethod::Get,
118 Method::POST => HttpMethod::Post,
119 Method::PUT => HttpMethod::Put,
120 Method::PATCH => HttpMethod::Patch,
121 Method::DELETE => HttpMethod::Delete,
122 Method::HEAD => HttpMethod::Head,
123 Method::OPTIONS => HttpMethod::Options,
124 Method::TRACE => HttpMethod::Trace,
125 _ => return None,
126 })
127 }
128
129 pub(crate) fn into_axum(self) -> Option<axum::routing::MethodRouter> {
131 use axum::routing::MethodFilter;
132 let filter = match self.method {
133 Method::GET => MethodFilter::GET,
134 Method::POST => MethodFilter::POST,
135 Method::PUT => MethodFilter::PUT,
136 Method::PATCH => MethodFilter::PATCH,
137 Method::DELETE => MethodFilter::DELETE,
138 Method::HEAD => MethodFilter::HEAD,
139 Method::OPTIONS => MethodFilter::OPTIONS,
140 Method::TRACE => MethodFilter::TRACE,
141 _ => return None,
142 };
143 let handler = self.handler;
144 let status = self.meta.status;
145 let background = self.background_tasks;
146 Some(axum::routing::on(
147 filter,
148 move |req: axum::extract::Request| {
149 let handler = Arc::clone(&handler);
150 async move {
151 let mut req = req.map(Body::from_inner);
152 let guard = background
153 .then(|| crate::background::RequestGuard::install(req.extensions_mut()));
154 let mut response = handler(req).await;
155 if let Some(status) = status
156 && response.status() == StatusCode::OK
157 {
158 *response.status_mut() = status;
159 }
160 if let Some(guard) = guard {
161 guard.finish(response.status());
162 }
163 response.map(Body::into_inner)
164 }
165 },
166 ))
167 }
168}
169
170#[derive(Debug, Clone, Default)]
172pub struct MethodRouter {
173 pub(crate) endpoints: Vec<Endpoint>,
174}
175
176macro_rules! method_routes {
177 ($($name:ident => $method:ident),+ $(,)?) => {
178 $(
179 #[doc = concat!("Route `", stringify!($method), "` requests to `handler`.")]
180 pub fn $name<H: Handler<T>, T: 'static>(handler: H) -> MethodRouter {
181 MethodRouter::default().$name(handler)
182 }
183 )+
184
185 impl MethodRouter {
186 $(
187 #[doc = concat!("Additionally route `", stringify!($method), "` requests to `handler`.")]
188 #[must_use]
189 pub fn $name<H: Handler<T>, T: 'static>(self, handler: H) -> Self {
190 self.on(Method::$method, handler)
191 }
192 )+
193 }
194 };
195}
196
197method_routes! {
198 get => GET,
199 post => POST,
200 put => PUT,
201 patch => PATCH,
202 delete => DELETE,
203 head => HEAD,
204 options => OPTIONS,
205}
206
207impl MethodRouter {
208 #[must_use]
211 pub fn on<H: Handler<T>, T: 'static>(mut self, method: Method, handler: H) -> Self {
212 self.endpoints.push(Endpoint::new(method, handler));
213 self
214 }
215
216 fn last(mut self, f: impl FnOnce(&mut OperationMeta)) -> Self {
217 if let Some(e) = self.endpoints.last_mut() {
218 f(&mut e.meta);
219 }
220 self
221 }
222
223 #[must_use]
229 pub fn layer<L>(mut self, layer: L) -> Self
230 where
231 L: Layer<BoxService>,
232 L::Service: Service<Request, Response = Response, Error = Infallible>
233 + Clone
234 + Send
235 + Sync
236 + 'static,
237 <L::Service as Service<Request>>::Future: Send + 'static,
238 {
239 for endpoint in &mut self.endpoints {
240 let handler = Arc::clone(&endpoint.handler);
241 let inner = BoxService::new(tower::service_fn(move |req: Request| {
242 let fut = handler(req);
243 async move { Ok::<_, Infallible>(fut.await) }
244 }));
245 let service = layer.layer(inner);
246 endpoint.handler = Arc::new(move |req| {
247 let service = service.clone();
248 Box::pin(async move {
249 match service.oneshot(req).await {
250 Ok(response) => response,
251 Err(never) => match never {},
252 }
253 })
254 });
255 }
256 self
257 }
258
259 #[must_use]
261 pub fn summary(self, summary: impl Into<String>) -> Self {
262 let summary = summary.into();
263 self.last(|m| m.summary = Some(summary))
264 }
265
266 #[must_use]
268 pub fn description(self, description: impl Into<String>) -> Self {
269 let description = description.into();
270 self.last(|m| m.description = Some(description))
271 }
272
273 #[must_use]
275 pub fn tag(self, tag: impl Into<String>) -> Self {
276 let tag = tag.into();
277 self.last(|m| m.tags.push(tag))
278 }
279
280 #[must_use]
282 pub fn operation_id(self, id: impl Into<String>) -> Self {
283 let id = id.into();
284 self.last(|m| m.operation_id = Some(id))
285 }
286
287 #[must_use]
289 pub fn deprecated(self) -> Self {
290 self.last(|m| m.deprecated = true)
291 }
292
293 #[must_use]
295 pub fn hidden(self) -> Self {
296 self.last(|m| m.hidden = true)
297 }
298
299 #[must_use]
302 pub fn status(self, status: StatusCode) -> Self {
303 self.last(|m| m.status = Some(status))
304 }
305
306 #[must_use]
309 pub fn response_model<T: Schema + 'static>(self) -> Self {
310 fn describe<T: Schema + 'static>(op: &mut Operation, registry: &mut SchemaRegistry) {
311 let schema = registry.subschema::<T>();
312 op.add_response(
313 SUCCESS,
314 "Successful Response",
315 Some(("application/json", schema)),
316 );
317 }
318 self.last(|m| m.response_model = Some(describe::<T>))
319 }
320}
321
322#[derive(Debug, Clone)]
324pub struct Route {
325 pub path: &'static str,
327 pub router: MethodRouter,
329}
330
331impl Route {
332 pub fn new(path: &'static str, router: MethodRouter) -> Self {
334 Self { path, router }
335 }
336}