aro-core 1.0.0

Core domain layer for the Aro web framework
Documentation
//! Pagination primitives for repository queries.

use serde::{Deserialize, Serialize};

/// A request for a specific page of results.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageRequest {
    /// The 1-based page number.
    pub page: u64,
    /// The number of items per page.
    pub per_page: u64,
}

/// A page of results returned from a repository query.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Page<T> {
    /// The items in this page.
    pub items: Vec<T>,
    /// The total number of items across all pages.
    pub total: u64,
    /// The 1-based page number of this page.
    pub page: u64,
    /// The number of items per page.
    pub per_page: u64,
}

impl<T> Page<T> {
    /// Returns the total number of pages.
    pub fn total_pages(&self) -> u64 {
        if self.per_page == 0 {
            return 0;
        }
        self.total.div_ceil(self.per_page)
    }

    /// Returns `true` if there is a page after this one.
    pub fn has_next_page(&self) -> bool {
        self.page < self.total_pages()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn page_total_pages_exact_division() {
        let page: Page<i32> = Page {
            items: vec![1, 2],
            total: 10,
            page: 1,
            per_page: 5,
        };
        assert_eq!(page.total_pages(), 2);
    }

    #[test]
    fn page_total_pages_with_remainder() {
        let page: Page<i32> = Page {
            items: vec![1, 2, 3],
            total: 7,
            page: 1,
            per_page: 3,
        };
        assert_eq!(page.total_pages(), 3);
    }

    #[test]
    fn page_total_pages_zero_per_page() {
        let page: Page<i32> = Page {
            items: vec![],
            total: 10,
            page: 1,
            per_page: 0,
        };
        assert_eq!(page.total_pages(), 0);
    }

    #[test]
    fn page_total_pages_zero_total() {
        let page: Page<i32> = Page {
            items: vec![],
            total: 0,
            page: 1,
            per_page: 10,
        };
        assert_eq!(page.total_pages(), 0);
    }

    #[test]
    fn has_next_page_true() {
        let page: Page<i32> = Page {
            items: vec![1, 2],
            total: 10,
            page: 1,
            per_page: 5,
        };
        assert!(page.has_next_page());
    }

    #[test]
    fn has_next_page_false_on_last_page() {
        let page: Page<i32> = Page {
            items: vec![1],
            total: 10,
            page: 2,
            per_page: 5,
        };
        assert!(!page.has_next_page());
    }

    #[test]
    fn has_next_page_false_when_empty() {
        let page: Page<i32> = Page {
            items: vec![],
            total: 0,
            page: 1,
            per_page: 10,
        };
        assert!(!page.has_next_page());
    }

    #[test]
    fn page_request_construction() {
        let req = PageRequest {
            page: 2,
            per_page: 25,
        };
        assert_eq!(req.page, 2);
        assert_eq!(req.per_page, 25);
    }

    #[test]
    fn page_request_clone_and_eq() {
        let req = PageRequest {
            page: 1,
            per_page: 10,
        };
        let cloned = req.clone();
        assert_eq!(req, cloned);
    }
}