use crate::comm::ExecuteContext;
use crate::errors::Result;
use crate::interceptor::shared::InterceptorBase;
use crate::interceptor::{InterceptorType, OperationType};
#[derive(Debug, Clone)]
pub struct PaginationRequest {
pub page: u64,
pub size: u64,
pub count_total: bool,
}
impl PaginationRequest {
pub fn new(page: u64, size: u64) -> Self {
Self {
page,
size,
count_total: true,
}
}
pub fn without_count(mut self) -> Self {
self.count_total = false;
self
}
pub fn offset(&self) -> u64 {
if self.page > 0 {
(self.page - 1) * self.size
} else {
0
}
}
}
#[derive(Debug, Clone)]
pub struct PaginationResult {
pub page: u64,
pub size: u64,
pub total: Option<u64>,
pub pages: Option<u64>,
}
impl PaginationResult {
pub fn new(page: u64, size: u64, total: Option<u64>) -> Self {
let pages = total.map(|t| if size > 0 { (t + size - 1) / size } else { 0 });
Self {
page,
size,
total,
pages,
}
}
}
pub struct PaginationInterceptor {
default_page_size: u64,
max_page_size: u64,
}
impl Default for PaginationInterceptor {
fn default() -> Self {
Self::new()
}
}
impl PaginationInterceptor {
pub fn new() -> Self {
Self {
default_page_size: 10,
max_page_size: 1000,
}
}
pub fn with_default_page_size(mut self, size: u64) -> Self {
self.default_page_size = size;
self
}
pub fn with_max_page_size(mut self, size: u64) -> Self {
self.max_page_size = size;
self
}
fn normalize_request(&self, req: &PaginationRequest) -> PaginationRequest {
let size = if req.size == 0 {
self.default_page_size
} else {
req.size.min(self.max_page_size)
};
PaginationRequest {
page: req.page.max(1),
size,
count_total: req.count_total,
}
}
}
impl InterceptorBase for PaginationInterceptor {
fn name(&self) -> &'static str {
"pagination"
}
fn interceptor_type(&self) -> InterceptorType {
InterceptorType::Pagination
}
fn order(&self) -> i32 {
30 }
fn supports_operation(&self, operation: &OperationType) -> bool {
matches!(operation, OperationType::Select)
}
}
#[cfg(any(
feature = "mysql-sync",
feature = "postgres-sync",
feature = "sqlite-sync",
feature = "oracle-sync",
feature = "mssql-sync"
))]
impl crate::interceptor::blocking::AkitaInterceptor for PaginationInterceptor {
fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let pagination_json = ctx.get_metadata("pagination");
if let Some(_pagination) = pagination_json {
ctx.set_metadata("pagination_active".to_string(), "true".to_string());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pagination_request() {
let req = PaginationRequest::new(2, 10);
assert_eq!(req.page, 2);
assert_eq!(req.size, 10);
assert_eq!(req.offset(), 10);
assert!(req.count_total);
}
#[test]
fn test_pagination_request_first_page() {
let req = PaginationRequest::new(1, 20);
assert_eq!(req.offset(), 0);
}
#[test]
fn test_pagination_request_without_count() {
let req = PaginationRequest::new(1, 10).without_count();
assert!(!req.count_total);
}
#[test]
fn test_pagination_result() {
let result = PaginationResult::new(2, 10, Some(25));
assert_eq!(result.page, 2);
assert_eq!(result.size, 10);
assert_eq!(result.total, Some(25));
assert_eq!(result.pages, Some(3));
}
#[test]
fn test_pagination_result_no_total() {
let result = PaginationResult::new(1, 10, None);
assert_eq!(result.total, None);
assert_eq!(result.pages, None);
}
#[test]
fn test_pagination_interceptor() {
let interceptor = PaginationInterceptor::new();
assert_eq!(interceptor.name(), "pagination");
assert_eq!(interceptor.interceptor_type(), InterceptorType::Pagination);
assert_eq!(interceptor.order(), 30);
assert!(interceptor.supports_operation(&OperationType::Select));
assert!(!interceptor
.supports_operation(&OperationType::Insert(akita_core::InsertType::SingleInsert)));
}
#[test]
fn test_normalize_request() {
let interceptor = PaginationInterceptor::new()
.with_default_page_size(20)
.with_max_page_size(100);
let req = PaginationRequest::new(1, 0);
let normalized = interceptor.normalize_request(&req);
assert_eq!(normalized.size, 20);
let req = PaginationRequest::new(1, 200);
let normalized = interceptor.normalize_request(&req);
assert_eq!(normalized.size, 100);
let req = PaginationRequest::new(0, 10);
let normalized = interceptor.normalize_request(&req);
assert_eq!(normalized.page, 1);
}
}