use actix_web::HttpResponse; use serde::{Deserialize, Serialize}; fn get_pagination_header(instance_url: &str, path: &str, last_id: &str) -> String { let next_page_url = format!("{}{}?max_id={}", instance_url, path, last_id); // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link format!(r#"<{}>; rel="next""#, next_page_url) } pub fn get_paginated_response( instance_url: &str, path: &str, items: Vec, maybe_last_item_id: Option, ) -> HttpResponse { if let Some(last_item_id) = maybe_last_item_id { let pagination_header = get_pagination_header(instance_url, path, &last_item_id.to_string()); HttpResponse::Ok() .append_header(("Link", pagination_header)) .json(items) } else { HttpResponse::Ok().json(items) } } const PAGE_MAX_SIZE: u16 = 200; #[derive(Debug, Deserialize)] #[serde(try_from = "u16")] pub struct PageSize(u16); impl PageSize { pub fn new(size: u16) -> Self { Self(size) } pub fn inner(&self) -> u16 { self.0 } } impl TryFrom for PageSize { type Error = &'static str; fn try_from(value: u16) -> Result { if value > 0 && value <= PAGE_MAX_SIZE { Ok(Self(value)) } else { Err("expected an integer between 0 and 201") } } } #[cfg(test)] mod tests { use super::*; const INSTANCE_URL: &str = "https://example.org"; #[test] fn test_get_next_page_link() { let result = get_pagination_header(INSTANCE_URL, "/api/v1/notifications", "123"); assert_eq!( result, r#"; rel="next""#, ); } #[test] fn test_deserialize_page_size() { let value: PageSize = serde_json::from_str("10").unwrap(); assert_eq!(value.inner(), 10); let expected_error = "expected an integer between 0 and 201"; let error = serde_json::from_str::("0").unwrap_err(); assert_eq!(error.to_string(), expected_error); let error = serde_json::from_str::("201").unwrap_err(); assert_eq!(error.to_string(), expected_error); } }