1use 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<Cursor = Ulid> {
20    pub before: Option<Cursor>,
22
23    pub after: Option<Cursor>,
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
43pub trait Node<C = Ulid> {
45    fn cursor(&self) -> C;
47}
48
49impl<C> Pagination<C> {
50    pub fn try_new(
57        before: Option<C>,
58        after: Option<C>,
59        first: Option<usize>,
60        last: Option<usize>,
61    ) -> Result<Self, InvalidPagination> {
62        let (direction, count) = match (first, last) {
63            (Some(first), _) => (PaginationDirection::Forward, first),
64            (_, Some(last)) => (PaginationDirection::Backward, last),
65            (None, None) => return Err(InvalidPagination),
66        };
67
68        Ok(Self {
69            before,
70            after,
71            count,
72            direction,
73        })
74    }
75
76    #[must_use]
78    pub const fn first(first: usize) -> Self {
79        Self {
80            before: None,
81            after: None,
82            count: first,
83            direction: PaginationDirection::Forward,
84        }
85    }
86
87    #[must_use]
89    pub const fn last(last: usize) -> Self {
90        Self {
91            before: None,
92            after: None,
93            count: last,
94            direction: PaginationDirection::Backward,
95        }
96    }
97
98    #[must_use]
100    pub fn before(mut self, cursor: C) -> Self {
101        self.before = Some(cursor);
102        self
103    }
104
105    #[must_use]
107    pub fn clear_before(mut self) -> Self {
108        self.before = None;
109        self
110    }
111
112    #[must_use]
114    pub fn after(mut self, cursor: C) -> Self {
115        self.after = Some(cursor);
116        self
117    }
118
119    #[must_use]
121    pub fn clear_after(mut self) -> Self {
122        self.after = None;
123        self
124    }
125
126    #[must_use]
128    pub fn process<T: Node<C>>(&self, mut nodes: Vec<T>) -> Page<T, C> {
129        let is_full = nodes.len() == (self.count + 1);
130        if is_full {
131            nodes.pop();
132        }
133
134        let (has_previous_page, has_next_page) = match self.direction {
135            PaginationDirection::Forward => (false, is_full),
136            PaginationDirection::Backward => {
137                nodes.reverse();
139                (is_full, false)
140            }
141        };
142
143        let edges = nodes
144            .into_iter()
145            .map(|node| Edge {
146                cursor: node.cursor(),
147                node,
148            })
149            .collect();
150
151        Page {
152            has_next_page,
153            has_previous_page,
154            edges,
155        }
156    }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct Edge<T, C = Ulid> {
162    pub cursor: C,
164    pub node: T,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct Page<T, C = Ulid> {
171    pub has_next_page: bool,
173
174    pub has_previous_page: bool,
176
177    pub edges: Vec<Edge<T, C>>,
179}
180
181impl<T, C> Page<T, C> {
182    #[must_use]
188    pub fn map<F, T2>(self, mut f: F) -> Page<T2, C>
189    where
190        F: FnMut(T) -> T2,
191    {
192        let edges = self
193            .edges
194            .into_iter()
195            .map(|edge| Edge {
196                cursor: edge.cursor,
197                node: f(edge.node),
198            })
199            .collect();
200        Page {
201            has_next_page: self.has_next_page,
202            has_previous_page: self.has_previous_page,
203            edges,
204        }
205    }
206
207    pub fn try_map<F, E, T2>(self, mut f: F) -> Result<Page<T2, C>, E>
217    where
218        F: FnMut(T) -> Result<T2, E>,
219    {
220        let edges: Result<Vec<Edge<T2, C>>, E> = self
221            .edges
222            .into_iter()
223            .map(|edge| {
224                Ok(Edge {
225                    cursor: edge.cursor,
226                    node: f(edge.node)?,
227                })
228            })
229            .collect();
230
231        Ok(Page {
232            has_next_page: self.has_next_page,
233            has_previous_page: self.has_previous_page,
234            edges: edges?,
235        })
236    }
237}