Skip to main content

omnia_sdk/api/
reply.rs

1use std::fmt::Debug;
2use std::ops::Deref;
3
4use http::{HeaderMap, StatusCode};
5
6use crate::api::Body;
7
8/// Top-level response data structure common to all handlers.
9#[derive(Debug)]
10pub struct Reply<B>
11where
12    B: Body,
13{
14    /// HTTP status code.
15    pub status: StatusCode,
16
17    /// HTTP headers, if any.
18    pub headers: HeaderMap,
19
20    /// Response body.
21    pub body: B,
22}
23
24impl<B: Body> Reply<B> {
25    /// Create a success response
26    #[must_use]
27    pub fn ok(body: B) -> Self {
28        Self {
29            status: StatusCode::OK,
30            headers: HeaderMap::new(),
31            body,
32        }
33    }
34
35    /// Create a created response (201)
36    #[must_use]
37    pub fn created(body: B) -> Self {
38        Self {
39            status: StatusCode::CREATED,
40            headers: HeaderMap::new(),
41            body,
42        }
43    }
44
45    /// Create an accepted response (202)
46    #[must_use]
47    pub fn accepted(body: B) -> Self {
48        Self {
49            status: StatusCode::ACCEPTED,
50            headers: HeaderMap::new(),
51            body,
52        }
53    }
54
55    /// Check if response is successful (2xx)
56    #[must_use]
57    pub fn is_success(&self) -> bool {
58        self.status.is_success()
59    }
60
61    /// Create a success response with a specific status code.
62    #[must_use]
63    pub const fn status(mut self, status: StatusCode) -> Self {
64        self.status = status;
65        self
66    }
67
68    /// Add headers to the response.
69    #[must_use]
70    pub fn headers(mut self, headers: HeaderMap) -> Self {
71        self.headers = headers;
72        self
73    }
74}
75
76impl<B: Body> From<B> for Reply<B> {
77    fn from(body: B) -> Self {
78        Self {
79            status: StatusCode::OK,
80            headers: HeaderMap::new(),
81            body,
82        }
83    }
84}
85
86impl<B: Body> Deref for Reply<B> {
87    type Target = B;
88
89    fn deref(&self) -> &Self::Target {
90        &self.body
91    }
92}
93
94// /// Implemented by the `Reply::body` to convert itself into a body compatible with
95// /// `[IntoResponse]`.
96// pub trait IntoBody: Body {
97//     /// Convert into a body + content type.
98//     ///
99//     /// # Errors
100//     ///
101//     /// Returns an error if the body cannot be encoded (for example, if JSON
102//     /// serialization fails).
103//     fn into_body(self) -> anyhow::Result<Vec<u8>>;
104// }
105
106// impl<T> IntoResponse for Reply<T>
107// where
108//     T: IntoBody,
109// {
110//     fn into_response(self) -> Response {
111//         let body = match self.body.into_body() {
112//             Ok(v) => v,
113//             Err(e) => {
114//                 return (StatusCode::INTERNAL_SERVER_ERROR, format!("body encoding error: {e}"))
115//                     .into_response();
116//             }
117//         };
118
119//         let mut hm = self.headers;
120//         if !hm.contains_key(header::CONTENT_TYPE) {
121//             hm.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain; charset=utf-8"));
122//         }
123
124//         let status = self.status;
125//         (status, hm, body).into_response()
126//     }
127// }