use std::collections::HashMap;
use bytes::Bytes;
#[derive(Debug, Clone)]
pub struct Response {
pub version: String,
pub status: u16,
pub status_text: String,
pub headers: HashMap<String, String>,
pub body: Bytes,
}
impl Response {
pub fn new() -> Self {
Response {
version: "HTTP/1.1".to_string(),
status: 200,
status_text: "OK".to_string(),
headers: HashMap::new(),
body: Bytes::new(),
}
}
pub fn is_success(&self) -> bool {
self.status >= 200 && self.status < 300
}
pub fn is_redirect(&self) -> bool {
self.status >= 300 && self.status < 400
}
pub fn is_client_error(&self) -> bool {
self.status >= 400 && self.status < 500
}
pub fn is_server_error(&self) -> bool {
self.status >= 500 && self.status < 600
}
pub fn text(&self) -> Result<String, std::string::FromUtf8Error> {
String::from_utf8(self.body.to_vec())
}
pub fn bytes(&self) -> &Bytes {
&self.body
}
pub fn json<T: serde::de::DeserializeOwned>(&self) -> crate::Result<T> {
let text = self.text().map_err(|e| crate::ReqresError::Other(format!("UTF-8 error: {}", e)))?;
Ok(serde_json::from_str(&text)?)
}
pub fn status(&self) -> u16 {
self.status
}
pub fn header(&self, name: &str) -> Option<&String> {
let name_lower = name.to_lowercase();
self.headers
.iter()
.find(|(k, _)| k.to_lowercase() == name_lower)
.map(|(_, v)| v)
}
pub fn location(&self) -> Option<&String> {
self.header("Location")
}
}
impl Default for Response {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[test]
fn test_response_new() {
let response = Response::new();
assert_eq!(response.version, "HTTP/1.1");
assert_eq!(response.status, 200);
assert_eq!(response.status_text, "OK");
assert!(response.headers.is_empty());
assert!(response.body.is_empty());
}
#[test]
fn test_response_default() {
let response = Response::default();
assert_eq!(response.version, "HTTP/1.1");
assert_eq!(response.status, 200);
}
#[test]
fn test_is_success() {
let mut response = Response::new();
assert!(response.is_success());
response.status = 100; assert!(!response.is_success());
response.status = 299; assert!(response.is_success());
response.status = 300; assert!(!response.is_success());
}
#[test]
fn test_is_redirect() {
let mut response = Response::new();
response.status = 301;
assert!(response.is_redirect());
response.status = 300;
assert!(response.is_redirect());
response.status = 399;
assert!(response.is_redirect());
response.status = 299;
assert!(!response.is_redirect());
response.status = 400;
assert!(!response.is_redirect());
}
#[test]
fn test_is_client_error() {
let mut response = Response::new();
response.status = 404;
assert!(response.is_client_error());
response.status = 400;
assert!(response.is_client_error());
response.status = 499;
assert!(response.is_client_error());
response.status = 399;
assert!(!response.is_client_error());
response.status = 500;
assert!(!response.is_client_error());
}
#[test]
fn test_is_server_error() {
let mut response = Response::new();
response.status = 500;
assert!(response.is_server_error());
response.status = 503;
assert!(response.is_server_error());
response.status = 599;
assert!(response.is_server_error());
response.status = 499;
assert!(!response.is_server_error());
response.status = 600;
assert!(!response.is_server_error());
}
#[test]
fn test_text() {
let mut response = Response::new();
response.body = Bytes::from("Hello, world!");
let text = response.text().unwrap();
assert_eq!(text, "Hello, world!");
}
#[test]
fn test_text_invalid_utf8() {
let mut response = Response::new();
response.body = Bytes::from(vec![0xFF, 0xFE, 0xFD]);
let result = response.text();
assert!(result.is_err());
}
#[test]
fn test_bytes() {
let mut response = Response::new();
let data: &[u8] = b"Hello, world!";
response.body = Bytes::from(data);
let bytes = response.bytes();
assert_eq!(bytes, data);
}
#[test]
fn test_json() {
#[derive(Deserialize, Debug, PartialEq)]
struct TestData {
name: String,
value: i32,
}
let mut response = Response::new();
response.body = Bytes::from(r#"{"name":"test","value":42}"#);
let result: TestData = response.json().unwrap();
assert_eq!(result.name, "test");
assert_eq!(result.value, 42);
}
#[test]
fn test_json_invalid() {
let mut response = Response::new();
response.body = Bytes::from(r#"{"invalid json"#);
let result: std::result::Result<serde_json::Value, crate::ReqresError> = response.json();
assert!(result.is_err());
}
#[test]
fn test_header() {
let mut response = Response::new();
response.headers.insert("Content-Type".to_string(), "application/json".to_string());
response.headers.insert("Content-Length".to_string(), "100".to_string());
assert_eq!(response.header("Content-Type"), Some(&"application/json".to_string()));
assert_eq!(response.header("content-type"), Some(&"application/json".to_string()));
assert_eq!(response.header("CONTENT-TYPE"), Some(&"application/json".to_string()));
assert_eq!(response.header("Content-Length"), Some(&"100".to_string()));
assert_eq!(response.header("Non-Existent"), None);
}
#[test]
fn test_location() {
let mut response = Response::new();
response.status = 301;
response.headers.insert("Location".to_string(), "https://example.com/new".to_string());
assert_eq!(response.location(), Some(&"https://example.com/new".to_string()));
}
#[test]
fn test_location_missing() {
let mut response = Response::new();
response.status = 301;
assert_eq!(response.location(), None);
}
}