page_hunter/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
//! ***Page Hunter*** library is a Rust-based pagination tool that provides a way to manage and navigate through pages of data.
//! It offers a set of resources that encapsulates all the necessary pagination information such as the current page, total pages, previous page, next page and the items on the current page.
//!
//! The library also includes validation methods to ensure the integrity of the pagination data.
//! It's designed to be flexible and easy to integrate into any Rust project that requires pagination functionality standard data validation
//!
//! ## CRATE FEATURES
//! - `serde`: Add [Serialize](https://docs.rs/serde/1.0.200/serde/trait.Serialize.html) and [Deserialize](https://docs.rs/serde/1.0.200/serde/trait.Deserialize.html) support for [`Page`] and [`Book`] based on [serde](https://crates.io/crates/serde/1.0.200). This feature is useful for implementing pagination models as a request or response body in REST APIs, among other implementations.
//! - `utoipa`: Add [ToSchema](https://docs.rs/utoipa/4.2.0/utoipa/trait.ToSchema.html) support for [`Page`] and [`Book`] based on [utoipa](https://crates.io/crates/utoipa/4.2.0). This feature is useful for generating OpenAPI schemas for pagination models. This feature depends on the `serde` feature and therefore you only need to implement `utoipa` to get both.
//! - `sqlx`: Add support for pagination with [SQLx](https://docs.rs/sqlx/0.8.1/sqlx/) for Postgres, MySQL and SQLite databases.
//!
//! ## BASIC OPERATION
//!
//! The **page-hunter** library provides two main models to manage pagination:
//! - [`Page`]: Represents a page of records with the current page, total pages, previous page, next page, and the items on the current page.
//! - [`Book`]: Represents a book of pages with a collection of [`Page`] instances.
//!
//! The library also provides a set of functions to paginate records into a [`Page`] model and bind records into a [`Book`] model. The following examples show how to use the **page-hunter** library:
//!
//! #### Paginate records:
//! If you need to paginate records and get a specific [`Page`]:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let records: Vec<u32> = vec![1, 2, 3, 4, 5];
//! let page: usize = 0;
//! let size: usize = 2;
//!
//! let pagination_result: PaginationResult<Page<u32>> =
//! paginate_records(&records, page, size);
//! ```
//!
//! To create a new `Page` instance from known parameters:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let items: Vec<u32> = vec![1, 2];
//! let page: usize = 0;
//! let size: usize = 2;
//! let total_elements: usize = 5;
//!
//! let page_model_result: PaginationResult<Page<u32>> = Page::new(
//! &items,
//! page,
//! size,
//! total_elements,
//! );
//! ```
//!
//! On feature `serde` enabled, you can serialize and deserialize a [`Page`] as follows:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let items: Vec<u32> = vec![1, 2];
//! let page: usize = 0;
//! let size: usize = 2;
//! let total_elements: usize = 5;
//!
//! let page_model: Page<u32> = Page::new(
//! &items,
//! page,
//! size,
//! total_elements,
//! ).unwrap_or_else(|error| {
//! panic!("Error creating page model: {:?}", error);
//! });
//!
//! let serialized_page: String = serde_json::to_string(&page_model).unwrap_or_else(|error| {
//! panic!("Error serializing page model: {:?}", error);
//! });
//!
//! let deserialized_page: Page<u32> = serde_json::from_str(&serialized_page).unwrap_or_else(|error| {
//! panic!("Error deserializing page model: {:?}", error);
//! });
//! ```
//!
//! When you create a new [`Page`] instance from the constructor or deserialization, the following rules are validated for the fields on the page:
//! - ***pages*** must be equal to ***total*** divided by ***size*** rounded up. When ***size*** is 0, ***pages*** must be 1.
//! - ***page*** must be less than or equal to ***pages*** - 1.
//! - if ***page*** is less than ***pages*** - 1, ***items*** length must be equal to ***size***.
//! - if ***page*** is equal to ***pages*** - 1, ***total*** must be equal to (***pages*** - 1) * ***size*** + ***items*** length.
//! - ***previous_page*** must be equal to ***page*** - 1 if ***page*** is greater than 0, otherwise it must be [`None`].
//! - ***next_page*** must be equal to ***page*** + 1 if ***page*** is less than ***pages*** - 1, otherwise it must be [`None`].
//!
//! If any of these rules are violated, a [`PaginationError`] will be returned.
//!
//! #### Bind records:
//! If you need to bind records into a [`Book`] model:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let records: Vec<u32> = vec![1, 2, 3, 4, 5];
//! let size: usize = 2;
//!
//! let book_result: PaginationResult<Book<u32>> =
//! bind_records(&records, size);
//! ```
//!
//! To create a new [`Book`] instance from known parameters:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let sheets: Vec<Page<u32>> = vec![
//! Page::new(&vec![1, 2], 0, 2, 5).unwrap(),
//! Page::new(&vec![3, 4], 1, 2, 5).unwrap(),
//! ];
//!
//! let book: Book<u32> = Book::new(&sheets);
//! ```
//!
//! On feature `serde` enabled, you can serialize and deserialize a [`Book`] as follows:
//! ```rust,no_run
//! use page_hunter::*;
//!
//! let sheets: Vec<Page<u32>> = vec![
//! Page::new(&vec![1, 2], 0, 2, 5).unwrap(),
//! Page::new(&vec![3, 4], 1, 2, 5).unwrap(),
//! ];
//!
//! let book: Book<u32> = Book::new(&sheets);
//!
//! let serialized_book: String = serde_json::to_string(&book).unwrap_or_else(|error| {
//! panic!("Error serializing book model: {:?}", error);
//! });
//!
//! let deserialized_book: Book<u32> = serde_json::from_str(&serialized_book).unwrap_or_else(|error| {
//! panic!("Error deserializing book model: {:?}", error);
//! });
//! ```
//!
//! #### Generate OpenAPI schemas:
//! On feature `utoipa` enabled, you can generate OpenAPI schemas for [`Page`] and [`Book`] models as follows:
//!
//! ```rust,no_run
//! use page_hunter::*;
//! use utoipa::{OpenApi, ToSchema};
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, ToSchema)]
//! pub struct Person {
//! id: u16,
//! name: String,
//! last_name: String,
//! still_alive: bool,
//! }
//!
//! pub type PeoplePage = Page<Person>;
//! pub type PeopleBook = Book<Person>;
//!
//! #[derive(OpenApi)]
//! #[openapi(
//! components(schemas(PeoplePage, PeopleBook))
//! )]
//! pub struct ApiDoc;
//! ```
//!
//! Take a look at the [examples](https://github.com/JMTamayo/page-hunter/tree/main/examples) folder where you can find practical uses in REST API implementations with some web frameworks.
//!
//! #### Paginate records from a relational database with SQLx:
//! To paginate records from a Postgres database:
//! ```rust,no_run
//! use page_hunter::*;
//! use sqlx::postgres::{PgPool, Postgres};
//! use sqlx::{FromRow, QueryBuilder};
//! use uuid::Uuid;
//!
//! #[tokio::main]
//! async fn main() {
//! #[derive(Clone, Debug, FromRow)]
//! pub struct Country {
//! id: Uuid,
//! name: String,
//! }
//!
//! let pool: PgPool = PgPool::connect(
//! "postgres://username:password@localhost/db"
//! ).await.unwrap_or_else(|error| {
//! panic!("Error connecting to database: {:?}", error);
//! });
//!
//! let query: QueryBuilder<Postgres> = QueryBuilder::new(
//! "SELECT * FROM db.geo.countries"
//! );
//!
//! let page: Page<Country> =
//! query.paginate(&pool, 0, 10).await.unwrap_or_else(|error| {
//! panic!("Error paginating records: {:?}", error);
//! });
//! }
//! ```
mod book;
mod errors;
mod page;
mod pagination;
mod results;
pub use book::Book;
pub use errors::{ErrorKind, PaginationError};
pub use page::Page;
pub use pagination::records::{bind_records, paginate_records};
pub use results::PaginationResult;
#[cfg(feature = "sqlx")]
pub use pagination::sqlx::queries::SQLxPagination;