use crate::{
http::{Body, ResponseBuilder, Status},
rpc::{typed_data::Data, RpcHttp, TypedData},
};
use std::collections::HashMap;
#[derive(Default, Debug)]
pub struct HttpResponse {
pub(crate) data: RpcHttp,
pub(crate) status: Status,
}
impl HttpResponse {
pub(crate) fn new() -> Self {
HttpResponse {
data: RpcHttp::default(),
status: Status::Ok,
}
}
pub fn build() -> ResponseBuilder {
ResponseBuilder::new()
}
pub fn status(&self) -> Status {
self.status
}
pub fn body(&self) -> Body {
self.data
.body
.as_ref()
.map(|b| Body::from(&**b))
.unwrap_or(Body::Empty)
}
pub fn headers(&self) -> &HashMap<String, String> {
&self.data.headers
}
}
impl<'a, T> From<T> for HttpResponse
where
T: Into<Body<'a>>,
{
fn from(data: T) -> Self {
HttpResponse::build().body(data).finish()
}
}
#[doc(hidden)]
impl Into<TypedData> for HttpResponse {
fn into(mut self) -> TypedData {
self.data.status_code = self.status.to_string();
TypedData {
data: Some(Data::Http(Box::new(self.data))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use matches::matches;
use serde::{Deserialize, Serialize};
#[test]
fn it_is_empty_by_default() {
let response = HttpResponse::new();
assert_eq!(response.status(), Status::Ok);
assert!(matches!(response.body(), Body::Empty));
}
#[test]
fn it_is_empty_from_a_builder() {
let response: HttpResponse = HttpResponse::build().finish();
assert_eq!(response.status(), Status::Ok);
assert!(matches!(response.body(), Body::Empty));
}
#[test]
fn it_builds_with_a_status() {
let response: HttpResponse = HttpResponse::build().status(Status::Continue).finish();
assert_eq!(response.status(), Status::Continue);
}
#[test]
fn it_builds_with_a_string_body() {
const BODY: &'static str = "test body";
let response: HttpResponse = HttpResponse::build().body(BODY).finish();
assert_eq!(
response.headers().get("Content-Type").unwrap(),
"text/plain"
);
assert_eq!(response.body().as_str().unwrap(), BODY);
}
#[test]
fn it_builds_with_a_json_body() {
#[derive(Serialize, Deserialize)]
struct Data {
message: String,
};
const MESSAGE: &'static str = "test";
let data = Data {
message: MESSAGE.to_string(),
};
let response = HttpResponse::build()
.body(::serde_json::to_value(data).unwrap())
.finish();
assert_eq!(
response.headers().get("Content-Type").unwrap(),
"application/json"
);
assert_eq!(response.body().as_json::<Data>().unwrap().message, MESSAGE);
}
#[test]
fn it_builds_with_a_bytes_body() {
const BODY: &'static [u8] = &[1, 2, 3];
let response: HttpResponse = HttpResponse::build().body(BODY).finish();
assert_eq!(
response.headers().get("Content-Type").unwrap(),
"application/octet-stream"
);
assert_eq!(response.body().as_bytes(), BODY);
}
#[test]
fn it_builds_with_headers() {
let response: HttpResponse = HttpResponse::build()
.header("header1", "value1")
.header("header2", "value2")
.header("header3", "value3")
.finish();
assert_eq!(response.headers().get("header1").unwrap(), "value1");
assert_eq!(response.headers().get("header2").unwrap(), "value2");
assert_eq!(response.headers().get("header3").unwrap(), "value3");
}
#[test]
fn it_converts_to_typed_data() {
let response: HttpResponse = HttpResponse::build()
.status(Status::BadRequest)
.header("header", "value")
.body("body")
.finish();
let data: TypedData = response.into();
match data.data {
Some(Data::Http(http)) => {
assert_eq!(http.status_code, "400");
assert_eq!(http.headers.get("header").unwrap(), "value");
assert_eq!(
http.body,
Some(Box::new(TypedData {
data: Some(Data::String("body".to_string()))
}))
);
}
_ => assert!(false),
}
}
}