rama_http/service/web/endpoint/response/
into_response_parts.rs1#![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
15pub trait IntoResponseParts {
83 type Error: IntoResponse;
87
88 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#[derive(Debug)]
111pub struct ResponseParts {
112 pub(crate) res: Response,
113}
114
115impl ResponseParts {
116 #[must_use]
118 pub fn headers(&self) -> &HeaderMap {
119 self.res.headers()
120 }
121
122 pub fn headers_mut(&mut self) -> &mut HeaderMap {
124 self.res.headers_mut()
125 }
126
127 #[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
161pub 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}