soph_server/response/
builder.rs1use 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}