rdb_pagination_core/
pagination_options.rs1use crate::OrderByOptions;
2
3#[derive(Debug, Clone)]
13pub struct PaginationOptions<T: OrderByOptions = ()> {
14 pub page: usize,
21 pub items_per_page: usize,
27 pub order_by: T,
31}
32
33impl PaginationOptions {
34 #[inline]
48 pub const fn new() -> Self {
49 Self {
50 page: 1, items_per_page: 0, order_by: ()
51 }
52 }
53}
54
55impl<T: OrderByOptions> Default for PaginationOptions<T> {
56 #[inline]
70 fn default() -> Self {
71 Self {
72 page: 1, items_per_page: 0, order_by: T::default()
73 }
74 }
75}
76
77impl<T: OrderByOptions> PaginationOptions<T> {
78 #[inline]
83 pub const fn page(mut self, page: usize) -> Self {
84 self.page = page;
85
86 self
87 }
88
89 #[inline]
93 pub const fn items_per_page(mut self, items_per_page: usize) -> Self {
94 self.items_per_page = items_per_page;
95
96 self
97 }
98
99 #[inline]
101 pub fn order_by(mut self, order_by: T) -> Self {
102 self.order_by = order_by;
103
104 self
105 }
106
107 #[inline]
109 pub const fn offset(&self) -> u64 {
110 if self.items_per_page == 0 {
111 0
112 } else {
113 match self.page {
114 0 | 1 => 0,
115 _ => (self.items_per_page * (self.page - 1)) as u64,
116 }
117 }
118 }
119
120 #[inline]
122 pub const fn limit(&self) -> Option<usize> {
123 if self.items_per_page == 0 {
124 None
125 } else {
126 Some(self.items_per_page)
127 }
128 }
129}
130
131#[cfg(any(feature = "mysql", feature = "sqlite"))]
132impl<T: OrderByOptions> PaginationOptions<T> {
133 fn to_sql_limit_offset<'a>(&self, s: &'a mut String) -> &'a str {
134 use std::{fmt::Write, str::from_utf8_unchecked};
135
136 let len = s.len();
137
138 let limit = self.limit();
139
140 if let Some(limit) = limit {
141 s.write_fmt(format_args!("LIMIT {limit}")).unwrap();
142 }
143
144 let offset = self.offset();
145
146 if offset > 0 {
147 if !s.is_empty() {
148 s.push(' ');
149 }
150
151 s.write_fmt(format_args!("OFFSET {offset}")).unwrap();
152 }
153
154 unsafe { from_utf8_unchecked(&s.as_bytes()[len..]) }
155 }
156}
157
158#[cfg(feature = "mysql")]
159impl<T: OrderByOptions> PaginationOptions<T> {
160 #[inline]
174 pub fn to_mysql_limit_offset<'a>(&self, s: &'a mut String) -> &'a str {
175 self.to_sql_limit_offset(s)
176 }
177}
178
179#[cfg(feature = "sqlite")]
180impl<T: OrderByOptions> PaginationOptions<T> {
181 #[inline]
195 pub fn to_sqlite_limit_offset<'a>(&self, s: &'a mut String) -> &'a str {
196 self.to_sql_limit_offset(s)
197 }
198}
199
200#[cfg(feature = "mssql")]
201impl<T: OrderByOptions> PaginationOptions<T> {
202 #[inline]
210 pub fn to_mssql_limit_offset<'a>(&self, s: &'a mut String) -> &'a str {
211 use std::{fmt::Write, str::from_utf8_unchecked};
212
213 let len = s.len();
214
215 let limit = self.limit();
216
217 let offset = self.offset();
218
219 if let Some(limit) = limit {
220 s.write_fmt(format_args!("OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY")).unwrap();
221 } else if offset > 0 {
222 s.write_fmt(format_args!("OFFSET {offset} ROWS")).unwrap();
223 }
224
225 unsafe { from_utf8_unchecked(&s.as_bytes()[len..]) }
226 }
227}
228
229#[cfg(feature = "mssql2008")]
230impl<T: OrderByOptions> PaginationOptions<T> {
231 #[inline]
251 pub fn to_mssql2008_limit_offset<'a>(
252 &self,
253 row_number_column_name: impl AsRef<str>,
254 s: &'a mut String,
255 ) -> &'a str {
256 use std::{fmt::Write, str::from_utf8_unchecked};
257
258 let row_number_column_name = row_number_column_name.as_ref();
259
260 let len = s.len();
261
262 let offset = self.offset();
263
264 if let Some(limit) = self.limit() {
265 if offset > 0 {
266 s.write_fmt(format_args!(
267 "WHERE [{row_number_column_name}] BETWEEN {} AND {}",
268 offset + 1,
269 offset + limit as u64
270 ))
271 .unwrap();
272 } else {
273 s.write_fmt(format_args!("WHERE [{row_number_column_name}] <= {limit}")).unwrap();
274 }
275 } else if offset > 0 {
276 s.write_fmt(format_args!("WHERE [{row_number_column_name}] > {offset}")).unwrap();
277 }
278
279 unsafe { from_utf8_unchecked(&s.as_bytes()[len..]) }
280 }
281}
282
283#[cfg(feature = "serde")]
284mod serde_trait {
285 use core::{fmt, fmt::Formatter, marker::PhantomData};
286
287 use serde::{
288 de::{MapAccess, Visitor},
289 ser::SerializeStruct,
290 Deserialize, Deserializer, Serialize, Serializer,
291 };
292
293 use super::PaginationOptions;
294 use crate::OrderByOptions;
295
296 impl<'de, T: OrderByOptions + Deserialize<'de>> Deserialize<'de> for PaginationOptions<T> {
297 #[inline]
298 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
299 where
300 D: Deserializer<'de>, {
301 struct MyVisitor<T>(PhantomData<T>);
302
303 impl<'de, T: OrderByOptions + Deserialize<'de>> Visitor<'de> for MyVisitor<T> {
304 type Value = PaginationOptions<T>;
305
306 #[inline]
307 fn expecting(&self, f: &mut Formatter) -> fmt::Result {
308 f.write_str("a map of options")
309 }
310
311 #[inline]
312 fn visit_map<V>(self, mut v: V) -> Result<Self::Value, V::Error>
313 where
314 V: MapAccess<'de>, {
315 let mut page: Option<usize> = None;
316 let mut items_per_page: Option<usize> = None;
317 let mut order_by: Option<T> = None;
318
319 while let Some(key) = v.next_key::<&str>()? {
320 match key {
321 "page" => {
322 page = Some(v.next_value()?);
323 },
324 "items_per_page" => {
325 items_per_page = Some(v.next_value()?);
326 },
327 "order_by" => {
328 order_by = Some(v.next_value()?);
329 },
330 _ => continue,
331 }
332 }
333
334 let page = page.unwrap_or(1);
335 let items_per_page = items_per_page.unwrap_or(0);
336 let order_by = order_by.unwrap_or_default();
337
338 Ok(PaginationOptions {
339 page,
340 items_per_page,
341 order_by,
342 })
343 }
344 }
345
346 deserializer.deserialize_map(MyVisitor(PhantomData))
347 }
348 }
349
350 impl<T: OrderByOptions + Serialize> Serialize for PaginationOptions<T> {
351 #[inline]
352 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
353 where
354 S: Serializer, {
355 let mut s = serializer.serialize_struct("PaginationOptions", 3)?;
356
357 s.serialize_field("page", &self.page)?;
358 s.serialize_field("items_per_page", &self.items_per_page)?;
359 s.serialize_field("order_by", &self.order_by)?;
360
361 s.end()
362 }
363 }
364}