1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct CursorPosition(String);
9
10impl CursorPosition {
11 #[must_use]
13 pub fn new(value: impl Into<String>) -> Self {
14 Self(value.into())
15 }
16
17 #[must_use]
19 pub fn as_str(&self) -> &str {
20 &self.0
21 }
22}
23
24#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct TotalCount(u64);
27
28impl TotalCount {
29 #[must_use]
31 pub const fn new(value: u64) -> Self {
32 Self(value)
33 }
34
35 #[must_use]
37 pub const fn value(self) -> u64 {
38 self.0
39 }
40}
41
42#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct HasMore(bool);
45
46impl HasMore {
47 #[must_use]
49 pub const fn new(value: bool) -> Self {
50 Self(value)
51 }
52
53 #[must_use]
55 pub const fn value(self) -> bool {
56 self.0
57 }
58}
59
60#[derive(Clone, Debug, Default, Eq, PartialEq)]
62pub struct ResultSetMetadata {
63 total_count: Option<TotalCount>,
64 has_more: HasMore,
65}
66
67impl ResultSetMetadata {
68 #[must_use]
70 pub const fn new(total_count: Option<TotalCount>, has_more: HasMore) -> Self {
71 Self {
72 total_count,
73 has_more,
74 }
75 }
76
77 #[must_use]
79 pub const fn total_count(&self) -> Option<TotalCount> {
80 self.total_count
81 }
82
83 #[must_use]
85 pub const fn has_more(&self) -> HasMore {
86 self.has_more
87 }
88}
89
90#[derive(Clone, Debug, Default, Eq, PartialEq)]
92pub struct ResultSet<T> {
93 items: Vec<T>,
94 metadata: ResultSetMetadata,
95}
96
97impl<T> ResultSet<T> {
98 #[must_use]
100 pub const fn new(items: Vec<T>, metadata: ResultSetMetadata) -> Self {
101 Self { items, metadata }
102 }
103
104 #[must_use]
106 pub fn items(&self) -> &[T] {
107 &self.items
108 }
109
110 #[must_use]
112 pub const fn metadata(&self) -> &ResultSetMetadata {
113 &self.metadata
114 }
115
116 #[must_use]
118 pub fn len(&self) -> usize {
119 self.items.len()
120 }
121
122 #[must_use]
124 pub fn is_empty(&self) -> bool {
125 self.items.is_empty()
126 }
127}
128
129#[derive(Clone, Debug, Default, Eq, PartialEq)]
131pub struct PageInfo {
132 cursor: Option<CursorPosition>,
133 total_count: Option<TotalCount>,
134 has_more: HasMore,
135}
136
137impl PageInfo {
138 #[must_use]
140 pub const fn new(
141 cursor: Option<CursorPosition>,
142 total_count: Option<TotalCount>,
143 has_more: HasMore,
144 ) -> Self {
145 Self {
146 cursor,
147 total_count,
148 has_more,
149 }
150 }
151
152 #[must_use]
154 pub const fn cursor(&self) -> Option<&CursorPosition> {
155 self.cursor.as_ref()
156 }
157
158 #[must_use]
160 pub const fn has_more(&self) -> HasMore {
161 self.has_more
162 }
163
164 #[must_use]
166 pub const fn total_count(&self) -> Option<TotalCount> {
167 self.total_count
168 }
169}
170
171#[derive(Clone, Debug, Default, Eq, PartialEq)]
173pub struct ResultPage<T> {
174 items: Vec<T>,
175 page_info: PageInfo,
176}
177
178impl<T> ResultPage<T> {
179 #[must_use]
181 pub const fn new(items: Vec<T>, page_info: PageInfo) -> Self {
182 Self { items, page_info }
183 }
184
185 #[must_use]
187 pub fn items(&self) -> &[T] {
188 &self.items
189 }
190
191 #[must_use]
193 pub const fn page_info(&self) -> &PageInfo {
194 &self.page_info
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::{
201 CursorPosition, HasMore, PageInfo, ResultPage, ResultSet, ResultSetMetadata, TotalCount,
202 };
203
204 #[test]
205 fn stores_result_sets_and_pages() {
206 let metadata = ResultSetMetadata::new(Some(TotalCount::new(2)), HasMore::new(false));
207 let result_set = ResultSet::new(vec!["a", "b"], metadata);
208 let page_info = PageInfo::new(
209 Some(CursorPosition::new("next")),
210 Some(TotalCount::new(2)),
211 HasMore::new(true),
212 );
213 let page = ResultPage::new(vec![1, 2], page_info);
214
215 assert_eq!(result_set.len(), 2);
216 assert!(!result_set.is_empty());
217 assert_eq!(
218 result_set.metadata().total_count().expect("total").value(),
219 2
220 );
221 assert_eq!(page.page_info().cursor().expect("cursor").as_str(), "next");
222 assert!(page.page_info().has_more().value());
223 }
224}