Skip to main content

use_db_result/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Result set and page metadata primitives for `RustUse`.
5
6/// A cursor position label.
7#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct CursorPosition(String);
9
10impl CursorPosition {
11    /// Creates a cursor position label.
12    #[must_use]
13    pub fn new(value: impl Into<String>) -> Self {
14        Self(value.into())
15    }
16
17    /// Returns the cursor position label.
18    #[must_use]
19    pub fn as_str(&self) -> &str {
20        &self.0
21    }
22}
23
24/// Total result count metadata.
25#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct TotalCount(u64);
27
28impl TotalCount {
29    /// Creates total-count metadata.
30    #[must_use]
31    pub const fn new(value: u64) -> Self {
32        Self(value)
33    }
34
35    /// Returns the count value.
36    #[must_use]
37    pub const fn value(self) -> u64 {
38        self.0
39    }
40}
41
42/// Whether more results are available.
43#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct HasMore(bool);
45
46impl HasMore {
47    /// Creates a has-more marker.
48    #[must_use]
49    pub const fn new(value: bool) -> Self {
50        Self(value)
51    }
52
53    /// Returns the marker value.
54    #[must_use]
55    pub const fn value(self) -> bool {
56        self.0
57    }
58}
59
60/// Result set metadata.
61#[derive(Clone, Debug, Default, Eq, PartialEq)]
62pub struct ResultSetMetadata {
63    total_count: Option<TotalCount>,
64    has_more: HasMore,
65}
66
67impl ResultSetMetadata {
68    /// Creates result set metadata.
69    #[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    /// Returns total-count metadata.
78    #[must_use]
79    pub const fn total_count(&self) -> Option<TotalCount> {
80        self.total_count
81    }
82
83    /// Returns has-more metadata.
84    #[must_use]
85    pub const fn has_more(&self) -> HasMore {
86        self.has_more
87    }
88}
89
90/// Generic result set container.
91#[derive(Clone, Debug, Default, Eq, PartialEq)]
92pub struct ResultSet<T> {
93    items: Vec<T>,
94    metadata: ResultSetMetadata,
95}
96
97impl<T> ResultSet<T> {
98    /// Creates a result set.
99    #[must_use]
100    pub const fn new(items: Vec<T>, metadata: ResultSetMetadata) -> Self {
101        Self { items, metadata }
102    }
103
104    /// Returns result items.
105    #[must_use]
106    pub fn items(&self) -> &[T] {
107        &self.items
108    }
109
110    /// Returns result set metadata.
111    #[must_use]
112    pub const fn metadata(&self) -> &ResultSetMetadata {
113        &self.metadata
114    }
115
116    /// Returns the item count.
117    #[must_use]
118    pub fn len(&self) -> usize {
119        self.items.len()
120    }
121
122    /// Returns whether the result set is empty.
123    #[must_use]
124    pub fn is_empty(&self) -> bool {
125        self.items.is_empty()
126    }
127}
128
129/// Page metadata.
130#[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    /// Creates page metadata.
139    #[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    /// Returns the optional cursor.
153    #[must_use]
154    pub const fn cursor(&self) -> Option<&CursorPosition> {
155        self.cursor.as_ref()
156    }
157
158    /// Returns has-more metadata.
159    #[must_use]
160    pub const fn has_more(&self) -> HasMore {
161        self.has_more
162    }
163
164    /// Returns total-count metadata.
165    #[must_use]
166    pub const fn total_count(&self) -> Option<TotalCount> {
167        self.total_count
168    }
169}
170
171/// Generic result page.
172#[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    /// Creates a result page.
180    #[must_use]
181    pub const fn new(items: Vec<T>, page_info: PageInfo) -> Self {
182        Self { items, page_info }
183    }
184
185    /// Returns page items.
186    #[must_use]
187    pub fn items(&self) -> &[T] {
188        &self.items
189    }
190
191    /// Returns page metadata.
192    #[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}