elif_http/handlers/
handler.rs1use crate::errors::HttpResult;
6use crate::request::ElifRequest;
7use crate::response::{ElifResponse, IntoElifResponse};
8use axum::{
9 extract::Request as AxumRequest, handler::Handler as AxumHandler,
10 response::Response as AxumResponse,
11};
12use std::collections::HashMap;
13use std::future::Future;
14
15pub trait ElifHandler<T> {
17 type Output: IntoElifResponse + Send;
18 type Future: Future<Output = HttpResult<Self::Output>> + Send;
19
20 fn call(self, request: ElifRequest) -> Self::Future;
21}
22
23impl<F, Fut, R> ElifHandler<(ElifRequest,)> for F
25where
26 F: FnOnce(ElifRequest) -> Fut + Send,
27 Fut: Future<Output = HttpResult<R>> + Send,
28 R: IntoElifResponse + Send,
29{
30 type Output = R;
31 type Future = Fut;
32
33 fn call(self, request: ElifRequest) -> Self::Future {
34 self(request)
35 }
36}
37
38pub struct ElifHandlerWrapper<F, Fut, R>
40where
41 F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
42 Fut: Future<Output = HttpResult<R>> + Send + 'static,
43 R: IntoElifResponse + Send + 'static,
44{
45 handler: F,
46}
47
48impl<F, Fut, R> Clone for ElifHandlerWrapper<F, Fut, R>
49where
50 F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
51 Fut: Future<Output = HttpResult<R>> + Send + 'static,
52 R: IntoElifResponse + Send + 'static,
53{
54 fn clone(&self) -> Self {
55 Self {
56 handler: self.handler.clone(),
57 }
58 }
59}
60
61impl<F, Fut, R, S> AxumHandler<(), S> for ElifHandlerWrapper<F, Fut, R>
62where
63 F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
64 Fut: Future<Output = HttpResult<R>> + Send + 'static,
65 R: IntoElifResponse + Send + 'static,
66 S: Send + Sync + 'static,
67{
68 type Future = std::pin::Pin<Box<dyn Future<Output = AxumResponse> + Send>>;
69
70 fn call(self, req: AxumRequest, _state: S) -> Self::Future {
71 Box::pin(async move {
72 let (parts, body) = req.into_parts();
74
75 let body_bytes = (axum::body::to_bytes(body, usize::MAX).await).ok();
77
78 let query_params = if let Some(query) = parts.uri.query() {
80 serde_urlencoded::from_str::<HashMap<String, String>>(query).unwrap_or_default()
81 } else {
82 HashMap::new()
83 };
84
85 let elif_request = ElifRequest::extract_elif_request(
86 crate::request::ElifMethod::from_axum(parts.method),
87 parts.uri,
88 crate::response::ElifHeaderMap::from_axum(parts.headers),
89 body_bytes,
90 )
91 .with_query_params(query_params);
92
93 match (self.handler)(elif_request).await {
94 Ok(response) => {
95 let elif_response = response.into_response();
96 convert_elif_to_axum_response(elif_response)
97 }
98 Err(error) => {
99 let error_response = crate::response::IntoElifResponse::into_response(error);
100 convert_elif_to_axum_response(error_response)
101 }
102 }
103 })
104 }
105}
106
107pub fn elif_handler<F, Fut, R>(handler: F) -> ElifHandlerWrapper<F, Fut, R>
109where
110 F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
111 Fut: Future<Output = HttpResult<R>> + Send + 'static,
112 R: IntoElifResponse + Send + 'static,
113{
114 ElifHandlerWrapper { handler }
115}
116
117fn convert_elif_to_axum_response(elif_response: ElifResponse) -> AxumResponse {
119 use axum::response::IntoResponse as AxumIntoResponse;
121 AxumIntoResponse::into_response(elif_response)
122}
123
124#[macro_export]
126macro_rules! elif_route {
127 ($handler:expr) => {
128 $crate::handlers::elif_handler($handler)
129 };
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 async fn test_handler(_req: ElifRequest) -> HttpResult<ElifResponse> {
137 Ok(ElifResponse::ok().text("Hello, World!"))
138 }
139
140 #[test]
141 fn test_elif_handler_conversion() {
142 let _handler = elif_handler(test_handler);
143
144 assert!(true);
147 }
148}