fedimovies/src/mastodon_api/pagination.rs
2023-04-24 17:35:32 +02:00

82 lines
2.2 KiB
Rust

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<impl Serialize>,
maybe_last_item_id: Option<impl ToString>,
) -> 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<u16> for PageSize {
type Error = &'static str;
fn try_from(value: u16) -> Result<Self, Self::Error> {
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#"<https://example.org/api/v1/notifications?max_id=123>; 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::<PageSize>("0").unwrap_err();
assert_eq!(error.to_string(), expected_error);
let error = serde_json::from_str::<PageSize>("201").unwrap_err();
assert_eq!(error.to_string(), expected_error);
}
}