cronback_lib/database/
pagination.rs1use proto::common::{PaginationIn, PaginationOut};
2use sea_orm::{
3 ColumnTrait,
4 EntityTrait,
5 ModelTrait,
6 QueryFilter,
7 QueryOrder,
8 QuerySelect,
9 Related,
10 Select,
11 SelectTwo,
12};
13
14pub trait PaginatedEntity: EntityTrait {
16 fn cursor_column() -> Self::Column;
17}
18
19pub trait PaginatedSelect<E: EntityTrait> {
20 fn with_pagination(self, pagination: &PaginationIn) -> Self;
21}
22
23impl<E> PaginatedSelect<E> for Select<E>
24where
25 E: EntityTrait + PaginatedEntity,
26{
27 fn with_pagination(self, pagination: &PaginationIn) -> Select<E> {
28 let cursor_column = E::cursor_column();
29 let mut query = self
30 .order_by_desc(cursor_column)
31 .limit(Some(pagination.paginated_query_limit()));
34
35 if let Some(ref cursor) = pagination.cursor {
36 query = query.filter(cursor_column.lte(cursor));
37 }
38 query
39 }
40}
41
42impl<E, R> PaginatedSelect<E> for SelectTwo<E, R>
43where
44 E: EntityTrait + PaginatedEntity + Related<R>,
45 R: EntityTrait,
46{
47 fn with_pagination(self, pagination: &PaginationIn) -> SelectTwo<E, R> {
48 let cursor_column = E::cursor_column();
49 let mut query = self
50 .order_by_desc(cursor_column)
51 .limit(Some(pagination.paginated_query_limit()));
54
55 if let Some(ref cursor) = pagination.cursor {
56 query = query.filter(cursor_column.lte(cursor));
57 }
58 query
59 }
60}
61
62#[derive(Debug)]
63pub struct PaginatedResponse<T> {
64 pub pagination: PaginationOut,
65 pub data: Vec<T>,
66}
67
68impl<T> PaginatedResponse<T> {
69 pub fn from(data: Vec<T>, pagination: PaginationOut) -> Self {
70 Self { data, pagination }
71 }
72}
73
74impl<T> PaginatedResponse<T>
75where
76 T: ModelTrait,
77 T::Entity: PaginatedEntity,
78{
79 pub fn paginate(mut results: Vec<T>, pagination: &PaginationIn) -> Self {
80 let next_cursor = {
86 let clip = std::cmp::min(results.len(), pagination.limit());
88 let mut drained = results.drain(clip..);
89 drained.next()
90 };
91 let has_more = next_cursor.is_some();
93 let cursor_column = <T::Entity as PaginatedEntity>::cursor_column();
94
95 let pagination_out = PaginationOut {
96 next_cursor: next_cursor.map(|x| {
97 x.get(cursor_column)
98 .expect("Cursor column is not string convertible!")
99 }),
100 has_more,
101 };
102
103 Self {
104 pagination: pagination_out,
105 data: results,
106 }
107 }
108}