use bytes::Bytes;
use serde::Serialize;
use crate::{
cookies::{Cookies, cookie::Cookie},
request::Request,
routing::next::Next,
session::Session,
utils::{Values, http::Headers},
view::{ViewBag, ViewData},
};
pub type StatusCode = u16;
pub const HTTP_CONTINUE: StatusCode = 100;
pub const HTTP_SWITCHING_PROTOCOLS: StatusCode = 101;
pub const HTTP_PROCESSING: StatusCode = 102;
pub const HTTP_EARLY_HINTS: StatusCode = 103;
pub const HTTP_OK: StatusCode = 200;
pub const HTTP_CREATED: StatusCode = 201;
pub const HTTP_ACCEPTED: StatusCode = 202;
pub const HTTP_NON_AUTHORITATIVE_INFORMATION: StatusCode = 203;
pub const HTTP_NO_CONTENT: StatusCode = 204;
pub const HTTP_RESET_CONTENT: StatusCode = 205;
pub const HTTP_PARTIAL_CONTENT: StatusCode = 206;
pub const HTTP_MULTI_STATUS: StatusCode = 207;
pub const HTTP_ALREADY_REPORTED: StatusCode = 208;
pub const HTTP_IM_USED: StatusCode = 226;
pub const HTTP_MULTIPLE_CHOICES: StatusCode = 300;
pub const HTTP_MOVED_PERMANENTLY: StatusCode = 301;
pub const HTTP_FOUND: StatusCode = 302;
pub const HTTP_SEE_OTHER: StatusCode = 303;
pub const HTTP_NOT_MODIFIED: StatusCode = 304;
pub const HTTP_USE_PROXY: StatusCode = 305;
pub const HTTP_TEMPORARY_REDIRECT: StatusCode = 307;
pub const HTTP_PERMANENT_REDIRECT: StatusCode = 308;
pub const HTTP_BAD_REQUEST: StatusCode = 400;
pub const HTTP_UNAUTHORIZED: StatusCode = 401;
pub const HTTP_PAYMENT_REQUIRED: StatusCode = 402;
pub const HTTP_FORBIDDEN: StatusCode = 403;
pub const HTTP_NOT_FOUND: StatusCode = 404;
pub const HTTP_METHOD_NOT_ALLOWED: StatusCode = 405;
pub const HTTP_NOT_ACCEPTABLE: StatusCode = 406;
pub const HTTP_PROXY_AUTHENTICATION_REQUIRED: StatusCode = 407;
pub const HTTP_REQUEST_TIMEOUT: StatusCode = 408;
pub const HTTP_CONFLICT: StatusCode = 409;
pub const HTTP_GONE: StatusCode = 410;
pub const HTTP_LENGTH_REQUIRED: StatusCode = 411;
pub const HTTP_PRECONDITION_FAILED: StatusCode = 412;
pub const HTTP_CONTENT_TOO_LARGE: StatusCode = 413;
pub const HTTP_URI_TOO_LONG: StatusCode = 414;
pub const HTTP_UNSUPPORTED_MEDIA_TYPE: StatusCode = 415;
pub const HTTP_RANGE_NOT_SATISFIABLE: StatusCode = 416;
pub const HTTP_EXPECTATION_FAILED: StatusCode = 417;
pub const HTTP_IM_A_TEAPOT: StatusCode = 418;
pub const HTTP_MISDIRECTED_REQUEST: StatusCode = 421;
pub const HTTP_UNPROCESSABLE_CONTENT: StatusCode = 422;
pub const HTTP_LOCKED: StatusCode = 423;
pub const HTTP_FAILED_DEPENDENCY: StatusCode = 424;
pub const HTTP_TOO_EARLY: StatusCode = 425;
pub const HTTP_UPGRADE_REQUIRED: StatusCode = 426;
pub const HTTP_PRECONDITION_REQUIRED: StatusCode = 428;
pub const HTTP_TOO_MANY_REQUESTS: StatusCode = 429;
pub const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE: StatusCode = 431;
pub const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: StatusCode = 451;
pub const HTTP_INTERNAL_SERVER_ERROR: StatusCode = 500;
pub const HTTP_NOT_IMPLEMENTED: StatusCode = 501;
pub const HTTP_BAD_GATEWAY: StatusCode = 502;
pub const HTTP_SERVICE_UNAVAILABLE: StatusCode = 503;
pub const HTTP_GATEWAY_TIMEOUT: StatusCode = 504;
pub const HTTP_HTTP_VERSION_NOT_SUPPORTED: StatusCode = 505;
pub const HTTP_VARIANT_ALSO_NEGOTIATES: StatusCode = 506;
pub const HTTP_INSUFFICIENT_STORAGE: StatusCode = 507;
pub const HTTP_LOOP_DETECTED: StatusCode = 508;
pub const HTTP_NOT_EXTENDED: StatusCode = 510;
pub const HTTP_NETWORK_AUTHENTICATION_REQUIRED: StatusCode = 511;
#[derive(Clone)]
pub struct Response {
pub(crate) next: Option<Next>,
pub(crate) status_code: StatusCode,
pub(crate) headers: Headers,
pub(crate) referer: String,
pub(crate) content: Bytes,
pub(crate) cookies: Cookies,
pub(crate) session: Session,
pub(crate) view: Option<ViewBag>,
is_next: bool,
}
impl Into<serde_json::Value> for Response {
fn into(self) -> serde_json::Value {
serde_json::json!({
"status_code": &self.status_code,
"headers": &self.headers,
"cookies": &self.cookies,
"session": &self.session,
"view": &self.view,
})
}
}
impl Default for Response {
fn default() -> Self {
Self {
next: None,
status_code: HTTP_OK,
headers: Headers::new(),
referer: String::from("/"),
content: Bytes::new(),
cookies: Default::default(),
session: Default::default(),
view: None,
is_next: false,
}
}
}
impl Response {
#[inline]
pub(crate) fn new() -> Self {
Self::default()
}
#[inline]
pub fn request(&mut self) -> Request {
self.next
.as_mut()
.expect("Middleware chain error: Next is None")
.request()
}
#[inline]
pub fn status_code(mut self, status_code: StatusCode) -> Self {
self.status_code = status_code;
self
}
pub fn header(&self, key: &str) -> String {
self.headers
.get(key)
.cloned()
.unwrap_or_default()
}
#[inline]
pub fn set_header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
self.headers.insert(k.into(), v.into());
self
}
pub fn set_headers(mut self, headers: Headers) -> Self {
for (k, v) in headers {
self.headers.insert(k, v);
}
self
}
#[inline]
pub fn cookies(&mut self) -> &mut Cookies {
&mut self.cookies
}
#[inline]
pub fn set_cookie(&mut self, k: impl Into<String>, v: impl Into<String>) -> &mut Cookie {
self.cookies.set(k, v)
}
#[inline]
pub fn remove_cookie(mut self, k: impl Into<String>) -> Self {
self.cookies.remove(k);
self
}
#[inline]
pub fn session(&mut self) -> &mut Session {
&mut self.session
}
#[inline]
pub fn set_session(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
self.session.set(k, v);
self
}
#[inline]
pub fn remove_session(mut self, k: impl Into<String>) -> Self {
self.session.remove(k);
self
}
#[inline]
pub fn set_flash(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
self.session.set_flash(k, v);
self
}
#[inline]
pub fn body(mut self, body: impl Into<Bytes>) -> Self {
self.content = body.into();
self
}
pub fn json<J>(self, object: &J) -> Self
where
J: ?Sized + Serialize,
{
match serde_json::to_vec(object) {
Ok(bytes) => self
.set_header("Content-Type", "application/json")
.body(bytes),
Err(_) => self.status_code(HTTP_INTERNAL_SERVER_ERROR),
}
}
#[inline]
pub fn html(self, html: impl Into<String>) -> Self {
let html_str = html.into();
self.set_header("Content-Type", "text/html; charset=utf-8")
.body(html_str)
}
#[inline]
pub fn view(mut self, view: &str, data: Option<ViewData>) -> Self {
self.view = Some(ViewBag::new(view, data));
self
}
#[inline]
pub fn redirect(self, to: impl Into<String>) -> Self {
self.redirect_with_status_code(to, HTTP_TEMPORARY_REDIRECT)
}
#[inline]
pub fn redirect_permanent(self, to: impl Into<String>) -> Self {
self.redirect_with_status_code(to, HTTP_PERMANENT_REDIRECT)
}
fn redirect_with_status_code(self, to: impl Into<String>, status_code: StatusCode) -> Self {
let target = to.into();
let html = format!(
"<!DOCTYPE html><html><head><meta http-equiv=\"Refresh\" content=\"0; url='{target}'\"></head><body></body></html>"
);
self
.html(html)
.status_code(status_code)
}
pub fn back(self) -> Self {
if self.referer.is_empty() {
self.redirect("/")
} else {
let referer = self.referer.clone();
self.redirect(referer)
}
}
pub fn with_error(mut self, name: impl Into<String>, error: impl Into<String>) -> Self {
self.session.errors.insert(name.into(), error.into());
self
}
pub fn with_errors(mut self, errors: Values) -> Self {
for (name, error) in errors {
self.session.errors.insert(name, error);
}
self
}
pub fn with_flash(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.session.flash.insert(key.into(), value.into());
self
}
pub(crate) fn with_old(mut self, old: Values) -> Self {
for (k, v) in old {
self.session.old.insert(k, v);
}
self
}
#[inline]
pub(crate) fn next(&mut self, is: bool) {
self.is_next = is;
}
#[inline]
pub(crate) fn is_next(&self) -> bool {
self.is_next
}
}