Skip to main content

robinpath_modules/modules/
pagination_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    // pagination.paginate(totalItems, currentPage, pageSize) -> object
5    rp.register_builtin("pagination.paginate", |args, _| {
6        let total_items = args.first().map(|v| v.to_number()).unwrap_or(0.0) as i64;
7        let current_page = args.get(1).map(|v| v.to_number()).unwrap_or(1.0) as i64;
8        let page_size = args.get(2).map(|v| v.to_number()).unwrap_or(10.0) as i64;
9
10        let page_size = page_size.max(1);
11        let total_pages = if total_items <= 0 {
12            0
13        } else {
14            (total_items + page_size - 1) / page_size
15        };
16        let current_page = current_page.clamp(1, total_pages.max(1));
17        let start_index = (current_page - 1) * page_size;
18        let end_index = (start_index + page_size - 1).min(total_items - 1).max(0);
19        let has_next = current_page < total_pages;
20        let has_prev = current_page > 1;
21
22        let mut obj = indexmap::IndexMap::new();
23        obj.insert("totalItems".to_string(), Value::Number(total_items as f64));
24        obj.insert("pageSize".to_string(), Value::Number(page_size as f64));
25        obj.insert("currentPage".to_string(), Value::Number(current_page as f64));
26        obj.insert("totalPages".to_string(), Value::Number(total_pages as f64));
27        obj.insert("startIndex".to_string(), Value::Number(start_index as f64));
28        obj.insert("endIndex".to_string(), Value::Number(end_index as f64));
29        obj.insert("hasNext".to_string(), Value::Bool(has_next));
30        obj.insert("hasPrev".to_string(), Value::Bool(has_prev));
31        Ok(Value::Object(obj))
32    });
33
34    // pagination.offset(page, pageSize) -> offset number
35    rp.register_builtin("pagination.offset", |args, _| {
36        let page = args.first().map(|v| v.to_number()).unwrap_or(1.0) as i64;
37        let page_size = args.get(1).map(|v| v.to_number()).unwrap_or(10.0) as i64;
38        let offset = (page.max(1) - 1) * page_size.max(1);
39        Ok(Value::Number(offset as f64))
40    });
41
42    // pagination.cursor(items, cursorField, limit) -> {items, nextCursor, hasMore}
43    rp.register_builtin("pagination.cursor", |args, _| {
44        let items = match args.first() {
45            Some(Value::Array(arr)) => arr.clone(),
46            _ => return Err("pagination.cursor expects an array as first argument".to_string()),
47        };
48        let cursor_field = args.get(1).map(|v| v.to_display_string()).unwrap_or_else(|| "id".to_string());
49        let limit = args.get(2).map(|v| v.to_number()).unwrap_or(10.0) as usize;
50
51        let has_more = items.len() > limit;
52        let page_items: Vec<Value> = items.into_iter().take(limit).collect();
53
54        let next_cursor = if has_more {
55            // Get cursor field from last item
56            if let Some(Value::Object(last)) = page_items.last() {
57                last.get(&cursor_field)
58                    .cloned()
59                    .unwrap_or(Value::Null)
60            } else {
61                Value::Null
62            }
63        } else {
64            Value::Null
65        };
66
67        let mut obj = indexmap::IndexMap::new();
68        obj.insert("items".to_string(), Value::Array(page_items));
69        obj.insert("nextCursor".to_string(), next_cursor);
70        obj.insert("hasMore".to_string(), Value::Bool(has_more));
71        Ok(Value::Object(obj))
72    });
73
74    // pagination.hasNext(currentPage, totalPages) -> bool
75    rp.register_builtin("pagination.hasNext", |args, _| {
76        let current_page = args.first().map(|v| v.to_number()).unwrap_or(1.0) as i64;
77        let total_pages = args.get(1).map(|v| v.to_number()).unwrap_or(1.0) as i64;
78        Ok(Value::Bool(current_page < total_pages))
79    });
80
81    // pagination.hasPrev(currentPage) -> bool
82    rp.register_builtin("pagination.hasPrev", |args, _| {
83        let current_page = args.first().map(|v| v.to_number()).unwrap_or(1.0) as i64;
84        Ok(Value::Bool(current_page > 1))
85    });
86
87    // pagination.totalPages(totalItems, pageSize) -> number
88    rp.register_builtin("pagination.totalPages", |args, _| {
89        let total_items = args.first().map(|v| v.to_number()).unwrap_or(0.0) as i64;
90        let page_size = args.get(1).map(|v| v.to_number()).unwrap_or(10.0) as i64;
91        let page_size = page_size.max(1);
92        let total_pages = if total_items <= 0 {
93            0
94        } else {
95            (total_items + page_size - 1) / page_size
96        };
97        Ok(Value::Number(total_pages as f64))
98    });
99}