1use paginator_utils::{
2 Cursor, CursorDirection, CursorValue, Filter, FilterOperator, FilterValue,
3 IntoPaginationParams, PaginationParams, SearchParams, SortDirection,
4};
5use std::marker::PhantomData;
6
7pub struct Paginator<State = Ready> {
13 params: PaginationParams,
14 _state: PhantomData<State>,
15}
16
17pub struct Ready;
19
20impl Default for Paginator {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl Paginator {
27 pub fn new() -> Self {
28 Self {
29 params: PaginationParams::default(),
30 _state: PhantomData,
31 }
32 }
33
34 pub fn page(mut self, page: u32) -> Self {
38 self.params.page = page.max(1);
39 self
40 }
41
42 pub fn per_page(mut self, per_page: u32) -> Self {
43 self.params.per_page = per_page.clamp(1, 100);
44 self
45 }
46
47 pub fn sort(self) -> SortBuilder<Self> {
51 SortBuilder::new(self)
52 }
53
54 pub fn filter(self) -> FilterBuilder<Self> {
58 FilterBuilder::with_parent(self)
59 }
60
61 pub fn search(self) -> SearchBuilder<Self> {
65 SearchBuilder::with_parent(self)
66 }
67
68 pub fn cursor(self) -> CursorBuilder<Self> {
72 CursorBuilder::with_parent(self)
73 }
74
75 pub fn disable_total_count(mut self) -> Self {
76 self.params.disable_total_count = true;
77 self
78 }
79
80 pub fn build(self) -> PaginationParams {
84 self.params
85 }
86}
87
88pub struct SortBuilder<P> {
95 parent: P,
96}
97
98impl<P> SortBuilder<P> {
99 fn new(parent: P) -> Self {
100 Self { parent }
101 }
102
103 pub fn asc(mut self, field: impl Into<String>) -> P
104 where
105 P: HasParams,
106 {
107 let mut p = self.parent;
108 p.params_mut().sort_by = Some(field.into());
109 p.params_mut().sort_direction = Some(SortDirection::Asc);
110 p
111 }
112
113 pub fn desc(mut self, field: impl Into<String>) -> P
114 where
115 P: HasParams,
116 {
117 let mut p = self.parent;
118 p.params_mut().sort_by = Some(field.into());
119 p.params_mut().sort_direction = Some(SortDirection::Desc);
120 p
121 }
122}
123
124pub struct FilterBuilder<P = ()> {
131 parent: Option<P>,
132 filters: Vec<Filter>,
133}
134
135impl FilterBuilder<()> {
136 pub fn new() -> Self {
138 Self {
139 parent: None,
140 filters: Vec::new(),
141 }
142 }
143
144 pub fn build(self) -> Vec<Filter> {
146 self.filters
147 }
148}
149
150impl<P> FilterBuilder<P> {
151 pub fn with_parent(parent: P) -> Self {
153 Self {
154 parent: Some(parent),
155 filters: Vec::new(),
156 }
157 }
158
159 fn push(mut self, field: impl Into<String>, op: FilterOperator, value: FilterValue) -> Self {
162 self.filters.push(Filter::new(field, op, value));
163 self
164 }
165
166 pub fn eq(self, field: impl Into<String>, value: FilterValue) -> Self {
167 self.push(field, FilterOperator::Eq, value)
168 }
169
170 pub fn ne(self, field: impl Into<String>, value: FilterValue) -> Self {
171 self.push(field, FilterOperator::Ne, value)
172 }
173
174 pub fn gt(self, field: impl Into<String>, value: FilterValue) -> Self {
175 self.push(field, FilterOperator::Gt, value)
176 }
177
178 pub fn lt(self, field: impl Into<String>, value: FilterValue) -> Self {
179 self.push(field, FilterOperator::Lt, value)
180 }
181
182 pub fn gte(self, field: impl Into<String>, value: FilterValue) -> Self {
183 self.push(field, FilterOperator::Gte, value)
184 }
185
186 pub fn lte(self, field: impl Into<String>, value: FilterValue) -> Self {
187 self.push(field, FilterOperator::Lte, value)
188 }
189
190 pub fn like(self, field: impl Into<String>, pat: impl Into<String>) -> Self {
191 self.push(field, FilterOperator::Like, FilterValue::String(pat.into()))
192 }
193
194 pub fn ilike(self, field: impl Into<String>, pat: impl Into<String>) -> Self {
195 self.push(
196 field,
197 FilterOperator::ILike,
198 FilterValue::String(pat.into()),
199 )
200 }
201
202 pub fn r#in(self, field: impl Into<String>, values: Vec<FilterValue>) -> Self {
203 self.push(field, FilterOperator::In, FilterValue::Array(values))
204 }
205
206 pub fn not_in(self, field: impl Into<String>, values: Vec<FilterValue>) -> Self {
207 self.push(field, FilterOperator::NotIn, FilterValue::Array(values))
208 }
209
210 pub fn between(self, field: impl Into<String>, min: FilterValue, max: FilterValue) -> Self {
211 self.push(
212 field,
213 FilterOperator::Between,
214 FilterValue::Array(vec![min, max]),
215 )
216 }
217
218 pub fn is_null(self, field: impl Into<String>) -> Self {
219 self.push(field, FilterOperator::IsNull, FilterValue::Null)
220 }
221
222 pub fn is_not_null(self, field: impl Into<String>) -> Self {
223 self.push(field, FilterOperator::IsNotNull, FilterValue::Null)
224 }
225
226 pub fn contains(self, field: impl Into<String>, value: FilterValue) -> Self {
227 self.push(field, FilterOperator::Contains, value)
228 }
229
230 pub fn apply(self) -> P
236 where
237 P: HasParams,
238 {
239 let mut parent = self.parent.unwrap_or_else(|| {
243 panic!("BUG: FilterBuilder::apply called without a parent. This should be prevented by type system.")
244 });
245 parent.params_mut().filters.extend(self.filters);
246 parent
247 }
248}
249
250pub struct SearchBuilder<P = ()> {
257 parent: Option<P>,
258 query: Option<String>,
259 fields: Vec<String>,
260 exact: bool,
261 case_sensitive: bool,
262}
263
264impl SearchBuilder<()> {
265 pub fn new() -> Self {
266 Self {
267 parent: None,
268 query: None,
269 fields: Vec::new(),
270 exact: false,
271 case_sensitive: false,
272 }
273 }
274
275 pub fn build(self) -> Option<SearchParams> {
276 self.query.map(|q| {
277 let mut params = SearchParams::new(q, self.fields);
278 if self.exact {
279 params = params.with_exact_match(true);
280 }
281 if self.case_sensitive {
282 params = params.with_case_sensitive(true);
283 }
284 params
285 })
286 }
287}
288
289impl<P> SearchBuilder<P> {
290 pub fn with_parent(parent: P) -> Self {
291 Self {
292 parent: Some(parent),
293 query: None,
294 fields: Vec::new(),
295 exact: false,
296 case_sensitive: false,
297 }
298 }
299
300 pub fn query(mut self, q: impl Into<String>) -> Self {
301 self.query = Some(q.into());
302 self
303 }
304
305 pub fn fields<I, S>(mut self, fields: I) -> Self
306 where
307 I: IntoIterator<Item = S>,
308 S: Into<String>,
309 {
310 self.fields = fields.into_iter().map(Into::into).collect();
311 self
312 }
313
314 pub fn exact(mut self, yes: bool) -> Self {
315 self.exact = yes;
316 self
317 }
318
319 pub fn case_sensitive(mut self, yes: bool) -> Self {
320 self.case_sensitive = yes;
321 self
322 }
323
324 pub fn apply(self) -> P
330 where
331 P: HasParams,
332 {
333 let mut parent = self.parent.unwrap_or_else(|| {
337 panic!("BUG: SearchBuilder::apply called without a parent. This should be prevented by type system.")
338 });
339
340 if let Some(q) = self.query {
341 let mut s = SearchParams::new(q, self.fields);
342 if self.exact {
343 s = s.with_exact_match(true);
344 }
345 if self.case_sensitive {
346 s = s.with_case_sensitive(true);
347 }
348 parent.params_mut().search = Some(s);
349 }
350
351 parent
352 }
353}
354
355pub struct CursorBuilder<P = ()> {
362 parent: Option<P>,
363 cursor: Option<Cursor>,
364}
365
366impl CursorBuilder<()> {
367 pub fn new() -> Self {
368 Self {
369 parent: None,
370 cursor: None,
371 }
372 }
373
374 pub fn build(self) -> Option<Cursor> {
375 self.cursor
376 }
377}
378
379impl<P> CursorBuilder<P> {
380 pub fn with_parent(parent: P) -> Self {
381 Self {
382 parent: Some(parent),
383 cursor: None,
384 }
385 }
386
387 pub fn after(mut self, field: impl Into<String>, value: CursorValue) -> Self {
388 self.cursor = Some(Cursor::new(field.into(), value, CursorDirection::After));
389 self
390 }
391
392 pub fn before(mut self, field: impl Into<String>, value: CursorValue) -> Self {
393 self.cursor = Some(Cursor::new(field.into(), value, CursorDirection::Before));
394 self
395 }
396
397 pub fn from_encoded(mut self, encoded: &str) -> Result<Self, String> {
398 self.cursor = Some(Cursor::decode(encoded)?);
399 Ok(self)
400 }
401
402 pub fn apply(self) -> P
408 where
409 P: HasParams,
410 {
411 let mut parent = self.parent.unwrap_or_else(|| {
415 panic!("BUG: CursorBuilder::apply called without a parent. This should be prevented by type system.")
416 });
417 if let Some(cursor) = self.cursor {
418 parent.params_mut().cursor = Some(cursor);
419 }
420 parent
421 }
422}
423
424pub trait HasParams {
426 fn params_mut(&mut self) -> &mut PaginationParams;
427}
428
429impl<S> HasParams for Paginator<S> {
430 fn params_mut(&mut self) -> &mut PaginationParams {
431 &mut self.params
432 }
433}
434
435impl<S> IntoPaginationParams for Paginator<S> {
436 fn into_pagination_params(self) -> PaginationParams {
437 self.params
438 }
439}
440
441impl IntoPaginationParams for FilterBuilder<()> {
442 fn into_pagination_params(self) -> PaginationParams {
443 PaginationParams {
444 filters: self.filters,
445 ..Default::default()
446 }
447 }
448}
449
450impl IntoPaginationParams for SearchBuilder<()> {
451 fn into_pagination_params(self) -> PaginationParams {
452 let mut params = PaginationParams::default();
453 if let Some(query) = self.query {
454 let mut search = SearchParams::new(query, self.fields);
455 if self.exact {
456 search = search.with_exact_match(true);
457 }
458 if self.case_sensitive {
459 search = search.with_case_sensitive(true);
460 }
461 params.search = Some(search);
462 }
463 params
464 }
465}
466
467impl IntoPaginationParams for CursorBuilder<()> {
468 fn into_pagination_params(self) -> PaginationParams {
469 PaginationParams {
470 cursor: self.cursor,
471 ..Default::default()
472 }
473 }
474}
475
476impl IntoPaginationParams for PaginatorBuilder {
477 fn into_pagination_params(self) -> PaginationParams {
478 self.params
479 }
480}
481
482pub struct PaginatorBuilder {
490 params: PaginationParams,
491}
492
493impl Default for PaginatorBuilder {
494 fn default() -> Self {
495 Self::new()
496 }
497}
498
499impl PaginatorBuilder {
500 pub fn new() -> Self {
501 Self {
502 params: PaginationParams::default(),
503 }
504 }
505
506 pub fn page(mut self, page: u32) -> Self {
507 self.params.page = page.max(1);
508 self
509 }
510
511 pub fn per_page(mut self, per_page: u32) -> Self {
512 self.params.per_page = per_page.clamp(1, 100);
513 self
514 }
515
516 pub fn sort_by(mut self, field: impl Into<String>) -> Self {
517 self.params.sort_by = Some(field.into());
518 self
519 }
520
521 pub fn sort_asc(mut self) -> Self {
522 self.params.sort_direction = Some(SortDirection::Asc);
523 self
524 }
525
526 pub fn sort_desc(mut self) -> Self {
527 self.params.sort_direction = Some(SortDirection::Desc);
528 self
529 }
530
531 pub fn filter(
532 mut self,
533 field: impl Into<String>,
534 operator: FilterOperator,
535 value: FilterValue,
536 ) -> Self {
537 self.params
538 .filters
539 .push(Filter::new(field, operator, value));
540 self
541 }
542
543 pub fn filter_eq(mut self, field: impl Into<String>, value: FilterValue) -> Self {
544 self.params
545 .filters
546 .push(Filter::new(field, FilterOperator::Eq, value));
547 self
548 }
549
550 pub fn filter_ne(mut self, field: impl Into<String>, value: FilterValue) -> Self {
551 self.params
552 .filters
553 .push(Filter::new(field, FilterOperator::Ne, value));
554 self
555 }
556
557 pub fn filter_gt(mut self, field: impl Into<String>, value: FilterValue) -> Self {
558 self.params
559 .filters
560 .push(Filter::new(field, FilterOperator::Gt, value));
561 self
562 }
563
564 pub fn filter_lt(mut self, field: impl Into<String>, value: FilterValue) -> Self {
565 self.params
566 .filters
567 .push(Filter::new(field, FilterOperator::Lt, value));
568 self
569 }
570
571 pub fn filter_gte(mut self, field: impl Into<String>, value: FilterValue) -> Self {
572 self.params
573 .filters
574 .push(Filter::new(field, FilterOperator::Gte, value));
575 self
576 }
577
578 pub fn filter_lte(mut self, field: impl Into<String>, value: FilterValue) -> Self {
579 self.params
580 .filters
581 .push(Filter::new(field, FilterOperator::Lte, value));
582 self
583 }
584
585 pub fn filter_like(mut self, field: impl Into<String>, pattern: impl Into<String>) -> Self {
586 self.params.filters.push(Filter::new(
587 field,
588 FilterOperator::Like,
589 FilterValue::String(pattern.into()),
590 ));
591 self
592 }
593
594 pub fn filter_ilike(mut self, field: impl Into<String>, pattern: impl Into<String>) -> Self {
595 self.params.filters.push(Filter::new(
596 field,
597 FilterOperator::ILike,
598 FilterValue::String(pattern.into()),
599 ));
600 self
601 }
602
603 pub fn filter_in(mut self, field: impl Into<String>, values: Vec<FilterValue>) -> Self {
604 self.params.filters.push(Filter::new(
605 field,
606 FilterOperator::In,
607 FilterValue::Array(values),
608 ));
609 self
610 }
611
612 pub fn filter_between(
613 mut self,
614 field: impl Into<String>,
615 min: FilterValue,
616 max: FilterValue,
617 ) -> Self {
618 self.params.filters.push(Filter::new(
619 field,
620 FilterOperator::Between,
621 FilterValue::Array(vec![min, max]),
622 ));
623 self
624 }
625
626 pub fn filter_is_null(mut self, field: impl Into<String>) -> Self {
627 self.params.filters.push(Filter::new(
628 field,
629 FilterOperator::IsNull,
630 FilterValue::Null,
631 ));
632 self
633 }
634
635 pub fn filter_is_not_null(mut self, field: impl Into<String>) -> Self {
636 self.params.filters.push(Filter::new(
637 field,
638 FilterOperator::IsNotNull,
639 FilterValue::Null,
640 ));
641 self
642 }
643
644 pub fn search(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
645 self.params.search = Some(SearchParams::new(query, fields));
646 self
647 }
648
649 pub fn search_exact(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
650 self.params.search = Some(SearchParams::new(query, fields).with_exact_match(true));
651 self
652 }
653
654 pub fn search_case_sensitive(mut self, query: impl Into<String>, fields: Vec<String>) -> Self {
655 self.params.search = Some(SearchParams::new(query, fields).with_case_sensitive(true));
656 self
657 }
658
659 pub fn disable_total_count(mut self) -> Self {
660 self.params.disable_total_count = true;
661 self
662 }
663
664 pub fn cursor(
665 mut self,
666 field: impl Into<String>,
667 value: CursorValue,
668 direction: CursorDirection,
669 ) -> Self {
670 self.params.cursor = Some(Cursor::new(field.into(), value, direction));
671 self
672 }
673
674 pub fn cursor_after(mut self, field: impl Into<String>, value: CursorValue) -> Self {
675 self.params.cursor = Some(Cursor::new(field.into(), value, CursorDirection::After));
676 self
677 }
678
679 pub fn cursor_before(mut self, field: impl Into<String>, value: CursorValue) -> Self {
680 self.params.cursor = Some(Cursor::new(field.into(), value, CursorDirection::Before));
681 self
682 }
683
684 pub fn cursor_from_encoded(mut self, encoded: &str) -> Result<Self, String> {
685 self.params.cursor = Some(Cursor::decode(encoded)?);
686 Ok(self)
687 }
688
689 pub fn build(self) -> PaginationParams {
690 self.params
691 }
692}