Skip to main content

ullun/
response.rs

1//! Response types and builders
2
3use crate::error::Error;
4use bytes::Bytes;
5use http::StatusCode;
6use serde::Serialize;
7
8/// HTTP Response wrapper
9pub struct Response {
10    pub status: StatusCode,
11    pub headers: http::HeaderMap,
12    pub body: Bytes,
13}
14
15impl Response {
16    pub fn new(status: StatusCode) -> Self {
17        Self {
18            status,
19            headers: http::HeaderMap::new(),
20            body: Bytes::new(),
21        }
22    }
23
24    pub fn ok() -> Self {
25        Self::new(StatusCode::OK)
26    }
27
28    pub fn with_body(mut self, body: impl Into<Bytes>) -> Self {
29        self.body = body.into();
30        self
31    }
32
33    pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
34        let key = key.into();
35        let value = value.into();
36
37        // Validate and insert header, skip invalid ones
38        if let (Ok(header_name), Ok(header_value)) = (
39            http::HeaderName::from_bytes(key.as_bytes()),
40            http::HeaderValue::from_str(&value),
41        ) {
42            self.headers.insert(header_name, header_value);
43        } else {
44            eprintln!("Warning: Invalid header {}={}", key, value);
45        }
46        self
47    }
48
49    pub fn json<T: Serialize>(data: T) -> Self {
50        match serde_json::to_vec(&data) {
51            Ok(body) => Self::ok()
52                .with_header("content-type", "application/json")
53                .with_body(body),
54            Err(e) => {
55                eprintln!("JSON serialization error: {}", e);
56                Self::new(StatusCode::INTERNAL_SERVER_ERROR)
57                    .with_header("content-type", "application/json")
58                    .with_body("{\"error\":\"Internal serialization error\"}")
59            }
60        }
61    }
62
63    pub fn text(text: impl Into<String>) -> Self {
64        Self::ok()
65            .with_header("content-type", "text/plain")
66            .with_body(text.into())
67    }
68
69    pub fn from_error(error: Error) -> Self {
70        let status =
71            StatusCode::from_u16(error.status_code()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
72        let body = serde_json::json!({
73            "error": error.message(),
74            "status": error.status_code(),
75        });
76        let body_bytes =
77            serde_json::to_vec(&body).unwrap_or_else(|_| b"{\"error\":\"Unknown error\"}".to_vec());
78        Self::new(status)
79            .with_header("content-type", "application/json")
80            .with_body(body_bytes)
81    }
82
83    pub(crate) fn into_hyper(self) -> hyper::Response<http_body_util::Full<Bytes>> {
84        let mut builder = hyper::Response::builder().status(self.status);
85
86        for (key, value) in self.headers.iter() {
87            builder = builder.header(key, value);
88        }
89
90        builder
91            .body(http_body_util::Full::new(self.body))
92            .unwrap_or_else(|e| {
93                eprintln!("Failed to build response: {}", e);
94                hyper::Response::builder()
95                    .status(StatusCode::INTERNAL_SERVER_ERROR)
96                    .body(http_body_util::Full::new(Bytes::from(
97                        "Internal Server Error",
98                    )))
99                    .unwrap()
100            })
101    }
102}
103
104/// JSON response helper
105pub struct Json<T>(pub T);
106
107impl<T: Serialize> From<Json<T>> for Response {
108    fn from(json: Json<T>) -> Self {
109        Response::json(json.0)
110    }
111}