use serde::{Deserialize, Serialize};
#[cfg(feature = "utoipa")]
use utoipa::ToSchema;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct Paged<T> {
pub items: Vec<T>,
pub total: i64,
pub skip: i64,
pub limit: i64,
pub page: i64,
pub page_size: i64,
}
impl<T> Paged<T> {
#[must_use]
pub fn new(items: Vec<T>, total: i64, skip: i64, limit: i64) -> Self {
Self {
items,
total,
skip,
limit,
page: if limit > 0 { (skip / limit).abs() } else { 0 },
page_size: limit,
}
}
#[must_use]
pub fn map<U, F: FnMut(T) -> U>(self, f: F) -> Paged<U> {
Paged {
items: self.items.into_iter().map(f).collect(),
total: self.total,
skip: self.skip,
limit: self.limit,
page: self.page,
page_size: self.page_size,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derives_page_from_skip_and_limit() {
let p = Paged::new(vec![1, 2, 3], 137, 20, 10);
assert_eq!(p.skip, 20);
assert_eq!(p.limit, 10);
assert_eq!(p.page, 2);
assert_eq!(p.page_size, 10);
}
#[test]
fn keeps_exact_skip_when_not_a_multiple_of_limit() {
let p = Paged::new(vec![1], 137, 25, 10);
assert_eq!(p.skip, 25);
assert_eq!(p.limit, 10);
assert_eq!(p.page, 2);
}
#[test]
fn zero_limit_does_not_divide_by_zero() {
let p = Paged::new(Vec::<i32>::new(), 0, 0, 0);
assert_eq!(p.page, 0);
assert_eq!(p.page_size, 0);
}
#[test]
fn map_preserves_counters() {
let p = Paged::new(vec![1, 2], 7, 5, 2).map(|i| i.to_string());
assert_eq!(p.items, vec!["1".to_string(), "2".to_string()]);
assert_eq!(p.total, 7);
assert_eq!(p.skip, 5);
assert_eq!(p.limit, 2);
assert_eq!(p.page, 2);
assert_eq!(p.page_size, 2);
}
}