ayun_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
64                .inner
65                .header(header::ETAG, HeaderValue::from_str(etag)?),
66        })
67    }
68
69    #[cfg(feature = "response-cookie")]
70    pub fn cookies(
71        self,
72        cookies: &[axum_extra::extract::cookie::Cookie<'_>],
73    ) -> ServerResult<Self> {
74        let mut resp = self.inner;
75
76        for cookie in cookies {
77            let header_value = cookie.encoded().to_string().parse::<HeaderValue>()?;
78            resp = resp.header(header::SET_COOKIE, header_value);
79        }
80
81        Ok(Self { inner: resp })
82    }
83
84    pub fn text(self, content: &str) -> ServerResult<Response> {
85        Ok(self
86            .inner
87            .header(
88                header::CONTENT_TYPE,
89                HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
90            )
91            .body(Body::from(content.to_string()))?)
92    }
93
94    pub fn empty(self) -> ServerResult<Response> {
95        Ok(self.inner.body(Body::empty())?)
96    }
97
98    pub fn html(self, content: &str) -> ServerResult<Response> {
99        Ok(self
100            .inner
101            .header(
102                header::CONTENT_TYPE,
103                HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
104            )
105            .body(Body::from(content.to_string()))?)
106    }
107
108    #[cfg(feature = "response-json")]
109    pub fn json<T>(self, item: T) -> ServerResult<Response>
110    where
111        T: serde::Serialize,
112    {
113        use bytes::{BufMut, BytesMut};
114
115        let mut buf = BytesMut::with_capacity(128).writer();
116        serde_json::to_writer(&mut buf, &item)?;
117
118        let body = Body::from(buf.into_inner().freeze());
119
120        Ok(self
121            .inner
122            .header(
123                header::CONTENT_TYPE,
124                HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
125            )
126            .body(body)?)
127    }
128
129    #[cfg(feature = "response-view")]
130    pub fn view<T>(self, template: &str, data: T) -> ServerResult<Response>
131    where
132        T: serde::Serialize,
133    {
134        let engine = ayun_core::support::app().resolve::<ayun_view::View>()?;
135        let html = engine.render(template, &tera::Context::from_serialize(data)?)?;
136
137        self.html(&html)
138    }
139
140    pub fn redirect(self, to: &str) -> ServerResult<Response> {
141        Ok(self
142            .inner
143            .status(StatusCode::SEE_OTHER)
144            .header(header::LOCATION, to)
145            .body(Body::empty())?)
146    }
147}