http_endpoint_server_harness/entities/
handler.rs

1use super::{Request, Response};
2use std::sync::Arc;
3
4/// Type alias for dynamic handler functions
5pub type HandlerFn = Arc<dyn Fn(&Request) -> Response + Send + Sync>;
6
7/// A handler that returns either a static or dynamic HTTP response
8#[derive(Clone)]
9pub enum Handler {
10    /// Static response - always returns the same response
11    Static(Response),
12    /// Dynamic response - builds response based on the request
13    Dynamic(HandlerFn),
14}
15
16impl std::fmt::Debug for Handler {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Handler::Static(response) => f.debug_tuple("Static").field(response).finish(),
20            Handler::Dynamic(_) => f.debug_tuple("Dynamic").field(&"<fn>").finish(),
21        }
22    }
23}
24
25impl Handler {
26    /// Create a new static handler with a predefined response
27    pub fn new(response: Response) -> Self {
28        Handler::Static(response)
29    }
30
31    /// Create a dynamic handler that builds responses based on the request
32    pub fn dynamic<F>(f: F) -> Self
33    where
34        F: Fn(&Request) -> Response + Send + Sync + 'static,
35    {
36        Handler::Dynamic(Arc::new(f))
37    }
38
39    /// Create a static handler from a JSON value
40    pub fn from_json<T: serde::Serialize>(value: &T) -> Self {
41        Handler::Static(Response::ok().with_json(value))
42    }
43
44    /// Modify the status code (only works for static handlers, returns a new static handler)
45    pub fn with_status(self, status: u16) -> Self {
46        match self {
47            Handler::Static(mut response) => {
48                response.status = status;
49                Handler::Static(response)
50            }
51            Handler::Dynamic(_) => self, // Cannot modify dynamic handler
52        }
53    }
54
55    /// Add a header (only works for static handlers)
56    pub fn with_header(self, key: impl Into<String>, value: impl Into<String>) -> Self {
57        match self {
58            Handler::Static(response) => Handler::Static(response.with_header(key, value)),
59            Handler::Dynamic(_) => self, // Cannot modify dynamic handler
60        }
61    }
62
63    /// Get the response for a given request
64    pub fn respond(&self, request: &Request) -> Response {
65        match self {
66            Handler::Static(response) => response.clone(),
67            Handler::Dynamic(f) => f(request),
68        }
69    }
70
71    /// Get the static response (for backwards compatibility)
72    /// Returns a default response for dynamic handlers
73    pub fn response(&self) -> Response {
74        match self {
75            Handler::Static(response) => response.clone(),
76            Handler::Dynamic(_) => Response::new(200),
77        }
78    }
79}
80
81impl From<Response> for Handler {
82    fn from(response: Response) -> Self {
83        Handler::Static(response)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::entities::Method;
91    use std::collections::HashMap;
92
93    fn create_test_request(method: Method, path: &str, body: &[u8]) -> Request {
94        Request {
95            method,
96            path: path.to_string(),
97            headers: HashMap::new(),
98            body: body.to_vec(),
99        }
100    }
101
102    #[test]
103    fn test_handler_new() {
104        let response = Response::new(200);
105        let handler = Handler::new(response.clone());
106        assert!(matches!(handler, Handler::Static(_)));
107    }
108
109    #[test]
110    fn test_handler_from_json() {
111        let handler = Handler::from_json(&serde_json::json!({"test": true}));
112        let req = create_test_request(Method::Get, "/", &[]);
113        let response = handler.respond(&req);
114        assert_eq!(response.status, 200);
115        assert!(response.headers.get("content-type").unwrap().contains("application/json"));
116    }
117
118    #[test]
119    fn test_handler_with_status() {
120        let handler = Handler::from_json(&serde_json::json!({})).with_status(201);
121        let req = create_test_request(Method::Get, "/", &[]);
122        assert_eq!(handler.respond(&req).status, 201);
123    }
124
125    #[test]
126    fn test_handler_from_response() {
127        let response = Response::new(404);
128        let handler: Handler = response.into();
129        let req = create_test_request(Method::Get, "/", &[]);
130        assert_eq!(handler.respond(&req).status, 404);
131    }
132
133    #[test]
134    fn test_dynamic_handler() {
135        let handler = Handler::dynamic(|req: &Request| {
136            let body = format!("You requested: {}", req.path);
137            Response::new(200).with_body(body)
138        });
139
140        let req = create_test_request(Method::Get, "/api/users", &[]);
141        let response = handler.respond(&req);
142        assert_eq!(response.status, 200);
143        assert!(String::from_utf8_lossy(&response.body).contains("/api/users"));
144    }
145
146    #[test]
147    fn test_dynamic_handler_with_body() {
148        let handler = Handler::dynamic(|req: &Request| {
149            if let Some(body_str) = req.body_as_str() {
150                if let Ok(json) = serde_json::from_str::<serde_json::Value>(body_str) {
151                    if let Some(name) = json.get("name").and_then(|v| v.as_str()) {
152                        return Response::new(200).with_json(&serde_json::json!({
153                            "message": format!("Hello, {}!", name)
154                        }));
155                    }
156                }
157            }
158            Response::new(400).with_body("Invalid request")
159        });
160
161        let req = create_test_request(Method::Post, "/greet", b"{\"name\": \"World\"}");
162        let response = handler.respond(&req);
163        assert_eq!(response.status, 200);
164        assert!(String::from_utf8_lossy(&response.body).contains("Hello, World!"));
165    }
166
167    #[test]
168    fn test_dynamic_handler_based_on_method() {
169        let handler = Handler::dynamic(|req: &Request| {
170            match req.method {
171                Method::Get => Response::new(200).with_body("GET response"),
172                Method::Post => Response::new(201).with_body("POST response"),
173                _ => Response::new(405).with_body("Method not allowed"),
174            }
175        });
176
177        let get_req = create_test_request(Method::Get, "/", &[]);
178        assert_eq!(handler.respond(&get_req).status, 200);
179
180        let post_req = create_test_request(Method::Post, "/", &[]);
181        assert_eq!(handler.respond(&post_req).status, 201);
182
183        let delete_req = create_test_request(Method::Delete, "/", &[]);
184        assert_eq!(handler.respond(&delete_req).status, 405);
185    }
186}
187