Skip to main content

ullun/
response.rs

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