use crate::errors::HttpResult;
use crate::request::ElifRequest;
use crate::response::{ElifResponse, IntoElifResponse};
use axum::{
extract::Request as AxumRequest, handler::Handler as AxumHandler,
response::Response as AxumResponse,
};
use std::collections::HashMap;
use std::future::Future;
pub trait ElifHandler<T> {
type Output: IntoElifResponse + Send;
type Future: Future<Output = HttpResult<Self::Output>> + Send;
fn call(self, request: ElifRequest) -> Self::Future;
}
impl<F, Fut, R> ElifHandler<(ElifRequest,)> for F
where
F: FnOnce(ElifRequest) -> Fut + Send,
Fut: Future<Output = HttpResult<R>> + Send,
R: IntoElifResponse + Send,
{
type Output = R;
type Future = Fut;
fn call(self, request: ElifRequest) -> Self::Future {
self(request)
}
}
pub struct ElifHandlerWrapper<F, Fut, R>
where
F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
Fut: Future<Output = HttpResult<R>> + Send + 'static,
R: IntoElifResponse + Send + 'static,
{
handler: F,
}
impl<F, Fut, R> Clone for ElifHandlerWrapper<F, Fut, R>
where
F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
Fut: Future<Output = HttpResult<R>> + Send + 'static,
R: IntoElifResponse + Send + 'static,
{
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
}
}
}
impl<F, Fut, R, S> AxumHandler<(), S> for ElifHandlerWrapper<F, Fut, R>
where
F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
Fut: Future<Output = HttpResult<R>> + Send + 'static,
R: IntoElifResponse + Send + 'static,
S: Send + Sync + 'static,
{
type Future = std::pin::Pin<Box<dyn Future<Output = AxumResponse> + Send>>;
fn call(self, req: AxumRequest, _state: S) -> Self::Future {
Box::pin(async move {
let (parts, body) = req.into_parts();
let body_bytes = (axum::body::to_bytes(body, usize::MAX).await).ok();
let query_params = if let Some(query) = parts.uri.query() {
serde_urlencoded::from_str::<HashMap<String, String>>(query).unwrap_or_default()
} else {
HashMap::new()
};
let elif_request = ElifRequest::extract_elif_request(
crate::request::ElifMethod::from_axum(parts.method),
parts.uri,
crate::response::ElifHeaderMap::from_axum(parts.headers),
body_bytes,
)
.with_query_params(query_params);
match (self.handler)(elif_request).await {
Ok(response) => {
let elif_response = response.into_response();
convert_elif_to_axum_response(elif_response)
}
Err(error) => {
let error_response = crate::response::IntoElifResponse::into_response(error);
convert_elif_to_axum_response(error_response)
}
}
})
}
}
pub fn elif_handler<F, Fut, R>(handler: F) -> ElifHandlerWrapper<F, Fut, R>
where
F: Fn(ElifRequest) -> Fut + Send + Clone + 'static,
Fut: Future<Output = HttpResult<R>> + Send + 'static,
R: IntoElifResponse + Send + 'static,
{
ElifHandlerWrapper { handler }
}
fn convert_elif_to_axum_response(elif_response: ElifResponse) -> AxumResponse {
use axum::response::IntoResponse as AxumIntoResponse;
AxumIntoResponse::into_response(elif_response)
}
#[macro_export]
macro_rules! elif_route {
($handler:expr) => {
$crate::handlers::elif_handler($handler)
};
}
#[cfg(test)]
mod tests {
use super::*;
async fn test_handler(_req: ElifRequest) -> HttpResult<ElifResponse> {
Ok(ElifResponse::ok().text("Hello, World!"))
}
#[test]
fn test_elif_handler_conversion() {
let _handler = elif_handler(test_handler);
assert!(true);
}
}