Skip to main content

rama_http/service/web/endpoint/response/
into_response_parts.rs

1#![expect(
2    clippy::allow_attributes,
3    reason = "macro-generated `#[allow]` attributes whose underlying lints fire only for some expansions"
4)]
5
6use super::IntoResponse;
7use crate::{
8    Response, StatusCode,
9    header::{HeaderMap, HeaderName, HeaderValue},
10};
11use rama_core::extensions::{Extensions, ExtensionsRef};
12use rama_utils::macros::all_the_tuples_no_last_special_case;
13use std::{convert::Infallible, fmt};
14
15/// Trait for adding headers and extensions to a response.
16///
17/// # Example
18///
19/// ```rust
20/// use rama_http_types::{
21///     StatusCode, HeaderName, HeaderValue, Response,
22/// };
23/// use rama_http::service::web::response::{
24///     ResponseParts, IntoResponse, IntoResponseParts,
25/// };
26///
27/// // Hypothetical helper type for setting a single header
28/// struct SetHeader<'a>(&'a str, &'a str);
29///
30/// impl<'a> IntoResponseParts for SetHeader<'a> {
31///     type Error = (StatusCode, String);
32///
33///     fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
34///         match (self.0.parse::<HeaderName>(), self.1.parse::<HeaderValue>()) {
35///             (Ok(name), Ok(value)) => {
36///                 res.headers_mut().insert(name, value);
37///             },
38///             (Err(_), _) => {
39///                 return Err((
40///                     StatusCode::INTERNAL_SERVER_ERROR,
41///                     format!("Invalid header name {}", self.0),
42///                 ));
43///             },
44///             (_, Err(_)) => {
45///                 return Err((
46///                     StatusCode::INTERNAL_SERVER_ERROR,
47///                     format!("Invalid header value {}", self.1),
48///                 ));
49///             },
50///         }
51///
52///         Ok(res)
53///     }
54/// }
55///
56/// // Its also recommended to implement `IntoResponse` so `SetHeader` can be used on its own as
57/// // the response
58/// impl<'a> IntoResponse for SetHeader<'a> {
59///     fn into_response(self) -> Response {
60///         // This gives an empty response with the header
61///         (self, ()).into_response()
62///     }
63/// }
64///
65/// // We can now return `SetHeader` in responses
66/// //
67/// // Note that returning `impl IntoResponse` might be easier if the response has many parts to
68/// // it. The return type is written out here for clarity.
69/// async fn handler() -> (SetHeader<'static>, SetHeader<'static>, &'static str) {
70///     (
71///         SetHeader("server", "rama"),
72///         SetHeader("x-foo", "custom"),
73///         "body",
74///     )
75/// }
76///
77/// // Or on its own as the whole response
78/// async fn other_handler() -> SetHeader<'static> {
79///     SetHeader("x-foo", "custom")
80/// }
81/// ```
82pub trait IntoResponseParts {
83    /// The type returned in the event of an error.
84    ///
85    /// This can be used to fallibly convert types into headers or extensions.
86    type Error: IntoResponse;
87
88    /// Set parts of the response
89    fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error>;
90}
91
92impl<T> IntoResponseParts for Option<T>
93where
94    T: IntoResponseParts,
95{
96    type Error = T::Error;
97
98    fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error> {
99        if let Some(inner) = self {
100            inner.into_response_parts(res)
101        } else {
102            Ok(res)
103        }
104    }
105}
106
107/// Parts of a response.
108///
109/// Used with [`IntoResponseParts`].
110#[derive(Debug)]
111pub struct ResponseParts {
112    pub(crate) res: Response,
113}
114
115impl ResponseParts {
116    /// Gets a reference to the response headers.
117    #[must_use]
118    pub fn headers(&self) -> &HeaderMap {
119        self.res.headers()
120    }
121
122    /// Gets a mutable reference to the response headers.
123    pub fn headers_mut(&mut self) -> &mut HeaderMap {
124        self.res.headers_mut()
125    }
126
127    /// Gets a reference to the response extensions.
128    #[must_use]
129    pub fn extensions(&self) -> &Extensions {
130        self.res.extensions()
131    }
132}
133
134impl IntoResponseParts for HeaderMap {
135    type Error = Infallible;
136
137    fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
138        res.headers_mut().extend(self);
139        Ok(res)
140    }
141}
142
143impl<K, V, const N: usize> IntoResponseParts for [(K, V); N]
144where
145    K: TryInto<HeaderName, Error: fmt::Display>,
146    V: TryInto<HeaderValue, Error: fmt::Display>,
147{
148    type Error = TryIntoHeaderError<K::Error, V::Error>;
149
150    fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
151        for (key, value) in self {
152            let key = key.try_into().map_err(TryIntoHeaderError::key)?;
153            let value = value.try_into().map_err(TryIntoHeaderError::value)?;
154            res.headers_mut().insert(key, value);
155        }
156
157        Ok(res)
158    }
159}
160
161/// Error returned if converting a value to a header fails.
162pub struct TryIntoHeaderError<K, V> {
163    kind: TryIntoHeaderErrorKind<K, V>,
164}
165
166impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for TryIntoHeaderError<K, V> {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.debug_struct("TryIntoHeaderError")
169            .field("kind", &self.kind)
170            .finish()
171    }
172}
173
174impl<K, V> TryIntoHeaderError<K, V> {
175    pub(super) fn key(err: K) -> Self {
176        Self {
177            kind: TryIntoHeaderErrorKind::Key(err),
178        }
179    }
180
181    pub(super) fn value(err: V) -> Self {
182        Self {
183            kind: TryIntoHeaderErrorKind::Value(err),
184        }
185    }
186}
187
188enum TryIntoHeaderErrorKind<K, V> {
189    Key(K),
190    Value(V),
191}
192
193impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for TryIntoHeaderErrorKind<K, V> {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self {
196            Self::Key(key) => write!(f, "TryIntoHeaderErrorKind::Key({key:?})"),
197            Self::Value(value) => write!(f, "TryIntoHeaderErrorKind::Value({value:?})"),
198        }
199    }
200}
201
202impl<K, V> IntoResponse for TryIntoHeaderError<K, V>
203where
204    K: fmt::Display,
205    V: fmt::Display,
206{
207    fn into_response(self) -> Response {
208        match self.kind {
209            TryIntoHeaderErrorKind::Key(inner) => {
210                (StatusCode::INTERNAL_SERVER_ERROR, inner.to_string()).into_response()
211            }
212            TryIntoHeaderErrorKind::Value(inner) => {
213                (StatusCode::INTERNAL_SERVER_ERROR, inner.to_string()).into_response()
214            }
215        }
216    }
217}
218
219impl<K, V> fmt::Display for TryIntoHeaderError<K, V> {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        match self.kind {
222            TryIntoHeaderErrorKind::Key(_) => write!(f, "failed to convert key to a header name"),
223            TryIntoHeaderErrorKind::Value(_) => {
224                write!(f, "failed to convert value to a header value")
225            }
226        }
227    }
228}
229
230impl<K, V> std::error::Error for TryIntoHeaderError<K, V>
231where
232    K: std::error::Error + 'static,
233    V: std::error::Error + 'static,
234{
235    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
236        match &self.kind {
237            TryIntoHeaderErrorKind::Key(inner) => Some(inner),
238            TryIntoHeaderErrorKind::Value(inner) => Some(inner),
239        }
240    }
241}
242
243macro_rules! impl_into_response_parts {
244    ( $($ty:ident),* $(,)? ) => {
245        #[allow(non_snake_case)]
246        impl<$($ty,)*> IntoResponseParts for ($($ty,)*)
247        where
248            $( $ty: IntoResponseParts, )*
249        {
250            type Error = Response;
251
252            fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error> {
253                let ($($ty,)*) = self;
254
255                $(
256                    let res = match $ty.into_response_parts(res) {
257                        Ok(res) => res,
258                        Err(err) => {
259                            return Err(err.into_response());
260                        }
261                    };
262                )*
263
264                Ok(res)
265            }
266        }
267    }
268}
269
270all_the_tuples_no_last_special_case!(impl_into_response_parts);
271
272impl IntoResponseParts for Extensions {
273    type Error = Infallible;
274
275    fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error> {
276        res.extensions().extend(&self);
277        Ok(res)
278    }
279}