Skip to main content

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

1use super::IntoResponse;
2use crate::Body;
3use crate::Response;
4use crate::StatusCode;
5use crate::headers::ContentType;
6use rama_core::error::BoxError;
7use rama_core::telemetry::tracing;
8use rama_utils::macros::impl_deref;
9use serde::Serialize;
10
11use super::Headers;
12
13/// Wrapper used to create Form Http [`Response`]s,
14/// as well as to extract Form from Http [`Request`] bodies.
15///
16/// [`Request`]: crate::Request
17/// [`Response`]: crate::Response
18///
19/// # Examples
20/// ## Creating a Form Response
21///
22/// ```
23/// use serde::Serialize;
24/// use rama_http::service::web::response::{
25///     IntoResponse, Form,
26/// };
27///
28/// #[derive(Serialize)]
29/// struct Payload {
30///     name: String,
31///     age: i32,
32///     is_student: bool
33/// }
34///
35/// async fn handler() -> impl IntoResponse {
36///     Form(Payload {
37///         name: "john".to_string(),
38///         age: 30,
39///         is_student: false
40///     })
41/// }
42/// ```
43///
44/// ## Extracting Form from a Request
45///
46/// ```
47/// use rama_http::service::web::response::Form;
48///
49/// #[derive(Debug, serde::Deserialize)]
50/// struct Input {
51///     name: String,
52///     age: u8,
53///     alive: Option<bool>,
54/// }
55///
56/// # fn bury(name: impl AsRef<str>) {}
57///
58/// async fn handler(Form(input): Form<Input>) {
59///     if !input.alive.unwrap_or_default() {
60///         bury(&input.name);
61///     }
62/// }
63/// ```
64#[derive(Debug, Clone, Copy)]
65pub struct Form<T>(pub T);
66
67impl_deref!(Form);
68
69impl<T> From<T> for Form<T> {
70    fn from(value: T) -> Self {
71        Self(value)
72    }
73}
74
75impl<T> IntoResponse for Form<T>
76where
77    T: Serialize,
78{
79    fn into_response(self) -> Response {
80        // Extracted into separate fn so it's only compiled once for all T.
81        fn make_response(ser_result: Result<String, serde_html_form::ser::Error>) -> Response {
82            match ser_result {
83                Ok(body) => {
84                    (Headers::single(ContentType::form_url_encoded()), body).into_response()
85                }
86                Err(err) => {
87                    tracing::error!("response error: {err:?}");
88                    StatusCode::INTERNAL_SERVER_ERROR.into_response()
89                }
90            }
91        }
92        make_response(serde_html_form::to_string(&self.0))
93    }
94}
95
96impl<T> TryFrom<Form<T>> for Body
97where
98    T: Serialize,
99{
100    type Error = BoxError;
101
102    fn try_from(form: Form<T>) -> Result<Self, Self::Error> {
103        match serde_html_form::to_string(&form.0) {
104            Ok(body) => Ok(body.into()),
105            Err(err) => Err(BoxError::from(err)),
106        }
107    }
108}