Skip to main content

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

1use super::IntoResponse;
2use crate::{Body, Response, StatusCode};
3use csv;
4use rama_core::bytes::buf::Writer;
5use rama_core::bytes::{BufMut, BytesMut};
6use rama_core::error::BoxError;
7use rama_http_headers::ContentType;
8use rama_utils::macros::impl_deref;
9use serde::Serialize;
10
11use super::Headers;
12
13/// Wrapper used to create Csv Http [`Response`]s,
14/// as well as to extract Csv from Http [`Request`] bodies.
15///
16/// [`Request`]: crate::Request
17/// [`Response`]: crate::Response
18///
19/// # Examples
20///
21/// ## Creating a Csv Response
22///
23/// ```
24/// use serde_json::json;
25/// use rama_http::service::web::response::{IntoResponse, Csv};
26///
27/// async fn handler() -> impl IntoResponse {
28///     Csv(
29///         vec![
30///             json!({
31///                 "name": "john",
32///                 "age": 30,
33///                 "is_student": false
34///             })
35///         ]
36///     )
37/// }
38/// ```
39///
40/// ## Extracting Csv from a Request
41///
42/// ```
43/// use serde_json::json;
44/// use rama_http::service::web::response::Csv;
45///
46/// #[derive(Debug, serde::Deserialize)]
47/// struct Input {
48///     name: String,
49///     age: u8,
50///     alive: Option<bool>,
51/// }
52///
53/// # fn bury(name: impl AsRef<str>) {}
54///
55/// async fn handler(Csv(input): Csv<Vec<Input>>) {
56///     if !input[0].alive.unwrap_or_default() {
57///         bury(&input[0].name);
58///     }
59/// }
60/// ```
61#[derive(Debug, Clone, Copy)]
62pub struct Csv<T>(pub T);
63
64impl_deref!(Csv);
65
66impl<T> From<T> for Csv<T> {
67    fn from(inner: T) -> Self {
68        Self(inner)
69    }
70}
71
72impl<T> IntoResponse for Csv<T>
73where
74    T: IntoIterator<Item: Serialize> + std::fmt::Debug,
75{
76    fn into_response(self) -> Response {
77        // Extracted into separate fn so it's only compiled once for all T.
78        fn make_response(
79            res: csv::Result<Vec<()>>,
80            mut wtr: csv::Writer<Writer<BytesMut>>,
81        ) -> Response {
82            if let Err(err) = res {
83                return (
84                    StatusCode::INTERNAL_SERVER_ERROR,
85                    Headers::single(ContentType::text_utf8()),
86                    err.to_string(),
87                )
88                    .into_response();
89            }
90            if let Err(err) = wtr.flush() {
91                return (
92                    StatusCode::INTERNAL_SERVER_ERROR,
93                    Headers::single(ContentType::text_utf8()),
94                    err.to_string(),
95                )
96                    .into_response();
97            }
98
99            let bw = match wtr.into_inner() {
100                Ok(bw) => bw,
101                Err(err) => {
102                    return (
103                        StatusCode::INTERNAL_SERVER_ERROR,
104                        Headers::single(ContentType::text_utf8()),
105                        err.to_string(),
106                    )
107                        .into_response();
108                }
109            };
110
111            (
112                Headers::single(ContentType::csv_utf8()),
113                bw.into_inner().freeze(),
114            )
115                .into_response()
116        }
117
118        // Use a small initial capacity of 128 bytes like serde_json::to_vec
119        // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189
120        let buf = BytesMut::with_capacity(128).writer();
121
122        let mut wtr = csv::Writer::from_writer(buf);
123        let res: Result<Vec<_>, _> = self.0.into_iter().map(|rec| wtr.serialize(rec)).collect();
124
125        make_response(res, wtr)
126    }
127}
128
129impl<T> TryFrom<Csv<T>> for Body
130where
131    T: IntoIterator<Item: Serialize>,
132{
133    type Error = BoxError;
134
135    fn try_from(csv: Csv<T>) -> Result<Self, Self::Error> {
136        // Use a small initial capacity of 128 bytes like serde_json::to_vec
137        // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189
138        let mut buf = BytesMut::with_capacity(128).writer();
139        {
140            let mut wtr = csv::Writer::from_writer(&mut buf);
141            let res: Result<Vec<_>, _> = csv.0.into_iter().map(|rec| wtr.serialize(rec)).collect();
142            if let Err(err) = res {
143                return Err(BoxError::from(err));
144            }
145            if let Err(err) = wtr.flush() {
146                return Err(BoxError::from(err));
147            }
148        }
149
150        Ok(buf.into_inner().freeze().into())
151    }
152}