Skip to main content

icydb_core/db/response/
mod.rs

1//! Module: response
2//! Responsibility: materialized query/write response payload contracts.
3//! Does not own: execution routing, planning policy, or cursor token protocol.
4//! Boundary: Tier-2 db API DTO surface returned by session execution.
5//! Architecture: `Response<R>` is transport-only and row-shape-agnostic.
6//! Query semantics (for example cardinality checks) must live in query/session
7//! extension traits rather than inherent response DTO methods.
8
9mod grouped;
10mod paged;
11
12use crate::{prelude::*, traits::EntityValue, types::Id, value::Value};
13use thiserror::Error as ThisError;
14
15mod private {
16    ///
17    /// Sealed
18    ///
19    /// Internal marker used to seal response row-shape marker implementations.
20    ///
21
22    pub trait Sealed {}
23}
24
25pub(in crate::db) use grouped::GroupedTextCursorPageWithTrace;
26pub use grouped::{GroupedRow, PagedGroupedExecution, PagedGroupedExecutionWithTrace};
27pub use paged::{PagedLoadExecution, PagedLoadExecutionWithTrace};
28
29///
30/// ResponseRow
31///
32/// Marker trait for row-shape DTOs that are valid payloads for `Response<R>`.
33/// This trait is sealed to keep row-shape admission local to the response layer.
34///
35
36pub trait ResponseRow: private::Sealed {}
37
38impl ResponseRow for GroupedRow {}
39
40impl private::Sealed for GroupedRow {}
41
42///
43/// Row
44///
45/// Materialized entity row with explicit identity and payload accessors.
46///
47
48#[derive(Clone, Debug, Eq, PartialEq)]
49pub struct Row<E: EntityKind> {
50    id: Id<E>,
51    entity: E,
52}
53
54impl<E: EntityKind> Row<E> {
55    /// Construct one row from identity and entity payload.
56    #[must_use]
57    pub const fn new(id: Id<E>, entity: E) -> Self {
58        Self { id, entity }
59    }
60
61    /// Borrow this row's identity.
62    #[must_use]
63    pub const fn id(&self) -> Id<E> {
64        self.id
65    }
66
67    /// Consume and return this row's entity payload.
68    #[must_use]
69    pub fn entity(self) -> E {
70        self.entity
71    }
72
73    /// Borrow this row's entity payload.
74    #[must_use]
75    pub const fn entity_ref(&self) -> &E {
76        &self.entity
77    }
78
79    /// Consume and return `(id, entity)` parts.
80    #[must_use]
81    pub fn into_parts(self) -> (Id<E>, E) {
82        (self.id, self.entity)
83    }
84}
85
86impl<E: EntityKind> From<(Id<E>, E)> for Row<E> {
87    fn from(value: (Id<E>, E)) -> Self {
88        Self::new(value.0, value.1)
89    }
90}
91
92impl<E: EntityKind> private::Sealed for Row<E> {}
93
94impl<E: EntityKind> ResponseRow for Row<E> {}
95
96///
97/// ProjectedRow
98///
99/// One scalar projection output row emitted in planner declaration order.
100/// `values` carries evaluated expression outputs for this row.
101///
102
103#[derive(Clone, Debug, Eq, PartialEq)]
104pub struct ProjectedRow<E: EntityKind> {
105    id: Id<E>,
106    values: Vec<Value>,
107}
108
109impl<E: EntityKind> ProjectedRow<E> {
110    /// Construct one projected scalar row.
111    #[must_use]
112    pub const fn new(id: Id<E>, values: Vec<Value>) -> Self {
113        Self { id, values }
114    }
115
116    /// Borrow the source row identifier.
117    #[must_use]
118    pub const fn id(&self) -> Id<E> {
119        self.id
120    }
121
122    /// Borrow projected scalar values in declaration order.
123    #[must_use]
124    pub const fn values(&self) -> &[Value] {
125        self.values.as_slice()
126    }
127
128    /// Consume and return `(id, projected_values)`.
129    #[must_use]
130    pub fn into_parts(self) -> (Id<E>, Vec<Value>) {
131        (self.id, self.values)
132    }
133}
134
135impl<E: EntityKind> private::Sealed for ProjectedRow<E> {}
136
137impl<E: EntityKind> ResponseRow for ProjectedRow<E> {}
138
139///
140/// ResponseError
141///
142
143#[derive(Debug, ThisError)]
144pub enum ResponseError {
145    #[error("expected exactly one row, found 0 (entity {entity})")]
146    NotFound { entity: &'static str },
147
148    #[error("expected exactly one row, found {count} (entity {entity})")]
149    NotUnique { entity: &'static str, count: u32 },
150}
151
152impl ResponseError {
153    /// Construct one response not-found cardinality error.
154    #[must_use]
155    pub const fn not_found(entity: &'static str) -> Self {
156        Self::NotFound { entity }
157    }
158
159    /// Construct one response not-unique cardinality error.
160    #[must_use]
161    pub const fn not_unique(entity: &'static str, count: u32) -> Self {
162        Self::NotUnique { entity, count }
163    }
164}
165
166///
167/// Response
168///
169/// Generic response transport container for one row shape `R`.
170///
171
172#[derive(Debug)]
173pub struct Response<R: ResponseRow>(Vec<R>);
174
175///
176/// EntityResponse
177///
178/// Entity-row response transport alias.
179///
180
181pub type EntityResponse<E> = Response<Row<E>>;
182
183///
184/// ProjectionResponse
185///
186/// Scalar projection response transport alias.
187///
188
189pub type ProjectionResponse<E> = Response<ProjectedRow<E>>;
190
191impl<R: ResponseRow> Response<R> {
192    /// Construct one response from ordered rows.
193    #[must_use]
194    pub const fn new(rows: Vec<R>) -> Self {
195        Self(rows)
196    }
197
198    /// Construct one response from rows convertible into `R`.
199    #[must_use]
200    pub fn from_rows<T>(rows: Vec<T>) -> Self
201    where
202        T: Into<R>,
203    {
204        Self(rows.into_iter().map(Into::into).collect())
205    }
206
207    /// Return the number of rows.
208    #[must_use]
209    pub const fn len(&self) -> usize {
210        self.0.len()
211    }
212
213    /// Return the number of rows as a u32 API contract count.
214    #[must_use]
215    #[expect(clippy::cast_possible_truncation)]
216    pub const fn count(&self) -> u32 {
217        self.0.len() as u32
218    }
219
220    /// Return whether this response has no rows.
221    #[must_use]
222    pub const fn is_empty(&self) -> bool {
223        self.0.is_empty()
224    }
225
226    /// Consume and return all rows in response order.
227    #[must_use]
228    pub fn rows(self) -> Vec<R> {
229        self.0
230    }
231
232    /// Borrow an iterator over rows in response order.
233    pub fn iter(&self) -> std::slice::Iter<'_, R> {
234        self.0.iter()
235    }
236}
237
238impl<R: ResponseRow> AsRef<[R]> for Response<R> {
239    fn as_ref(&self) -> &[R] {
240        self.0.as_slice()
241    }
242}
243
244impl<R: ResponseRow> std::ops::Deref for Response<R> {
245    type Target = [R];
246
247    fn deref(&self) -> &Self::Target {
248        self.0.as_slice()
249    }
250}
251
252impl<E: EntityKind> Response<Row<E>> {
253    /// Return the first row identifier, if present.
254    #[must_use]
255    pub fn id(&self) -> Option<Id<E>> {
256        self.0.first().map(Row::id)
257    }
258
259    /// Consume and return all entities in response order.
260    #[must_use]
261    pub fn entities(self) -> Vec<E> {
262        self.0.into_iter().map(Row::entity).collect()
263    }
264
265    /// Borrow an iterator over row identifiers in response order.
266    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_ {
267        self.0.iter().map(Row::id)
268    }
269
270    /// Check whether the response contains the provided identifier.
271    pub fn contains_id(&self, id: &Id<E>) -> bool {
272        self.0.iter().any(|row| row.id() == *id)
273    }
274}
275
276impl<R: ResponseRow> IntoIterator for Response<R> {
277    type Item = R;
278    type IntoIter = std::vec::IntoIter<Self::Item>;
279
280    fn into_iter(self) -> Self::IntoIter {
281        self.0.into_iter()
282    }
283}
284
285impl<'a, R: ResponseRow> IntoIterator for &'a Response<R> {
286    type Item = &'a R;
287    type IntoIter = std::slice::Iter<'a, R>;
288
289    fn into_iter(self) -> Self::IntoIter {
290        self.iter()
291    }
292}
293
294///
295/// WriteBatchResponse
296///
297/// Result of a batch write operation.
298/// Provides explicit access to stored entities and their identifiers.
299///
300
301#[derive(Debug)]
302pub struct WriteBatchResponse<E> {
303    entities: Vec<E>,
304}
305
306impl<E> WriteBatchResponse<E> {
307    /// Construct a batch response from stored entities.
308    #[must_use]
309    pub const fn new(entities: Vec<E>) -> Self {
310        Self { entities }
311    }
312
313    /// Return the number of entries.
314    #[must_use]
315    pub const fn len(&self) -> usize {
316        self.entities.len()
317    }
318
319    /// Returns `true` if the batch is empty.
320    #[must_use]
321    pub const fn is_empty(&self) -> bool {
322        self.entities.is_empty()
323    }
324
325    /// Return all stored entities.
326    #[must_use]
327    pub fn entities(self) -> Vec<E> {
328        self.entities
329    }
330
331    /// Borrow an iterator over primary keys in stable batch order.
332    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_
333    where
334        E: EntityValue,
335    {
336        self.entities.iter().map(EntityValue::id)
337    }
338
339    /// Borrow an iterator over write entries in stable batch order.
340    pub fn iter(&self) -> std::slice::Iter<'_, E> {
341        self.entities.iter()
342    }
343}
344
345impl<E> IntoIterator for WriteBatchResponse<E> {
346    type Item = E;
347    type IntoIter = std::vec::IntoIter<E>;
348
349    fn into_iter(self) -> Self::IntoIter {
350        self.entities.into_iter()
351    }
352}
353
354impl<'a, E> IntoIterator for &'a WriteBatchResponse<E> {
355    type Item = &'a E;
356    type IntoIter = std::slice::Iter<'a, E>;
357
358    fn into_iter(self) -> Self::IntoIter {
359        self.iter()
360    }
361}