paginator_utils/
cursor.rs

1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
5pub struct Cursor {
6    pub field: String,
7    pub value: CursorValue,
8    pub direction: CursorDirection,
9}
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12#[serde(rename_all = "lowercase")]
13pub enum CursorDirection {
14    After,
15    Before,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
19#[serde(untagged)]
20pub enum CursorValue {
21    String(String),
22    Int(i64),
23    Float(f64),
24}
25
26impl Cursor {
27    pub fn new(field: String, value: CursorValue, direction: CursorDirection) -> Self {
28        Self {
29            field,
30            value,
31            direction,
32        }
33    }
34
35    pub fn encode(&self) -> Result<String, String> {
36        let json = serde_json::to_string(self).map_err(|e| e.to_string())?;
37        Ok(BASE64.encode(json.as_bytes()))
38    }
39
40    pub fn decode(encoded: &str) -> Result<Self, String> {
41        let decoded = BASE64.decode(encoded).map_err(|e| e.to_string())?;
42        let json = String::from_utf8(decoded).map_err(|e| e.to_string())?;
43        serde_json::from_str(&json).map_err(|e| e.to_string())
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_cursor_encode_decode_string() {
53        let cursor = Cursor::new(
54            "id".to_string(),
55            CursorValue::String("abc123".to_string()),
56            CursorDirection::After,
57        );
58        let encoded = cursor.encode().unwrap();
59        let decoded = Cursor::decode(&encoded).unwrap();
60        assert_eq!(cursor, decoded);
61    }
62
63    #[test]
64    fn test_cursor_encode_decode_int() {
65        let cursor = Cursor::new(
66            "id".to_string(),
67            CursorValue::Int(12345),
68            CursorDirection::Before,
69        );
70        let encoded = cursor.encode().unwrap();
71        let decoded = Cursor::decode(&encoded).unwrap();
72        assert_eq!(cursor, decoded);
73    }
74
75    #[test]
76    fn test_cursor_encode_decode_float() {
77        let cursor = Cursor::new(
78            "timestamp".to_string(),
79            CursorValue::Float(1234567890.123),
80            CursorDirection::After,
81        );
82        let encoded = cursor.encode().unwrap();
83        let decoded = Cursor::decode(&encoded).unwrap();
84        assert_eq!(cursor, decoded);
85    }
86}