agentic_forge_core/query/
pagination.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct CursorPage<T> {
7 pub items: Vec<T>,
8 pub cursor: Option<String>,
9 pub has_more: bool,
10 pub total: Option<usize>,
11}
12
13impl<T> CursorPage<T> {
14 pub fn empty() -> Self {
15 Self {
16 items: Vec::new(),
17 cursor: None,
18 has_more: false,
19 total: Some(0),
20 }
21 }
22
23 pub fn from_slice(all: Vec<T>, cursor: Option<&str>, limit: usize) -> Self
24 where
25 T: Clone,
26 {
27 let start = cursor.and_then(|c| c.parse::<usize>().ok()).unwrap_or(0);
28 let total = all.len();
29
30 if start >= total {
31 return Self {
32 items: Vec::new(),
33 cursor: None,
34 has_more: false,
35 total: Some(total),
36 };
37 }
38
39 let end = (start + limit).min(total);
40 let items = all[start..end].to_vec();
41 let has_more = end < total;
42 let next_cursor = if has_more {
43 Some(end.to_string())
44 } else {
45 None
46 };
47
48 Self {
49 items,
50 cursor: next_cursor,
51 has_more,
52 total: Some(total),
53 }
54 }
55
56 pub fn len(&self) -> usize {
57 self.items.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
61 self.items.is_empty()
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_cursor_page_first() {
71 let data: Vec<i32> = (0..20).collect();
72 let page = CursorPage::from_slice(data, None, 5);
73 assert_eq!(page.len(), 5);
74 assert!(page.has_more);
75 assert_eq!(page.cursor, Some("5".into()));
76 assert_eq!(page.total, Some(20));
77 }
78
79 #[test]
80 fn test_cursor_page_second() {
81 let data: Vec<i32> = (0..20).collect();
82 let page = CursorPage::from_slice(data, Some("5"), 5);
83 assert_eq!(page.items, vec![5, 6, 7, 8, 9]);
84 assert!(page.has_more);
85 }
86
87 #[test]
88 fn test_cursor_page_last() {
89 let data: Vec<i32> = (0..20).collect();
90 let page = CursorPage::from_slice(data, Some("15"), 5);
91 assert_eq!(page.len(), 5);
92 assert!(!page.has_more);
93 assert_eq!(page.cursor, None);
94 }
95
96 #[test]
97 fn test_cursor_page_past_end() {
98 let data: Vec<i32> = (0..5).collect();
99 let page = CursorPage::from_slice(data, Some("100"), 5);
100 assert!(page.is_empty());
101 assert!(!page.has_more);
102 }
103
104 #[test]
105 fn test_cursor_page_empty() {
106 let page: CursorPage<i32> = CursorPage::empty();
107 assert!(page.is_empty());
108 assert_eq!(page.total, Some(0));
109 }
110
111 #[test]
112 fn test_cursor_page_exact_boundary() {
113 let data: Vec<i32> = (0..10).collect();
114 let page = CursorPage::from_slice(data, None, 10);
115 assert_eq!(page.len(), 10);
116 assert!(!page.has_more);
117 }
118
119 #[test]
120 fn test_cursor_page_limit_larger_than_data() {
121 let data: Vec<i32> = (0..3).collect();
122 let page = CursorPage::from_slice(data, None, 100);
123 assert_eq!(page.len(), 3);
124 assert!(!page.has_more);
125 }
126}