mas_storage/
pagination.rs1use thiserror::Error;
10use ulid::Ulid;
11
12#[derive(Debug, Error)]
14#[error("Either 'first' or 'last' must be specified")]
15pub struct InvalidPagination;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct Pagination {
20    pub before: Option<Ulid>,
22
23    pub after: Option<Ulid>,
25
26    pub count: usize,
28
29    pub direction: PaginationDirection,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum PaginationDirection {
36    Forward,
38
39    Backward,
41}
42
43impl Pagination {
44    pub const fn try_new(
51        before: Option<Ulid>,
52        after: Option<Ulid>,
53        first: Option<usize>,
54        last: Option<usize>,
55    ) -> Result<Self, InvalidPagination> {
56        let (direction, count) = match (first, last) {
57            (Some(first), _) => (PaginationDirection::Forward, first),
58            (_, Some(last)) => (PaginationDirection::Backward, last),
59            (None, None) => return Err(InvalidPagination),
60        };
61
62        Ok(Self {
63            before,
64            after,
65            count,
66            direction,
67        })
68    }
69
70    #[must_use]
72    pub const fn first(first: usize) -> Self {
73        Self {
74            before: None,
75            after: None,
76            count: first,
77            direction: PaginationDirection::Forward,
78        }
79    }
80
81    #[must_use]
83    pub const fn last(last: usize) -> Self {
84        Self {
85            before: None,
86            after: None,
87            count: last,
88            direction: PaginationDirection::Backward,
89        }
90    }
91
92    #[must_use]
94    pub const fn before(mut self, id: Ulid) -> Self {
95        self.before = Some(id);
96        self
97    }
98
99    #[must_use]
101    pub const fn clear_before(mut self) -> Self {
102        self.before = None;
103        self
104    }
105
106    #[must_use]
108    pub const fn after(mut self, id: Ulid) -> Self {
109        self.after = Some(id);
110        self
111    }
112
113    #[must_use]
115    pub const fn clear_after(mut self) -> Self {
116        self.after = None;
117        self
118    }
119
120    #[must_use]
122    pub fn process<T>(&self, mut edges: Vec<T>) -> Page<T> {
123        let is_full = edges.len() == (self.count + 1);
124        if is_full {
125            edges.pop();
126        }
127
128        let (has_previous_page, has_next_page) = match self.direction {
129            PaginationDirection::Forward => (false, is_full),
130            PaginationDirection::Backward => {
131                edges.reverse();
133                (is_full, false)
134            }
135        };
136
137        Page {
138            has_next_page,
139            has_previous_page,
140            edges,
141        }
142    }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct Page<T> {
148    pub has_next_page: bool,
150
151    pub has_previous_page: bool,
153
154    pub edges: Vec<T>,
156}
157
158impl<T> Page<T> {
159    #[must_use]
165    pub fn map<F, T2>(self, f: F) -> Page<T2>
166    where
167        F: FnMut(T) -> T2,
168    {
169        let edges = self.edges.into_iter().map(f).collect();
170        Page {
171            has_next_page: self.has_next_page,
172            has_previous_page: self.has_previous_page,
173            edges,
174        }
175    }
176
177    pub fn try_map<F, E, T2>(self, f: F) -> Result<Page<T2>, E>
187    where
188        F: FnMut(T) -> Result<T2, E>,
189    {
190        let edges: Result<Vec<T2>, E> = self.edges.into_iter().map(f).collect();
191        Ok(Page {
192            has_next_page: self.has_next_page,
193            has_previous_page: self.has_previous_page,
194            edges: edges?,
195        })
196    }
197}