icydb_core/db/response/
mod.rs

1mod ext;
2
3pub use ext::*;
4
5use crate::{Error, Key, ThisError, db::DbError, traits::EntityKind};
6
7///
8/// ResponseError
9/// Errors related to interpreting a materialized response.
10///
11
12#[derive(Debug, ThisError)]
13pub enum ResponseError {
14    #[error("expected exactly one row, found 0 (entity {entity})")]
15    NotFound { entity: &'static str },
16
17    #[error("expected exactly one row, found {count} (entity {entity})")]
18    NotUnique { entity: &'static str, count: u32 },
19}
20
21impl From<ResponseError> for Error {
22    fn from(err: ResponseError) -> Self {
23        DbError::from(err).into()
24    }
25}
26
27///
28/// Response
29/// Materialized query result: ordered `(Key, Entity)` pairs.
30///
31
32#[derive(Debug)]
33pub struct Response<E: EntityKind>(pub Vec<(Key, E)>);
34
35impl<E: EntityKind> Response<E> {
36    // ======================================================================
37    // Cardinality (introspection only)
38    // ======================================================================
39
40    /// Number of rows in the response, truncated to `u32`.
41    #[must_use]
42    #[allow(clippy::cast_possible_truncation)]
43    pub const fn count(&self) -> u32 {
44        self.0.len() as u32
45    }
46
47    /// True when no rows were returned.
48    #[must_use]
49    pub const fn is_empty(&self) -> bool {
50        self.0.is_empty()
51    }
52
53    // ======================================================================
54    // Cardinality guards (non-consuming)
55    // ======================================================================
56
57    /// Require exactly one row.
58    pub fn require_one(&self) -> Result<(), Error> {
59        match self.count() {
60            1 => Ok(()),
61            0 => Err(ResponseError::NotFound { entity: E::PATH }.into()),
62            n => Err(ResponseError::NotUnique {
63                entity: E::PATH,
64                count: n,
65            }
66            .into()),
67        }
68    }
69
70    // ======================================================================
71    // Row extractors (consume self)
72    // ======================================================================
73
74    /// Require exactly one row and return it.
75    pub fn one(self) -> Result<(Key, E), Error> {
76        self.require_one()?;
77        Ok(self.0.into_iter().next().unwrap())
78    }
79
80    /// Require at most one row and return it.
81    pub fn one_opt(self) -> Result<Option<(Key, E)>, Error> {
82        match self.count() {
83            0 => Ok(None),
84            1 => Ok(Some(self.0.into_iter().next().unwrap())),
85            n => Err(ResponseError::NotUnique {
86                entity: E::PATH,
87                count: n,
88            }
89            .into()),
90        }
91    }
92
93    // ======================================================================
94    // Key extractors
95    // ======================================================================
96
97    /// First key in the response, if present.
98    #[must_use]
99    pub fn key(&self) -> Option<Key> {
100        self.0.first().map(|(k, _)| *k)
101    }
102
103    /// Collect all keys in order.
104    #[must_use]
105    pub fn keys(&self) -> Vec<Key> {
106        self.0.iter().map(|(k, _)| *k).collect()
107    }
108
109    /// Require exactly one row and return its key.
110    pub fn one_key(self) -> Result<Key, Error> {
111        self.one().map(|(k, _)| k)
112    }
113
114    /// Require at most one row and return its key.
115    pub fn one_opt_key(self) -> Result<Option<Key>, Error> {
116        Ok(self.one_opt()?.map(|(k, _)| k))
117    }
118
119    #[must_use]
120    pub fn contains_key(&self, key: &Key) -> bool {
121        self.0.iter().any(|(k, _)| k == key)
122    }
123
124    // ======================================================================
125    // Entity extractors
126    // ======================================================================
127
128    /// Consume the response and return the first entity, if any.
129    #[must_use]
130    pub fn entity(self) -> Option<E> {
131        self.0.into_iter().next().map(|(_, e)| e)
132    }
133
134    /// Consume the response and collect all entities.
135    #[must_use]
136    pub fn entities(self) -> Vec<E> {
137        self.0.into_iter().map(|(_, e)| e).collect()
138    }
139
140    /// Require exactly one entity.
141    pub fn one_entity(self) -> Result<E, Error> {
142        self.one().map(|(_, e)| e)
143    }
144
145    /// Require at most one entity.
146    pub fn one_opt_entity(self) -> Result<Option<E>, Error> {
147        Ok(self.one_opt()?.map(|(_, e)| e))
148    }
149
150    // ======================================================================
151    // Primary key extractors
152    // ======================================================================
153
154    /// First primary key in the response, if present.
155    #[must_use]
156    pub fn pk(&self) -> Option<E::PrimaryKey> {
157        self.0.first().map(|(_, e)| e.primary_key())
158    }
159
160    /// Collect all primary keys in order.
161    #[must_use]
162    pub fn pks(&self) -> Vec<E::PrimaryKey> {
163        self.0.iter().map(|(_, e)| e.primary_key()).collect()
164    }
165
166    /// Require exactly one primary key.
167    pub fn one_pk(self) -> Result<E::PrimaryKey, Error> {
168        self.one_entity().map(|e| e.primary_key())
169    }
170
171    /// Require at most one primary key.
172    pub fn one_opt_pk(self) -> Result<Option<E::PrimaryKey>, Error> {
173        Ok(self.one_opt_entity()?.map(|e| e.primary_key()))
174    }
175
176    // ======================================================================
177    // View extractors
178    // ======================================================================
179
180    /// Convert the first entity to its view type, if present.
181    #[must_use]
182    pub fn view(self) -> Option<E::ViewType> {
183        self.entity().map(|e| e.to_view())
184    }
185
186    /// Require exactly one view.
187    pub fn one_view(self) -> Result<E::ViewType, Error> {
188        self.one_entity().map(|e| e.to_view())
189    }
190
191    /// Require at most one view.
192    pub fn one_opt_view(self) -> Result<Option<E::ViewType>, Error> {
193        Ok(self.one_opt_entity()?.map(|e| e.to_view()))
194    }
195
196    /// Convert all entities to their view types.
197    #[must_use]
198    pub fn views(self) -> Vec<E::ViewType> {
199        self.entities().into_iter().map(|e| e.to_view()).collect()
200    }
201
202    // ======================================================================
203    // Arbitrary row access (no cardinality guarantees)
204    // ======================================================================
205
206    /// Return the first row in the response, if any.
207    ///
208    /// This does NOT enforce cardinality. Use only when row order is
209    /// well-defined and uniqueness is irrelevant.
210    #[must_use]
211    pub fn first(self) -> Option<(Key, E)> {
212        self.0.into_iter().next()
213    }
214
215    /// Return the first entity in the response, if any.
216    ///
217    /// This does NOT enforce cardinality. Use only when row order is
218    /// well-defined and uniqueness is irrelevant.
219    #[must_use]
220    pub fn first_entity(self) -> Option<E> {
221        self.first().map(|(_, e)| e)
222    }
223
224    /// Return the first primary key in the response, if any.
225    ///
226    /// This does NOT enforce cardinality.
227    #[must_use]
228    pub fn first_pk(self) -> Option<E::PrimaryKey> {
229        self.first_entity().map(|e| e.primary_key())
230    }
231}
232
233impl<E: EntityKind> IntoIterator for Response<E> {
234    type Item = (Key, E);
235    type IntoIter = std::vec::IntoIter<Self::Item>;
236
237    fn into_iter(self) -> Self::IntoIter {
238        self.0.into_iter()
239    }
240}