#![warn(missing_docs)]
use crate::res::{response_cookie::Cookie, response_status::StatusCode};
use bytes::Bytes;
use futures::{Stream, StreamExt};
use serde::Serialize;
use std::pin::Pin;
mod response_body;
pub(crate) use response_body::{ResponseBody, ResponseBodyType};
pub mod response_headers;
pub mod response_status;
pub mod response_cookie;
use response_cookie::AddCookie;
pub use response_cookie::{CookieOptions, CookieSameSiteOptions};
use response_headers::ResponseHeaders;
pub mod conversions;
mod response_error;
pub use response_error::HttpResponseError;
pub struct HttpResponse {
pub(crate) body: ResponseBody,
pub(crate) status_code: StatusCode,
pub headers: ResponseHeaders,
pub(crate) cookies: Vec<Cookie>,
pub(crate) stream:
Option<Pin<Box<dyn Stream<Item = Result<Bytes, HttpResponseError>> + Send + 'static>>>,
}
impl std::fmt::Debug for HttpResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpResponse")
.field("status_code", &self.status_code)
.field("body", &self.body)
.field("cookies", &self.cookies)
.field("headers", &self.headers)
.field("stream", &"<stream>")
.finish()
}
}
impl Default for HttpResponse {
fn default() -> Self {
Self::new()
}
}
impl Clone for HttpResponse {
fn clone(&self) -> Self {
Self {
status_code: self.status_code,
body: self.body.clone(),
cookies: self.cookies.clone(),
headers: self.headers.clone(),
stream: None,
}
}
}
impl HttpResponse {
pub fn new() -> Self {
Self {
status_code: StatusCode::Ok,
body: ResponseBody::TEXT(String::new()),
headers: ResponseHeaders::new(),
cookies: Vec::new(),
stream: None,
}
}
pub fn ok(mut self) -> Self {
self.status_code = StatusCode::Ok;
self
}
pub fn created(mut self) -> Self {
self.status_code = StatusCode::Created;
self
}
pub fn accepted(mut self) -> Self {
self.status_code = StatusCode::Accepted;
self
}
pub fn no_content(mut self) -> Self {
self.status_code = StatusCode::NoContent;
self
}
pub fn bad_request(mut self) -> Self {
self.status_code = StatusCode::BadRequest;
return self;
}
pub fn unauthorized(mut self) -> Self {
self.status_code = StatusCode::Unauthorized;
return self;
}
pub fn forbidden(mut self) -> Self {
self.status_code = StatusCode::Forbidden;
return self;
}
pub fn not_found(mut self) -> Self {
self.status_code = StatusCode::NotFound;
return self;
}
pub fn method_not_allowed(mut self) -> Self {
self.status_code = StatusCode::MethodNotAllowed;
return self;
}
pub fn conflict(mut self) -> Self {
self.status_code = StatusCode::Conflict;
return self;
}
pub fn internal_server_error(mut self) -> Self {
self.status_code = StatusCode::InternalServerError;
return self;
}
pub fn not_implemented(mut self) -> Self {
self.status_code = StatusCode::NotImplemented;
return self;
}
pub fn bad_gateway(mut self) -> Self {
self.status_code = StatusCode::BadGateway;
return self;
}
pub fn service_unavailable(mut self) -> Self {
self.status_code = StatusCode::ServiceUnavailable;
return self;
}
pub fn status(mut self, status_code: u16) -> Self {
self.status_code = StatusCode::from_u16(status_code);
return self;
}
pub fn status_code(&self) -> u16 {
self.status_code.as_u16()
}
pub fn text<T: Into<String>>(mut self, text: T) -> Self {
self.body = ResponseBody::new_text(text);
return self;
}
pub fn json<T: Serialize>(mut self, json: T) -> Self {
self.body = ResponseBody::new_json(json);
return self;
}
pub fn bytes<T: Into<Bytes>>(mut self, bytes: T) -> Self {
self.body = ResponseBody::new_binary(bytes.into());
return self;
}
pub fn set_header<K, V>(
mut self,
header_name: K,
header_value: V,
) -> Self where K: Into<String>, V: Into<String> {
self.headers.insert(header_name.into(), header_value.into());
self
}
pub fn set_cookie(
mut self,
cookie_name: &'static str,
cookie_value: &'static str,
options: Option<CookieOptions>,
) -> Self {
self.cookies.push(Cookie::AddCookie(AddCookie {
name: cookie_name,
value: cookie_value,
options: options.unwrap_or_default(),
}));
self
}
pub(crate) fn set_cookie_raw(mut self, cookie: Cookie) -> Self {
self.cookies.push(cookie.clone());
self
}
pub fn clear_cookie(mut self, key: &'static str) -> Self {
self.cookies.retain(|cookie| match cookie {
Cookie::AddCookie(add_cookie) => add_cookie.name != key,
Cookie::RemoveCookie(name) => *name != key,
});
self.cookies.push(Cookie::RemoveCookie(key));
self
}
pub fn redirect(mut self, path: &'static str) -> Self {
self.status_code = StatusCode::Redirect;
self.headers.insert("Location", path);
self
}
pub fn permanent_redirect(mut self, path: &'static str) -> Self {
self.status_code = StatusCode::PermanentRedirect;
self.headers.insert("Location", path);
self
}
pub fn html(mut self, html: &str) -> Self {
self.body = ResponseBody::new_html(html);
self
}
pub async fn send_file(mut self, path: &'static str) -> Self {
let file = tokio::fs::read(path).await;
match file {
Ok(file) => {
self.body = ResponseBody::new_binary(file);
}
Err(e) => {
eprintln!("Error reading file: {}", e);
}
}
self
}
pub fn write<S, E>(mut self, stream: S) -> Self
where
S: Stream<Item = Result<Bytes, E>> + Send + 'static,
E: Into<HttpResponseError> + Send + 'static,
{
self.headers.insert("transfer-encoding", "chunked");
self.headers.insert("cache-control", "no-cache");
self.stream = Some(Box::pin(stream.map(|result| result.map_err(Into::into))));
self
}
}