paginator_rs/
lib.rs

1use paginator_utils;
2pub use paginator_utils::*;
3
4use serde::Serialize;
5use serde_json::{Value, to_value};
6use std::error::Error;
7use std::fmt;
8
9/// Errors that can occur during pagination
10#[derive(Debug)]
11pub enum PaginatorError {
12    /// Invalid page number (must be >= 1)
13    InvalidPage(u32),
14    /// Invalid per_page value (must be between 1 and max limit)
15    InvalidPerPage(u32),
16    /// Serialization error
17    SerializationError(String),
18    /// Custom error with message
19    Custom(String),
20}
21
22impl fmt::Display for PaginatorError {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            PaginatorError::InvalidPage(page) => {
26                write!(f, "Invalid page number: {}. Page must be >= 1", page)
27            }
28            PaginatorError::InvalidPerPage(per_page) => {
29                write!(
30                    f,
31                    "Invalid per_page value: {}. Must be between 1 and 100",
32                    per_page
33                )
34            }
35            PaginatorError::SerializationError(msg) => {
36                write!(f, "Serialization error: {}", msg)
37            }
38            PaginatorError::Custom(msg) => write!(f, "{}", msg),
39        }
40    }
41}
42
43impl Error for PaginatorError {}
44
45/// Result type for pagination operations
46pub type PaginatorResult<T> = Result<T, PaginatorError>;
47
48/// Main trait for implementing pagination on collections
49pub trait PaginatorTrait<T>
50where
51    T: Serialize,
52{
53    /// Paginate the collection with given parameters
54    fn paginate(&self, params: &PaginationParams) -> PaginatorResult<PaginatorResponse<T>> {
55        // Validate parameters
56        if params.page < 1 {
57            return Err(PaginatorError::InvalidPage(params.page));
58        }
59        if params.per_page < 1 || params.per_page > 100 {
60            return Err(PaginatorError::InvalidPerPage(params.per_page));
61        }
62
63        Ok(PaginatorResponse {
64            data: vec![],
65            meta: PaginatorResponseMeta::new(0, params.per_page, 0),
66        })
67    }
68
69    /// Paginate and return as JSON Value
70    fn paginate_json(&self, params: &PaginationParams) -> PaginatorResult<Value> {
71        let response = self.paginate(params)?;
72        to_value(response)
73            .map_err(|e| PaginatorError::SerializationError(e.to_string()))
74    }
75}
76
77/// Builder for creating pagination parameters with fluent API
78pub struct PaginatorBuilder {
79    params: PaginationParams,
80}
81
82impl Default for PaginatorBuilder {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88impl PaginatorBuilder {
89    /// Create a new builder with default parameters
90    pub fn new() -> Self {
91        Self {
92            params: PaginationParams::default(),
93        }
94    }
95
96    /// Set the page number (1-indexed)
97    pub fn page(mut self, page: u32) -> Self {
98        self.params.page = page.max(1);
99        self
100    }
101
102    /// Set the number of items per page
103    pub fn per_page(mut self, per_page: u32) -> Self {
104        self.params.per_page = per_page.max(1).min(100);
105        self
106    }
107
108    /// Set the sorting field
109    pub fn sort_by(mut self, field: impl Into<String>) -> Self {
110        self.params.sort_by = Some(field.into());
111        self
112    }
113
114    /// Set ascending sort direction
115    pub fn sort_asc(mut self) -> Self {
116        self.params.sort_direction = Some(SortDirection::Asc);
117        self
118    }
119
120    /// Set descending sort direction
121    pub fn sort_desc(mut self) -> Self {
122        self.params.sort_direction = Some(SortDirection::Desc);
123        self
124    }
125
126    /// Add a custom filter
127    pub fn filter(mut self, field: impl Into<String>, operator: FilterOperator, value: FilterValue) -> Self {
128        self.params.filters.push(Filter::new(field, operator, value));
129        self
130    }
131
132    /// Add an equals filter (field = value)
133    pub fn filter_eq(mut self, field: impl Into<String>, value: FilterValue) -> Self {
134        self.params.filters.push(Filter::new(field, FilterOperator::Eq, value));
135        self
136    }
137
138    /// Add a not equals filter (field != value)
139    pub fn filter_ne(mut self, field: impl Into<String>, value: FilterValue) -> Self {
140        self.params.filters.push(Filter::new(field, FilterOperator::Ne, value));
141        self
142    }
143
144    /// Add a greater than filter (field > value)
145    pub fn filter_gt(mut self, field: impl Into<String>, value: FilterValue) -> Self {
146        self.params.filters.push(Filter::new(field, FilterOperator::Gt, value));
147        self
148    }
149
150    /// Add a less than filter (field < value)
151    pub fn filter_lt(mut self, field: impl Into<String>, value: FilterValue) -> Self {
152        self.params.filters.push(Filter::new(field, FilterOperator::Lt, value));
153        self
154    }
155
156    /// Add a greater than or equal filter (field >= value)
157    pub fn filter_gte(mut self, field: impl Into<String>, value: FilterValue) -> Self {
158        self.params.filters.push(Filter::new(field, FilterOperator::Gte, value));
159        self
160    }
161
162    /// Add a less than or equal filter (field <= value)
163    pub fn filter_lte(mut self, field: impl Into<String>, value: FilterValue) -> Self {
164        self.params.filters.push(Filter::new(field, FilterOperator::Lte, value));
165        self
166    }
167
168    /// Add a LIKE filter (field LIKE pattern)
169    pub fn filter_like(mut self, field: impl Into<String>, pattern: impl Into<String>) -> Self {
170        self.params.filters.push(Filter::new(
171            field,
172            FilterOperator::Like,
173            FilterValue::String(pattern.into()),
174        ));
175        self
176    }
177
178    /// Add a case-insensitive LIKE filter (field ILIKE pattern)
179    pub fn filter_ilike(mut self, field: impl Into<String>, pattern: impl Into<String>) -> Self {
180        self.params.filters.push(Filter::new(
181            field,
182            FilterOperator::ILike,
183            FilterValue::String(pattern.into()),
184        ));
185        self
186    }
187
188    /// Add an IN filter (field IN array)
189    pub fn filter_in(mut self, field: impl Into<String>, values: Vec<FilterValue>) -> Self {
190        self.params.filters.push(Filter::new(
191            field,
192            FilterOperator::In,
193            FilterValue::Array(values),
194        ));
195        self
196    }
197
198    /// Add a BETWEEN filter (field BETWEEN min AND max)
199    pub fn filter_between(mut self, field: impl Into<String>, min: FilterValue, max: FilterValue) -> Self {
200        self.params.filters.push(Filter::new(
201            field,
202            FilterOperator::Between,
203            FilterValue::Array(vec![min, max]),
204        ));
205        self
206    }
207
208    /// Add an IS NULL filter
209    pub fn filter_is_null(mut self, field: impl Into<String>) -> Self {
210        self.params.filters.push(Filter::new(
211            field,
212            FilterOperator::IsNull,
213            FilterValue::Null,
214        ));
215        self
216    }
217
218    /// Add an IS NOT NULL filter
219    pub fn filter_is_not_null(mut self, field: impl Into<String>) -> Self {
220        self.params.filters.push(Filter::new(
221            field,
222            FilterOperator::IsNotNull,
223            FilterValue::Null,
224        ));
225        self
226    }
227
228    /// Add search parameters
229    pub fn search(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
230        self.params.search = Some(SearchParams::new(query, fields));
231        self
232    }
233
234    /// Add exact match search
235    pub fn search_exact(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
236        self.params.search = Some(SearchParams::new(query, fields).with_exact_match(true));
237        self
238    }
239
240    /// Add case-sensitive search
241    pub fn search_case_sensitive(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
242        self.params.search = Some(SearchParams::new(query, fields).with_case_sensitive(true));
243        self
244    }
245
246    /// Build the final PaginationParams
247    pub fn build(self) -> PaginationParams {
248        self.params
249    }
250}