elif_http/handlers/
handler.rs

1//! Handler abstraction for elif.rs framework
2//!
3//! This module provides a bridge between elif types and Axum's handler system.
4
5use 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
15/// Trait for elif handlers that work with ElifRequest/ElifResponse
16pub 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
23/// Implement ElifHandler for functions that take ElifRequest
24impl<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
38/// Wrapper struct that implements the Handler trait
39pub 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            // Convert Axum request to ElifRequest
73            let (parts, body) = req.into_parts();
74
75            // Extract body bytes
76            let body_bytes = (axum::body::to_bytes(body, usize::MAX).await).ok();
77
78            // Extract query parameters from URI
79            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
107/// Convert elif handler to Axum handler for any state
108pub 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
117/// Convert ElifResponse to Axum Response
118fn convert_elif_to_axum_response(elif_response: ElifResponse) -> AxumResponse {
119    // ElifResponse already implements IntoResponse for Axum
120    use axum::response::IntoResponse as AxumIntoResponse;
121    AxumIntoResponse::into_response(elif_response)
122}
123
124/// Macro to create elif handlers more easily
125#[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        // This test verifies the handler compiles and can be used
145        // Full integration testing would require setting up Axum routing
146        assert!(true);
147    }
148}