soph_server/response/
builder.rs

1use crate::ServerResult;
2use axum::{
3    body::Body,
4    http::{header, response::Builder, HeaderName, HeaderValue, StatusCode},
5    response::Response,
6};
7use std::any::Any;
8
9pub struct ResponseBuilder {
10    inner: Builder,
11}
12
13impl Default for ResponseBuilder {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19impl ResponseBuilder {
20    fn new() -> Self {
21        Self {
22            inner: Builder::new().status(StatusCode::OK),
23        }
24    }
25
26    pub fn builder(self) -> Builder {
27        self.inner
28    }
29
30    pub fn status<T>(self, status: T) -> Self
31    where
32        StatusCode: TryFrom<T>,
33        <StatusCode as TryFrom<T>>::Error: Into<axum::http::Error>,
34    {
35        Self {
36            inner: self.inner.status(status),
37        }
38    }
39
40    pub fn header<K, V>(self, key: K, value: V) -> Self
41    where
42        HeaderName: TryFrom<K>,
43        <HeaderName as TryFrom<K>>::Error: Into<axum::http::Error>,
44        HeaderValue: TryFrom<V>,
45        <HeaderValue as TryFrom<V>>::Error: Into<axum::http::Error>,
46    {
47        Self {
48            inner: self.inner.header(key, value),
49        }
50    }
51
52    pub fn extension<T>(self, extension: T) -> Self
53    where
54        T: Clone + Any + Send + Sync + 'static,
55    {
56        Self {
57            inner: self.inner.extension(extension),
58        }
59    }
60
61    pub fn etag(self, etag: &str) -> ServerResult<Self> {
62        Ok(Self {
63            inner: self.inner.header(header::ETAG, HeaderValue::from_str(etag)?),
64        })
65    }
66
67    #[cfg(feature = "response-cookie")]
68    pub fn cookies(self, cookies: &[axum_extra::extract::cookie::Cookie<'_>]) -> ServerResult<Self> {
69        let mut resp = self.inner;
70
71        for cookie in cookies {
72            let header_value = cookie.encoded().to_string().parse::<HeaderValue>()?;
73            resp = resp.header(header::SET_COOKIE, header_value);
74        }
75
76        Ok(Self { inner: resp })
77    }
78
79    pub fn text(self, content: &str) -> ServerResult<Response> {
80        Ok(self
81            .inner
82            .header(
83                header::CONTENT_TYPE,
84                HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
85            )
86            .body(Body::from(content.to_string()))?)
87    }
88
89    pub fn empty(self) -> ServerResult<Response> {
90        Ok(self.inner.body(Body::empty())?)
91    }
92
93    pub fn html(self, content: &str) -> ServerResult<Response> {
94        Ok(self
95            .inner
96            .header(
97                header::CONTENT_TYPE,
98                HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
99            )
100            .body(Body::from(content.to_string()))?)
101    }
102
103    #[cfg(feature = "response-json")]
104    pub fn json<T>(self, item: T) -> ServerResult<Response>
105    where
106        T: serde::Serialize,
107    {
108        use bytes::{BufMut, BytesMut};
109
110        let mut buf = BytesMut::with_capacity(128).writer();
111        serde_json::to_writer(&mut buf, &item)?;
112
113        let body = Body::from(buf.into_inner().freeze());
114
115        Ok(self
116            .inner
117            .header(
118                header::CONTENT_TYPE,
119                HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
120            )
121            .body(body)?)
122    }
123
124    #[cfg(feature = "response-view")]
125    pub fn view<T>(self, template: &str, data: T) -> ServerResult<Response>
126    where
127        T: serde::Serialize,
128    {
129        let engine = soph_core::support::app().resolve::<soph_view::View>()?;
130        let html = engine.render(template, &tera::Context::from_serialize(data)?)?;
131
132        self.html(&html)
133    }
134
135    pub fn redirect(self, to: &str) -> ServerResult<Response> {
136        Ok(self
137            .inner
138            .status(StatusCode::SEE_OTHER)
139            .header(header::LOCATION, to)
140            .body(Body::empty())?)
141    }
142}