use serde::de::DeserializeOwned;
use serde_json::Value;
use crate::pagination::PaginationInfo;
pub struct SimklResponse {
pub status_code: u16,
pub headers: std::collections::HashMap<String, String>,
pub body: String,
}
impl SimklResponse {
pub fn new(
status_code: u16,
headers: std::collections::HashMap<String, String>,
body: String,
) -> Self {
Self {
status_code,
headers,
body,
}
}
pub fn json<T: DeserializeOwned>(&self) -> crate::error::Result<T> {
Ok(serde_json::from_str(&self.body)?)
}
pub fn json_value(&self) -> crate::error::Result<Value> {
Ok(serde_json::from_str(&self.body)?)
}
pub fn is_success(&self) -> bool {
(200..300).contains(&self.status_code)
}
pub fn is_client_error(&self) -> bool {
(400..500).contains(&self.status_code)
}
pub fn is_server_error(&self) -> bool {
(500..600).contains(&self.status_code)
}
pub fn pagination_info(&self) -> Option<PaginationInfo> {
let get = |key: &str| -> Option<u32> { self.headers.get(key).and_then(|v| v.parse().ok()) };
let current_page = get("X-Pagination-Page")?;
let items_per_page = get("X-Pagination-Limit").unwrap_or(10);
let total_pages = get("X-Pagination-Page-Count").unwrap_or(1);
let total_items = get("X-Pagination-Item-Count").unwrap_or(0);
Some(PaginationInfo {
current_page,
total_pages,
total_items,
items_per_page,
has_next: current_page < total_pages,
has_prev: current_page > 1,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
fn make_response(headers: Vec<(&str, &str)>) -> SimklResponse {
let map: HashMap<String, String> = headers
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
SimklResponse::new(200, map, "[]".to_string())
}
#[test]
fn test_pagination_info_present() {
let resp = make_response(vec![
("X-Pagination-Page", "2"),
("X-Pagination-Limit", "10"),
("X-Pagination-Page-Count", "5"),
("X-Pagination-Item-Count", "47"),
]);
let info = resp.pagination_info().expect("should have pagination");
assert_eq!(info.current_page, 2);
assert_eq!(info.items_per_page, 10);
assert_eq!(info.total_pages, 5);
assert_eq!(info.total_items, 47);
assert!(info.has_next);
assert!(info.has_prev);
}
#[test]
fn test_pagination_info_first_page() {
let resp = make_response(vec![
("X-Pagination-Page", "1"),
("X-Pagination-Limit", "10"),
("X-Pagination-Page-Count", "3"),
("X-Pagination-Item-Count", "25"),
]);
let info = resp.pagination_info().unwrap();
assert!(!info.has_prev);
assert!(info.has_next);
}
#[test]
fn test_pagination_info_last_page() {
let resp = make_response(vec![
("X-Pagination-Page", "3"),
("X-Pagination-Limit", "10"),
("X-Pagination-Page-Count", "3"),
("X-Pagination-Item-Count", "25"),
]);
let info = resp.pagination_info().unwrap();
assert!(info.has_prev);
assert!(!info.has_next);
}
#[test]
fn test_pagination_info_absent() {
let resp = make_response(vec![]);
assert!(resp.pagination_info().is_none());
}
}