Skip to main content

flyer/response/
mod.rs

1use bytes::Bytes;
2use serde::Serialize;
3
4use crate::{
5    cookies::{Cookies, cookie::Cookie},
6    request::Request,
7    routing::next::Next,
8    session::Session,
9    utils::{Values, http::Headers},
10    view::{ViewBag, ViewData},
11};
12
13pub type StatusCode = u16;
14
15pub const HTTP_CONTINUE: StatusCode = 100;
16pub const HTTP_SWITCHING_PROTOCOLS: StatusCode = 101;
17pub const HTTP_PROCESSING: StatusCode = 102;
18pub const HTTP_EARLY_HINTS: StatusCode = 103;
19pub const HTTP_OK: StatusCode = 200;
20pub const HTTP_CREATED: StatusCode = 201;
21pub const HTTP_ACCEPTED: StatusCode = 202;
22pub const HTTP_NON_AUTHORITATIVE_INFORMATION: StatusCode = 203;
23pub const HTTP_NO_CONTENT: StatusCode = 204;
24pub const HTTP_RESET_CONTENT: StatusCode = 205;
25pub const HTTP_PARTIAL_CONTENT: StatusCode = 206;
26pub const HTTP_MULTI_STATUS: StatusCode = 207;
27pub const HTTP_ALREADY_REPORTED: StatusCode = 208;
28pub const HTTP_IM_USED: StatusCode = 226;
29pub const HTTP_MULTIPLE_CHOICES: StatusCode = 300;
30pub const HTTP_MOVED_PERMANENTLY: StatusCode = 301;
31pub const HTTP_FOUND: StatusCode = 302;
32pub const HTTP_SEE_OTHER: StatusCode = 303;
33pub const HTTP_NOT_MODIFIED: StatusCode = 304;
34pub const HTTP_USE_PROXY: StatusCode = 305;
35pub const HTTP_TEMPORARY_REDIRECT: StatusCode = 307;
36pub const HTTP_PERMANENT_REDIRECT: StatusCode = 308;
37pub const HTTP_BAD_REQUEST: StatusCode = 400;
38pub const HTTP_UNAUTHORIZED: StatusCode = 401;
39pub const HTTP_PAYMENT_REQUIRED: StatusCode = 402;
40pub const HTTP_FORBIDDEN: StatusCode = 403;
41pub const HTTP_NOT_FOUND: StatusCode = 404;
42pub const HTTP_METHOD_NOT_ALLOWED: StatusCode = 405;
43pub const HTTP_NOT_ACCEPTABLE: StatusCode = 406;
44pub const HTTP_PROXY_AUTHENTICATION_REQUIRED: StatusCode = 407;
45pub const HTTP_REQUEST_TIMEOUT: StatusCode = 408;
46pub const HTTP_CONFLICT: StatusCode = 409;
47pub const HTTP_GONE: StatusCode = 410;
48pub const HTTP_LENGTH_REQUIRED: StatusCode = 411;
49pub const HTTP_PRECONDITION_FAILED: StatusCode = 412;
50pub const HTTP_CONTENT_TOO_LARGE: StatusCode = 413;
51pub const HTTP_URI_TOO_LONG: StatusCode = 414;
52pub const HTTP_UNSUPPORTED_MEDIA_TYPE: StatusCode = 415;
53pub const HTTP_RANGE_NOT_SATISFIABLE: StatusCode = 416;
54pub const HTTP_EXPECTATION_FAILED: StatusCode = 417;
55pub const HTTP_IM_A_TEAPOT: StatusCode = 418;
56pub const HTTP_MISDIRECTED_REQUEST: StatusCode = 421;
57pub const HTTP_UNPROCESSABLE_CONTENT: StatusCode = 422;
58pub const HTTP_LOCKED: StatusCode = 423;
59pub const HTTP_FAILED_DEPENDENCY: StatusCode = 424;
60pub const HTTP_TOO_EARLY: StatusCode = 425;
61pub const HTTP_UPGRADE_REQUIRED: StatusCode = 426;
62pub const HTTP_PRECONDITION_REQUIRED: StatusCode = 428;
63pub const HTTP_TOO_MANY_REQUESTS: StatusCode = 429;
64pub const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE: StatusCode = 431;
65pub const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: StatusCode = 451;
66pub const HTTP_INTERNAL_SERVER_ERROR: StatusCode = 500;
67pub const HTTP_NOT_IMPLEMENTED: StatusCode = 501;
68pub const HTTP_BAD_GATEWAY: StatusCode = 502;
69pub const HTTP_SERVICE_UNAVAILABLE: StatusCode = 503;
70pub const HTTP_GATEWAY_TIMEOUT: StatusCode = 504;
71pub const HTTP_HTTP_VERSION_NOT_SUPPORTED: StatusCode = 505;
72pub const HTTP_VARIANT_ALSO_NEGOTIATES: StatusCode = 506;
73pub const HTTP_INSUFFICIENT_STORAGE: StatusCode = 507;
74pub const HTTP_LOOP_DETECTED: StatusCode = 508;
75pub const HTTP_NOT_EXTENDED: StatusCode = 510;
76pub const HTTP_NETWORK_AUTHENTICATION_REQUIRED: StatusCode = 511;
77
78#[derive(Clone)]
79pub struct Response {
80    pub(crate) next: Option<Next>,
81    pub(crate) status_code: StatusCode,
82    pub(crate) headers: Headers,
83    pub(crate) referer: String,
84    pub(crate) content: Bytes,
85    pub(crate) cookies: Cookies,
86    pub(crate) session: Session,
87    pub(crate) view: Option<ViewBag>,
88    is_next: bool,
89}
90
91impl Into<serde_json::Value> for Response {
92    fn into(self) -> serde_json::Value {
93        serde_json::json!({
94            "status_code": &self.status_code,
95            "headers": &self.headers,
96            "cookies": &self.cookies,
97            "session": &self.session,
98            "view": &self.view,
99        })
100    }
101}
102
103impl Default for Response {
104    fn default() -> Self {
105        Self {
106            next: None,
107            status_code: HTTP_OK,
108            headers: Headers::new(),
109            referer: String::from("/"),
110            content: Bytes::new(),
111            cookies: Default::default(),
112            session: Default::default(),
113            view: None,
114            is_next: false,
115        }
116    }
117}
118
119impl Response {
120    #[inline]
121    pub(crate) fn new() -> Self {
122        Self::default()
123    }
124
125    #[inline]
126    pub fn request(&mut self) -> Request {
127        self.next
128            .as_mut()
129            .expect("Middleware chain error: Next is None")
130            .request()
131    }
132
133    #[inline]
134    pub fn status_code(mut self, status_code: StatusCode) -> Self {
135        self.status_code = status_code;
136        self
137    }
138
139    pub fn header(&self, key: &str) -> String {
140        self.headers
141            .get(key)
142            .cloned()
143            .unwrap_or_default()
144    }
145
146    #[inline]
147    pub fn set_header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
148        self.headers.insert(k.into(), v.into());
149        self
150    }
151
152    pub fn set_headers(mut self, headers: Headers) -> Self {
153        for (k, v) in headers {
154            self.headers.insert(k, v);
155        }
156        self
157    }
158
159    #[inline]
160    pub fn cookies(&mut self) -> &mut Cookies {
161        &mut self.cookies
162    }
163
164    #[inline]
165    pub fn set_cookie(&mut self, k: impl Into<String>, v: impl Into<String>) -> &mut Cookie {
166        self.cookies.set(k, v)
167    }
168
169    #[inline]
170    pub fn remove_cookie(mut self, k: impl Into<String>) -> Self {
171        self.cookies.remove(k);
172        self
173    }
174
175    #[inline]
176    pub fn session(&mut self) -> &mut Session {
177        &mut self.session
178    }
179
180    #[inline]
181    pub fn set_session(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
182        self.session.set(k, v);
183        self
184    }
185
186    #[inline]
187    pub fn remove_session(mut self, k: impl Into<String>) -> Self {
188        self.session.remove(k);
189        self
190    }
191
192    #[inline]
193    pub fn set_flash(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
194        self.session.set_flash(k, v);
195        self
196    }
197
198    #[inline]
199    pub fn body(mut self, body: impl Into<Bytes>) -> Self {
200        self.content = body.into();
201        self
202    }
203
204    pub fn json<J>(self, object: &J) -> Self
205    where
206        J: ?Sized + Serialize,
207    {
208        match serde_json::to_vec(object) {
209            Ok(bytes) => self
210                .set_header("Content-Type", "application/json")
211                .body(bytes),
212            Err(_) => self.status_code(HTTP_INTERNAL_SERVER_ERROR),
213        }
214    }
215
216    #[inline]
217    pub fn html(self, html: impl Into<String>) -> Self {
218        let html_str = html.into();
219        self.set_header("Content-Type", "text/html; charset=utf-8")
220            .body(html_str)
221    }
222
223    #[inline]
224    pub fn view(mut self, view: &str, data: Option<ViewData>) -> Self {
225        self.view = Some(ViewBag::new(view, data));
226        self
227    }
228
229    #[inline]
230    pub fn redirect(self, to: impl Into<String>) -> Self {
231        self.redirect_with_status_code(to, HTTP_TEMPORARY_REDIRECT)
232    }
233
234    #[inline]
235    pub fn redirect_permanent(self, to: impl Into<String>) -> Self {
236        self.redirect_with_status_code(to, HTTP_PERMANENT_REDIRECT)
237    }
238
239    fn redirect_with_status_code(self, to: impl Into<String>, status_code: StatusCode) -> Self {
240        let target = to.into();
241        let html = format!(
242            "<!DOCTYPE html><html><head><meta http-equiv=\"Refresh\" content=\"0; url='{target}'\"></head><body></body></html>"
243        );
244
245        self
246            .html(html)
247            .status_code(status_code)
248    }
249
250    pub fn back(self) -> Self {
251        if self.referer.is_empty() {
252            self.redirect("/")
253        } else {
254            let referer = self.referer.clone();
255            self.redirect(referer)
256        }
257    }
258
259    pub fn with_error(mut self, name: impl Into<String>, error: impl Into<String>) -> Self {
260        self.session.errors.insert(name.into(), error.into());
261        self
262    }
263
264    pub fn with_errors(mut self, errors: Values) -> Self {
265        for (name, error) in errors {
266            self.session.errors.insert(name, error);
267        }
268        self
269    }
270
271    pub fn with_flash(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
272        self.session.flash.insert(key.into(), value.into());
273        self
274    }
275
276    pub(crate) fn with_old(mut self, old: Values) -> Self {
277        for (k, v) in old {
278            self.session.old.insert(k, v);
279        }
280        self
281    }
282
283    #[inline]
284    pub(crate) fn next(&mut self, is: bool) {
285        self.is_next = is;
286    }
287
288    #[inline]
289    pub(crate) fn is_next(&self) -> bool {
290        self.is_next
291    }
292}