1use axum::{
2 response::{IntoResponse, Response},
3 Json,
4};
5use serde::Serialize;
6
7#[derive(Debug, Clone, Serialize)]
38pub struct CursorResponse<T: Serialize> {
39 pub data: Vec<T>,
41 #[serde(skip_serializing_if = "Option::is_none")]
44 pub next_cursor: Option<String>,
45 pub has_more: bool,
47}
48
49impl<T: Serialize> IntoResponse for CursorResponse<T> {
50 fn into_response(self) -> Response {
51 Json(self).into_response()
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use serde_json::json;
59
60 #[derive(Serialize)]
61 struct Item {
62 id: String,
63 }
64
65 #[test]
66 fn serializes_with_next_cursor() {
67 let resp = CursorResponse {
68 data: vec![Item { id: "1".into() }],
69 next_cursor: Some("cursor_abc".into()),
70 has_more: true,
71 };
72 let v = serde_json::to_value(&resp).unwrap();
73 assert_eq!(v["data"][0]["id"], "1");
74 assert_eq!(v["next_cursor"], "cursor_abc");
75 assert_eq!(v["has_more"], true);
76 }
77
78 #[test]
79 fn serializes_without_next_cursor_when_none() {
80 let resp: CursorResponse<Item> = CursorResponse {
81 data: vec![],
82 next_cursor: None,
83 has_more: false,
84 };
85 let v = serde_json::to_value(&resp).unwrap();
86 assert!(v.get("next_cursor").is_none());
87 assert_eq!(v["has_more"], false);
88 }
89
90 #[test]
91 fn has_more_false_at_end() {
92 let resp = CursorResponse {
93 data: vec![Item { id: "final".into() }],
94 next_cursor: None,
95 has_more: false,
96 };
97 let v = serde_json::to_value(&resp).unwrap();
98 assert_eq!(v["has_more"], false);
99 assert!(v.get("next_cursor").is_none());
100 }
101
102 #[test]
103 fn has_more_true_with_cursor() {
104 let resp = CursorResponse {
105 data: vec![Item { id: "x".into() }],
106 next_cursor: Some("token".into()),
107 has_more: true,
108 };
109 let v = serde_json::to_value(&resp).unwrap();
110 assert_eq!(v["has_more"], true);
111 assert_eq!(v["next_cursor"], "token");
112 }
113
114 #[test]
115 fn empty_data_with_no_cursor() {
116 let resp: CursorResponse<Item> = CursorResponse {
117 data: vec![],
118 next_cursor: None,
119 has_more: false,
120 };
121 let v = serde_json::to_value(&resp).unwrap();
122 assert_eq!(v["data"], json!([]));
123 assert_eq!(v["has_more"], false);
124 }
125
126 #[test]
127 fn multiple_items() {
128 let resp = CursorResponse {
129 data: vec![
130 Item { id: "1".into() },
131 Item { id: "2".into() },
132 Item { id: "3".into() },
133 ],
134 next_cursor: Some("next".into()),
135 has_more: true,
136 };
137 let v = serde_json::to_value(&resp).unwrap();
138 assert_eq!(v["data"].as_array().unwrap().len(), 3);
139 assert_eq!(v["data"][1]["id"], "2");
140 }
141}