icydb_core/db/response/
mod.rs

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