rustolio_utils/http/response/
builder.rs1use crate::bytes::Bytes;
12
13use crate::bytes::IntoUtf8Bytes;
14use crate::bytes::Utf8Bytes;
15
16use super::{Error, HeaderName, Outgoing, Response, Result, StatusCode, Version};
17
18#[cfg(not(target_arch = "wasm32"))]
19pub struct Builder<B> {
20 version: Version,
21 status: StatusCode,
22 headers: Vec<(Utf8Bytes, Utf8Bytes)>,
23 body: B,
24}
25
26#[cfg(not(target_arch = "wasm32"))]
27impl Default for Builder<()> {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl Builder<()> {
34 pub fn new() -> Self {
35 Builder {
36 version: Version::HTTP_11,
37 status: StatusCode::OK,
38 headers: Vec::new(),
39 body: (),
40 }
41 }
42}
43
44#[cfg(not(target_arch = "wasm32"))]
45impl<B> Builder<B> {
46 pub fn status(self, status: StatusCode) -> Self {
47 Self { status, ..self }
48 }
49
50 pub fn header(mut self, key: impl IntoUtf8Bytes, value: impl IntoUtf8Bytes) -> Self {
51 self.add_header(key, value);
52 self
53 }
54
55 fn add_header(&mut self, key: impl IntoUtf8Bytes, value: impl IntoUtf8Bytes) -> bool {
56 let key = key.into_utf8_bytes();
57 if self.headers.iter().any(|(k, _)| k == &key) {
58 return false;
59 }
60 self.headers.push((key, value.into_utf8_bytes()));
61 true
62 }
63}
64
65#[cfg(not(target_arch = "wasm32"))]
66impl Builder<()> {
67 pub fn empty(self) -> Builder<Outgoing> {
68 let body = Outgoing::empty();
69 Builder {
70 version: self.version,
71 status: self.status,
72 headers: self.headers,
73 body,
74 }
75 }
76
77 pub fn bytes(mut self, body: Bytes) -> Builder<Outgoing> {
78 self.add_header(HeaderName::CONTENT_TYPE, "application/octet-stream");
79 let body = Outgoing::from_bytes(body);
80 Builder {
81 version: self.version,
82 status: self.status,
83 headers: self.headers,
84 body,
85 }
86 }
87
88 pub fn encoded<T: crate::prelude::Encode>(self, body: &T) -> Result<Builder<Outgoing>> {
90 let encoded = crate::bytes::encoding::encode_to_bytes(body).map_err(|_| Error::Parse)?;
91 Ok(self.bytes(encoded))
92 }
93
94 pub fn html(mut self, body: impl IntoUtf8Bytes) -> Builder<Outgoing> {
95 self.add_header(HeaderName::CONTENT_TYPE, "text/html; charset=utf-8");
96 let body = Outgoing::from_bytes(body.into());
97 Builder {
98 version: self.version,
99 status: self.status,
100 headers: self.headers,
101 body,
102 }
103 }
104
105 pub fn text(mut self, body: impl IntoUtf8Bytes) -> Builder<Outgoing> {
106 self.add_header(HeaderName::CONTENT_TYPE, "text/plain; charset=utf-8");
107 let body = Outgoing::from_bytes(body.into());
108 Builder {
109 version: self.version,
110 status: self.status,
111 headers: self.headers,
112 body,
113 }
114 }
115
116 pub fn body<B>(self, body: B) -> Builder<B> {
132 Builder {
133 version: self.version,
134 status: self.status,
135 headers: self.headers,
136 body,
137 }
138 }
139}
140
141#[cfg(not(target_arch = "wasm32"))]
142impl<B> Builder<B> {
143 pub fn build(self) -> Result<Response<B>> {
144 let mut res = http::Response::builder()
145 .version(self.version)
146 .status(self.status)
147 .body(self.body)?;
148 let headers = res.headers_mut();
149 for (key, value) in self.headers {
150 let key: http::HeaderName = key.to_vec().try_into().map_err(http::Error::from)?;
151 let value: http::HeaderValue = value.to_vec().try_into().map_err(http::Error::from)?;
152 headers.append(key, value);
153 }
154 Ok(Response(res))
155 }
156}