agentic_evolve_core/query/
pagination.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct CursorPage<T> {
8 pub items: Vec<T>,
10 pub next_cursor: Option<String>,
12 pub has_more: bool,
14 pub total: Option<usize>,
16}
17
18impl<T: Clone> CursorPage<T> {
19 pub fn from_slice(data: &[T], cursor: Option<&str>, limit: usize) -> Self {
23 let start = cursor.and_then(|c| c.parse::<usize>().ok()).unwrap_or(0);
24
25 let clamped_start = start.min(data.len());
26 let end = (clamped_start + limit).min(data.len());
27 let items = data[clamped_start..end].to_vec();
28 let has_more = end < data.len();
29 let next_cursor = if has_more {
30 Some(end.to_string())
31 } else {
32 None
33 };
34
35 Self {
36 items,
37 next_cursor,
38 has_more,
39 total: Some(data.len()),
40 }
41 }
42
43 pub fn count(&self) -> usize {
45 self.items.len()
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.items.is_empty()
51 }
52
53 pub fn map<U: Clone, F: Fn(&T) -> U>(&self, f: F) -> CursorPage<U> {
55 CursorPage {
56 items: self.items.iter().map(f).collect(),
57 next_cursor: self.next_cursor.clone(),
58 has_more: self.has_more,
59 total: self.total,
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn from_slice_first_page() {
70 let data: Vec<i32> = (0..10).collect();
71 let page = CursorPage::from_slice(&data, None, 3);
72 assert_eq!(page.items, vec![0, 1, 2]);
73 assert!(page.has_more);
74 assert_eq!(page.next_cursor, Some("3".to_string()));
75 assert_eq!(page.total, Some(10));
76 }
77
78 #[test]
79 fn from_slice_second_page() {
80 let data: Vec<i32> = (0..10).collect();
81 let page = CursorPage::from_slice(&data, Some("3"), 3);
82 assert_eq!(page.items, vec![3, 4, 5]);
83 assert!(page.has_more);
84 assert_eq!(page.next_cursor, Some("6".to_string()));
85 }
86
87 #[test]
88 fn from_slice_last_page() {
89 let data: Vec<i32> = (0..10).collect();
90 let page = CursorPage::from_slice(&data, Some("8"), 5);
91 assert_eq!(page.items, vec![8, 9]);
92 assert!(!page.has_more);
93 assert_eq!(page.next_cursor, None);
94 }
95
96 #[test]
97 fn from_slice_exact_fit() {
98 let data = vec![1, 2, 3];
99 let page = CursorPage::from_slice(&data, None, 3);
100 assert_eq!(page.items, vec![1, 2, 3]);
101 assert!(!page.has_more);
102 }
103
104 #[test]
105 fn from_slice_empty() {
106 let data: Vec<i32> = vec![];
107 let page = CursorPage::from_slice(&data, None, 10);
108 assert!(page.is_empty());
109 assert!(!page.has_more);
110 }
111
112 #[test]
113 fn from_slice_cursor_beyond_end() {
114 let data = vec![1, 2, 3];
115 let page = CursorPage::from_slice(&data, Some("100"), 5);
116 assert!(page.is_empty());
117 assert!(!page.has_more);
118 }
119
120 #[test]
121 fn map_transforms_items() {
122 let data = vec![1, 2, 3];
123 let page = CursorPage::from_slice(&data, None, 10);
124 let mapped = page.map(|x| x * 10);
125 assert_eq!(mapped.items, vec![10, 20, 30]);
126 }
127
128 #[test]
129 fn count_and_is_empty() {
130 let data = vec![1, 2];
131 let page = CursorPage::from_slice(&data, None, 10);
132 assert_eq!(page.count(), 2);
133 assert!(!page.is_empty());
134 }
135}