1#[cfg(feature = "json")]
2use serde::{Deserialize, Serialize};
3
4#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))]
6pub type Result<T, E = sqlx_data_integration::Error> = ::std::result::Result<T, E>;
7
8#[cfg(not(any(feature = "sqlite", feature = "postgres", feature = "mysql")))]
10pub type Result<T, E = crate::cursor::CursorError> = ::std::result::Result<T, E>;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
15pub struct Serial<T> {
16 pub data: Vec<T>,
17 pub page: u32,
18 pub size: u32,
19 pub total_items: i64,
20 pub total_pages: u32,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
26pub struct Slice<T> {
27 pub data: Vec<T>,
28 pub page: u32,
29 pub size: u32,
30 pub has_next: bool,
31 pub has_previous: bool,
32 pub total_items: Option<i64>, }
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
38pub struct Cursor<T> {
39 pub data: Vec<T>,
40 pub per_page: u32,
41 pub has_next: bool,
42 pub has_prev: bool,
43 pub next_cursor: Option<String>,
44 pub prev_cursor: Option<String>,
45}
46
47impl<T> Serial<T> {
48 const DEFAULT_LIMIT: u32 = 20;
49
50 pub fn new(data: Vec<T>, params: &crate::Params, total_elements: i64) -> Self {
51 let Some(crate::pagination::Pagination::Serial(serial)) = ¶ms.pagination else {
52 let default_size = Self::page_size(params);
53 return Self {
55 data,
56 page: 1,
57 size: default_size,
58 total_items: total_elements,
59 total_pages: 1,
60 };
61 };
62
63 let size = serial.limit().max(1);
64 let total_pages = Self::calculate_total_pages(total_elements, size);
65
66 Self {
67 data,
68 page: serial.page(),
69 size,
70 total_items: total_elements,
71 total_pages,
72 }
73 }
74
75 #[inline]
76 fn calculate_total_pages(total_items: i64, size: u32) -> u32 {
77 let size = size as i64;
78
79 total_items
80 .checked_add(size - 1)
81 .and_then(|sum| sum.checked_div(size))
82 .and_then(|pages| u32::try_from(pages.max(0)).ok())
83 .unwrap_or(1)
84 }
85
86 #[inline]
87 fn page_size(params: &crate::Params) -> u32 {
88 params
89 .limit
90 .as_ref()
91 .map(|l| l.0)
92 .unwrap_or(Self::DEFAULT_LIMIT)
93 .max(1)
94 }
95}
96
97impl<T> Slice<T> {
98 const DEFAULT_LIMIT: u32 = 20;
99
100 pub fn new(mut data: Vec<T>, params: &crate::Params, total_elements: i64) -> Self {
101 let size = Self::page_size(params);
102 let has_next = Self::trim_to_page(&mut data, size);
103
104 if let Some(crate::pagination::Pagination::Slice(slice)) = ¶ms.pagination {
105 let page = slice.page();
106
107 return Self {
108 data,
109 page,
110 size,
111 has_next,
112 has_previous: page > 1,
113 total_items: (!slice.disable_total_count()).then_some(total_elements),
114 };
115 }
116
117 let page = Self::page_offset(params, size);
119
120 Self {
121 data,
122 page,
123 size,
124 has_next,
125 has_previous: page > 1,
126 total_items: Some(total_elements),
127 }
128 }
129
130 #[inline]
131 fn page_size(params: &crate::Params) -> u32 {
132 params
133 .limit
134 .as_ref()
135 .map(|l| l.0)
136 .unwrap_or(Self::DEFAULT_LIMIT)
137 .max(1)
138 }
139
140 #[inline]
141 fn page_offset(params: &crate::Params, size: u32) -> u32 {
142 params
143 .offset
144 .as_ref()
145 .map(|o| o.0 as i64)
146 .and_then(|offset| offset.checked_div(size as i64))
147 .and_then(|p| p.checked_add(1))
148 .unwrap_or(1) as u32
149 }
150
151 #[inline]
152 fn trim_to_page(data: &mut Vec<T>, size: u32) -> bool {
153 let size = size as usize;
154
155 if data.len() > size {
156 data.truncate(size);
157 return true;
158 }
159 false
160 }
161}
162
163impl<T: crate::CursorSecureExtract> Cursor<T> {
164 const DEFAULT_LIMIT: u32 = 20;
165
166 pub fn new(mut data: Vec<T>, params: &crate::Params) -> Result<Self> {
167 let sort = params.sort_by.as_ref().ok_or_else(|| {
169 crate::cursor::CursorError::decode_error(
170 "Cursor pagination requires ORDER BY (sort_by)",
171 )
172 })?;
173
174 let Some(crate::pagination::Pagination::Cursor(cursor)) = ¶ms.pagination else {
175 #[allow(clippy::useless_conversion)]
176 return Err(crate::cursor::CursorError::decode_error("Cursor params is not present").into());
177 };
178
179 let is_backward = cursor.direction == Some(crate::cursor::CursorDirection::Before);
180 let requested_limit = Self::page_size(params);
181
182 let has_more = data.len() > requested_limit as usize;
183
184 if has_more {
185 data.truncate(requested_limit as usize);
186 }
187
188 let had_prev = !cursor.is_empty(); let (next_cursor, prev_cursor) =
191 Self::generate_cursors(cursor, &data, has_more, had_prev, sort)?;
192
193 if is_backward {
194 data.reverse();
195 }
196
197 Ok(Self {
198 data,
199 per_page: requested_limit,
200 has_next: if is_backward { had_prev } else { has_more },
201 has_prev: if is_backward { has_more } else { had_prev },
202 next_cursor,
203 prev_cursor,
204 })
205 }
206
207 #[inline]
208 fn page_size(params: &crate::Params) -> u32 {
209 params
210 .limit
211 .as_ref()
212 .map(|l| l.0)
213 .unwrap_or(Self::DEFAULT_LIMIT)
214 .max(1)
215 }
216
217 #[inline]
218 fn generate_cursors(
219 cursor: &crate::pagination::CursorParams,
220 data: &[T],
221 has_more_pages: bool,
222 had_previous_cursor: bool,
223 sorting_params: &crate::sort::SortingParams,
224 ) -> Result<(Option<String>, Option<String>)> {
225 if data.is_empty() {
226 return Ok((None, None));
227 }
228
229 let next = if has_more_pages {
230 cursor.generate_next_cursor(data, has_more_pages, sorting_params)?
231 } else {
232 None
233 };
234
235 let prev = if had_previous_cursor {
236 cursor.generate_prev_cursor(data, had_previous_cursor, sorting_params)?
237 } else {
238 None
239 };
240
241 Ok((next, prev))
242 }
243}