road-runner-common 0.7.1

Shared Rust utilities for exchange ecosystem backend services.
Documentation
use serde::{Deserialize, Serialize};

#[cfg(feature = "openapi")]
use utoipa::ToSchema;

#[derive(Debug, Clone, Copy, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema, utoipa::IntoParams))]
pub struct PageRequest {
    #[serde(default = "default_page")]
    pub page: u32, // 1'den başlar

    #[serde(default = "default_size")]
    pub size: u32,
}

fn default_page() -> u32 {
    1
}
fn default_size() -> u32 {
    10
}

impl Default for PageRequest {
    fn default() -> Self {
        Self {
            page: default_page(),
            size: default_size(),
        }
    }
}

impl PageRequest {
    pub fn normalized(self, max_page_size: u32) -> Self {
        let size = self.size.clamp(1, max_page_size.max(1));
        let page = self.page.max(1);
        Self { page, size }
    }

    pub fn normalized_with_default(self, default_size: u32, max_page_size: u32) -> Self {
        let max_size = max_page_size.max(1);
        let default_size = default_size.clamp(1, max_size);
        let size = if self.size == 0 {
            default_size
        } else {
            self.size.clamp(1, max_size)
        };
        let page = self.page.max(1);
        Self { page, size }
    }

    pub fn offset_limit(self) -> (u32, u32) {
        let page = self.page.max(1);
        let size = self.size.max(1);
        let offset = (page - 1).saturating_mul(size);
        (offset, size)
    }
}

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

    #[test]
    fn normalized_with_default_uses_default_for_zero_size() {
        let page = PageRequest { page: 0, size: 0 }.normalized_with_default(100, 500);

        assert_eq!(page.page, 1);
        assert_eq!(page.size, 100);
    }

    #[test]
    fn normalized_with_default_clamps_to_max_size() {
        let page = PageRequest {
            page: 2,
            size: 1_000,
        }
        .normalized_with_default(100, 500);

        assert_eq!(page.page, 2);
        assert_eq!(page.size, 500);
    }

    #[test]
    fn normalized_with_default_keeps_minimum_size_safe() {
        let page = PageRequest { page: 1, size: 1 }.normalized_with_default(0, 0);

        assert_eq!(page.page, 1);
        assert_eq!(page.size, 1);
    }

    #[test]
    fn offset_limit_is_one_based() {
        let (offset, limit) = PageRequest { page: 3, size: 25 }.offset_limit();

        assert_eq!(offset, 50);
        assert_eq!(limit, 25);
    }
}

#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct Paged<T> {
    pub items: Vec<T>,
    pub total_items: u64,
    pub page: u32,
    pub size: u32,
    pub total_pages: u32,
}

impl<T> Paged<T> {
    pub fn new(items: Vec<T>, total_items: u64, page: u32, size: u32) -> Self {
        let size = size.max(1);
        let total_pages = if total_items == 0 {
            0
        } else {
            ((total_items + size as u64 - 1) / size as u64) as u32
        };

        Self {
            items,
            total_items,
            page: page.max(1),
            size,
            total_pages,
        }
    }
}