1use super::{RowIndex, RowModel};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct PaginationState {
5 pub page_index: usize,
6 pub page_size: usize,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct PaginationBounds {
11 pub page_index: usize,
12 pub page_count: usize,
13 pub can_prev: bool,
14 pub can_next: bool,
15 pub page_start: usize,
16 pub page_end: usize,
17}
18
19impl Default for PaginationState {
20 fn default() -> Self {
21 Self {
22 page_index: 0,
23 page_size: 10,
24 }
25 }
26}
27
28pub fn pagination_bounds(total_rows: usize, pagination: PaginationState) -> PaginationBounds {
29 if pagination.page_size == 0 || total_rows == 0 {
30 return PaginationBounds {
31 page_index: 0,
32 page_count: 0,
33 can_prev: false,
34 can_next: false,
35 page_start: 0,
36 page_end: 0,
37 };
38 }
39
40 let page_count = total_rows.div_ceil(pagination.page_size);
41 let page_index = pagination.page_index.min(page_count.saturating_sub(1));
42 let page_start = page_index.saturating_mul(pagination.page_size);
43 let page_end = (page_start.saturating_add(pagination.page_size)).min(total_rows);
44
45 PaginationBounds {
46 page_index,
47 page_count,
48 can_prev: page_index > 0,
49 can_next: page_index + 1 < page_count,
50 page_start,
51 page_end,
52 }
53}
54
55pub fn paginate_row_model<'a, TData>(
56 row_model: &RowModel<'a, TData>,
57 pagination: PaginationState,
58) -> RowModel<'a, TData> {
59 if row_model.root_rows().is_empty() {
60 return row_model.clone();
61 }
62 if pagination.page_size == 0 {
63 return RowModel {
64 root_rows: Vec::new(),
65 flat_rows: Vec::new(),
66 rows_by_key: row_model.rows_by_key().clone(),
67 rows_by_id: row_model.rows_by_id().clone(),
68 arena: row_model.arena().to_vec(),
69 };
70 }
71
72 let page_start = pagination.page_index.saturating_mul(pagination.page_size);
73 let page_end = page_start.saturating_add(pagination.page_size);
74
75 let mut out = row_model.clone();
76 let roots = out.root_rows.clone();
77 let start = page_start.min(roots.len());
78 let end = page_end.min(roots.len());
79 out.root_rows = roots[start..end].to_vec();
80
81 out.flat_rows.clear();
82 fn push_flat<TData>(row_model: &mut RowModel<'_, TData>, row: RowIndex) {
83 row_model.flat_rows.push(row);
84 let Some(r) = row_model.row(row) else {
85 return;
86 };
87 let children = r.sub_rows.clone();
88 for child in children {
89 push_flat(row_model, child);
90 }
91 }
92
93 let roots = out.root_rows.clone();
94 for root in roots {
95 push_flat(&mut out, root);
96 }
97
98 out
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::table::{RowKey, Table};
105
106 #[derive(Debug, Clone)]
107 struct Item {
108 #[allow(dead_code)]
109 value: i32,
110 }
111
112 #[test]
113 fn paginate_row_model_slices_root_rows_and_rebuilds_flat_rows() {
114 let data = (0..5).map(|i| Item { value: i }).collect::<Vec<_>>();
115 let table = Table::builder(&data).build();
116 let core = table.core_row_model();
117
118 let paged = paginate_row_model(
119 core,
120 PaginationState {
121 page_index: 1,
122 page_size: 2,
123 },
124 );
125
126 let ids = paged
127 .root_rows()
128 .iter()
129 .filter_map(|&i| paged.row(i).map(|r| r.key.0))
130 .collect::<Vec<_>>();
131 assert_eq!(ids, vec![2, 3]);
132 assert_eq!(paged.flat_rows().len(), 2);
133 assert!(
134 paged.row_by_key(RowKey::from_index(0)).is_some(),
135 "rows_by_key remains full"
136 );
137 assert!(
138 paged.row_by_key(RowKey::from_index(4)).is_some(),
139 "rows_by_key remains full"
140 );
141 }
142
143 #[test]
144 fn paginate_row_model_clamps_page_end_to_row_count() {
145 let data = (0..5).map(|i| Item { value: i }).collect::<Vec<_>>();
146 let table = Table::builder(&data).build();
147 let core = table.core_row_model();
148
149 let paged = paginate_row_model(
150 core,
151 PaginationState {
152 page_index: 0,
153 page_size: 10,
154 },
155 );
156
157 let ids = paged
158 .root_rows()
159 .iter()
160 .filter_map(|&i| paged.row(i).map(|r| r.key.0))
161 .collect::<Vec<_>>();
162 assert_eq!(ids, vec![0, 1, 2, 3, 4]);
163 }
164
165 #[test]
166 fn pagination_bounds_clamps_and_reports_can_next_prev() {
167 let b = pagination_bounds(
168 5,
169 PaginationState {
170 page_index: 0,
171 page_size: 2,
172 },
173 );
174 assert_eq!(
175 b,
176 PaginationBounds {
177 page_index: 0,
178 page_count: 3,
179 can_prev: false,
180 can_next: true,
181 page_start: 0,
182 page_end: 2,
183 }
184 );
185
186 let b = pagination_bounds(
187 5,
188 PaginationState {
189 page_index: 10,
190 page_size: 2,
191 },
192 );
193 assert_eq!(b.page_index, 2);
194 assert_eq!(b.page_count, 3);
195 assert!(!b.can_next);
196 assert!(b.can_prev);
197 assert_eq!((b.page_start, b.page_end), (4, 5));
198
199 let b = pagination_bounds(
200 0,
201 PaginationState {
202 page_index: 0,
203 page_size: 10,
204 },
205 );
206 assert_eq!(b.page_count, 0);
207 assert_eq!(b.page_index, 0);
208 assert!(!b.can_prev);
209 assert!(!b.can_next);
210 }
211}