paginator_utils/
params.rs

1use serde::{Deserialize, Serialize};
2use crate::filter::Filter;
3use crate::search::SearchParams;
4use crate::cursor::Cursor;
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "lowercase")]
8pub enum SortDirection {
9    Asc,
10    Desc,
11}
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct PaginationParams {
15    pub page: u32,
16    pub per_page: u32,
17    pub sort_by: Option<String>,
18    pub sort_direction: Option<SortDirection>,
19    #[serde(default)]
20    pub filters: Vec<Filter>,
21    pub search: Option<SearchParams>,
22    #[serde(default)]
23    pub disable_total_count: bool,
24    pub cursor: Option<Cursor>,
25}
26
27impl Default for PaginationParams {
28    fn default() -> Self {
29        Self {
30            page: 1,
31            per_page: 20,
32            sort_by: None,
33            sort_direction: None,
34            filters: Vec::new(),
35            search: None,
36            disable_total_count: false,
37            cursor: None,
38        }
39    }
40}
41
42impl PaginationParams {
43    pub fn new(page: u32, per_page: u32) -> Self {
44        Self {
45            page: page.max(1),
46            per_page: per_page.max(1).min(100),
47            sort_by: None,
48            sort_direction: None,
49            filters: Vec::new(),
50            search: None,
51            disable_total_count: false,
52            cursor: None,
53        }
54    }
55
56    pub fn with_sort(mut self, field: impl Into<String>) -> Self {
57        self.sort_by = Some(field.into());
58        self
59    }
60
61    pub fn with_direction(mut self, direction: SortDirection) -> Self {
62        self.sort_direction = Some(direction);
63        self
64    }
65
66    pub fn with_filter(mut self, filter: Filter) -> Self {
67        self.filters.push(filter);
68        self
69    }
70
71    pub fn with_filters(mut self, filters: Vec<Filter>) -> Self {
72        self.filters.extend(filters);
73        self
74    }
75
76    pub fn with_search(mut self, search: SearchParams) -> Self {
77        self.search = Some(search);
78        self
79    }
80
81    pub fn offset(&self) -> u32 {
82        (self.page - 1) * self.per_page
83    }
84
85    pub fn limit(&self) -> u32 {
86        self.per_page
87    }
88
89    pub fn to_sql_where(&self) -> Option<String> {
90        let mut conditions = Vec::new();
91
92        for filter in &self.filters {
93            conditions.push(filter.to_sql_where());
94        }
95
96        if let Some(ref search) = self.search {
97            conditions.push(search.to_sql_where());
98        }
99
100        if conditions.is_empty() {
101            None
102        } else {
103            Some(conditions.join(" AND "))
104        }
105    }
106
107    pub fn to_surrealql_where(&self) -> Option<String> {
108        let mut conditions = Vec::new();
109
110        for filter in &self.filters {
111            conditions.push(filter.to_surrealql_where());
112        }
113
114        if let Some(ref search) = self.search {
115            let search_conditions: Vec<String> = search
116                .fields
117                .iter()
118                .map(|field| {
119                    let pattern = if search.exact_match {
120                        format!("'{}'", search.query.replace('\'', "''"))
121                    } else {
122                        format!("'%{}%'", search.query.replace('\'', "''"))
123                    };
124                    format!("{} ~ {}", field, pattern)
125                })
126                .collect();
127            conditions.push(format!("({})", search_conditions.join(" OR ")));
128        }
129
130        if conditions.is_empty() {
131            None
132        } else {
133            Some(conditions.join(" AND "))
134        }
135    }
136}