use actix_web::{HttpResponse, Responder, http::StatusCode, http::header};
use serde::Serialize;
use std::collections::HashMap;
pub struct Json<T> {
data: T,
status: StatusCode,
}
impl<T> Json<T> {
pub fn new(data: T) -> Self {
Self {
data,
status: StatusCode::OK,
}
}
pub fn with_status(data: T, status: StatusCode) -> Self {
Self { data, status }
}
pub fn created(data: T) -> Self {
Self {
data,
status: StatusCode::CREATED,
}
}
pub fn accepted(data: T) -> Self {
Self {
data,
status: StatusCode::ACCEPTED,
}
}
}
impl<T: Serialize> Responder for Json<T> {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::build(self.status)
.content_type("application/json")
.json(&self.data)
}
}
impl<T: Serialize> From<T> for Json<T> {
fn from(data: T) -> Self {
Json::new(data)
}
}
pub struct EmptyJson;
impl Responder for EmptyJson {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::NoContent()
.content_type("application/json")
.finish()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Status(StatusCode);
impl Status {
pub fn new(code: u16) -> Self {
Status(StatusCode::from_u16(code).unwrap_or(StatusCode::OK))
}
pub fn code(&self) -> StatusCode {
self.0
}
pub const fn ok() -> Self {
Status(StatusCode::OK)
}
pub const fn created() -> Self {
Status(StatusCode::CREATED)
}
pub const fn accepted() -> Self {
Status(StatusCode::ACCEPTED)
}
pub const fn no_content() -> Self {
Status(StatusCode::NO_CONTENT)
}
pub const fn moved_permanently() -> Self {
Status(StatusCode::MOVED_PERMANENTLY)
}
pub const fn found() -> Self {
Status(StatusCode::FOUND)
}
pub const fn not_modified() -> Self {
Status(StatusCode::NOT_MODIFIED)
}
pub const fn bad_request() -> Self {
Status(StatusCode::BAD_REQUEST)
}
pub const fn unauthorized() -> Self {
Status(StatusCode::UNAUTHORIZED)
}
pub const fn forbidden() -> Self {
Status(StatusCode::FORBIDDEN)
}
pub const fn not_found() -> Self {
Status(StatusCode::NOT_FOUND)
}
pub const fn conflict() -> Self {
Status(StatusCode::CONFLICT)
}
pub const fn unprocessable_entity() -> Self {
Status(StatusCode::UNPROCESSABLE_ENTITY)
}
pub const fn too_many_requests() -> Self {
Status(StatusCode::TOO_MANY_REQUESTS)
}
pub const fn internal_server_error() -> Self {
Status(StatusCode::INTERNAL_SERVER_ERROR)
}
pub const fn not_implemented() -> Self {
Status(StatusCode::NOT_IMPLEMENTED)
}
pub const fn service_unavailable() -> Self {
Status(StatusCode::SERVICE_UNAVAILABLE)
}
}
impl Responder for Status {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::build(self.0).finish()
}
}
impl From<StatusCode> for Status {
fn from(code: StatusCode) -> Self {
Status(code)
}
}
pub struct Response {
status_code: StatusCode,
headers: HashMap<String, String>,
body: Option<String>,
}
impl Response {
pub fn new() -> Self {
Self {
status_code: StatusCode::OK,
headers: HashMap::new(),
body: None,
}
}
pub fn status(mut self, status: StatusCode) -> Self {
self.status_code = status;
self
}
pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.insert(key.into(), value.into());
self
}
pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
self.headers.insert("Content-Type".to_string(), content_type.into());
self
}
pub fn body<T: AsRef<str>>(mut self, body: T) -> Self {
self.body = Some(body.as_ref().to_string());
self
}
pub fn json<T: Serialize>(mut self, body: &T) -> Result<Self, serde_json::Error> {
match serde_json::to_string(body) {
Ok(json) => {
self.body = Some(json);
if !self.headers.contains_key("Content-Type") {
self.headers.insert("Content-Type".to_string(), "application/json".to_string());
}
Ok(self)
}
Err(e) => Err(e),
}
}
}
impl Default for Response {
fn default() -> Self {
Self::new()
}
}
impl Responder for Response {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
let mut response = HttpResponse::build(self.status_code);
for (key, value) in &self.headers {
if let Ok(header_name) = header::HeaderName::from_bytes(key.as_bytes()) {
response.insert_header((header_name, value.as_str()));
}
}
if let Some(body) = self.body {
response.body(body)
} else {
response.finish()
}
}
}