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