use crate::{HyperResponse, Result};
use bytes::Bytes;
use http_body_util::Full;
use hyper::StatusCode;
use hyper::header;
static CONTENT_TYPE_JSON: header::HeaderValue =
header::HeaderValue::from_static("application/json");
static CONTENT_TYPE_TEXT: header::HeaderValue =
header::HeaderValue::from_static("text/plain; charset=utf-8");
#[allow(dead_code)]
static CONTENT_TYPE_OCTET: header::HeaderValue =
header::HeaderValue::from_static("application/octet-stream");
pub struct Response {
pub inner: HyperResponse,
}
impl Response {
fn new(response: HyperResponse) -> Self {
Self { inner: response }
}
pub fn status(&self) -> StatusCode {
self.inner.status()
}
pub fn body<T>(body: T) -> Result<Self>
where
Bytes: From<T>,
{
let response = hyper::http::Response::builder()
.body(Full::new(Bytes::from(body)))?
.into();
Ok(response)
}
pub fn with_status(status: u16, val: String) -> Result<Self> {
let response = hyper::http::Response::builder()
.header(header::CONTENT_TYPE, CONTENT_TYPE_TEXT.clone())
.status(StatusCode::from_u16(status)?)
.body(Full::new(Bytes::from(val)))?
.into();
Ok(response)
}
pub fn json<T>(payload: T) -> Result<Self>
where
T: serde::Serialize + Sized + Send + Sync + 'static,
{
let data = serde_json::to_vec(&payload)?;
let response = hyper::http::Response::builder()
.header(header::CONTENT_TYPE, CONTENT_TYPE_JSON.clone())
.body(Full::new(Bytes::from(data)))?
.into();
Ok(response)
}
pub fn redirect(status: u16, url: &str) -> Result<Self> {
let response = hyper::http::Response::builder()
.status(hyper::StatusCode::from_u16(status)?)
.header(header::LOCATION, url)
.body(Full::new(Bytes::default()))?
.into();
Ok(response)
}
}
impl From<HyperResponse> for Response {
fn from(response: HyperResponse) -> Self {
Response::new(response)
}
}
impl From<()> for Response {
fn from(_: ()) -> Self {
hyper::http::Response::builder()
.header(header::CONTENT_TYPE, &CONTENT_TYPE_TEXT)
.body(Full::new(Bytes::default()))
.unwrap()
.into()
}
}
impl From<String> for Response {
fn from(val: String) -> Self {
hyper::http::Response::builder()
.header(header::CONTENT_TYPE, &CONTENT_TYPE_TEXT)
.body(Full::new(Bytes::from(val)))
.unwrap()
.into()
}
}
impl From<&'static str> for Response {
fn from(val: &'static str) -> Self {
hyper::http::Response::builder()
.header(header::CONTENT_TYPE, &CONTENT_TYPE_TEXT)
.body(Full::new(Bytes::from(val)))
.unwrap()
.into()
}
}
impl From<anyhow::Error> for Response {
fn from(e: anyhow::Error) -> Self {
Response::with_status(500, e.to_string()).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_response_status() {
let response = Response::with_status(200, "OK".to_string()).unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_response_with_status() {
let response = Response::with_status(404, "Not Found".to_string()).unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_response_body() {
let response = Response::body("Hello").unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_response_json() {
#[derive(serde::Serialize)]
struct TestData {
name: String,
value: i32,
}
let data = TestData {
name: "test".to_string(),
value: 42,
};
let response = Response::json(data).unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_response_redirect() {
let response = Response::redirect(302, "/redirected").unwrap();
assert_eq!(response.status(), StatusCode::FOUND);
}
#[test]
fn test_response_from_string() {
let response: Response = "hello".into();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_response_from_static_str() {
let response: Response = "static".into();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_response_from_unit() {
let response: Response = ().into();
assert_eq!(response.status(), StatusCode::OK);
}
}