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
12#[cfg(test)]
13use crate::value::Value;
14use crate::{prelude::*, traits::EntityValue, types::Id, value::OutputValue};
15
16mod private {
17    ///
18    /// Sealed
19    ///
20    /// Internal marker used to seal response row-shape marker implementations.
21    ///
22
23    pub trait Sealed {}
24}
25
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 row identity plus entity payload.
80    #[must_use]
81    pub fn into_id_and_entity(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<OutputValue>,
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<OutputValue>) -> Self {
113        Self { id, values }
114    }
115
116    /// Build one projected row from runtime values at the public output boundary.
117    #[cfg(test)]
118    #[must_use]
119    pub(in crate::db) fn from_runtime_values(id: Id<E>, values: Vec<Value>) -> Self {
120        Self::new(id, values.into_iter().map(OutputValue::from).collect())
121    }
122
123    /// Borrow the source row identifier.
124    #[must_use]
125    pub const fn id(&self) -> Id<E> {
126        self.id
127    }
128
129    /// Borrow projected scalar values in declaration order.
130    #[must_use]
131    pub const fn values(&self) -> &[OutputValue] {
132        self.values.as_slice()
133    }
134
135    /// Consume and return source row identity plus projected values.
136    #[must_use]
137    pub fn into_id_and_values(self) -> (Id<E>, Vec<OutputValue>) {
138        (self.id, self.values)
139    }
140}
141
142impl<E: EntityKind> private::Sealed for ProjectedRow<E> {}
143
144impl<E: EntityKind> ResponseRow for ProjectedRow<E> {}
145
146///
147/// ResponseError
148///
149
150#[derive(Debug)]
151pub enum ResponseError {
152    NotFound { entity: &'static str },
153
154    NotUnique { entity: &'static str, count: u32 },
155}
156
157impl ResponseError {
158    /// Construct one response not-found cardinality error.
159    #[must_use]
160    pub const fn not_found(entity: &'static str) -> Self {
161        Self::NotFound { entity }
162    }
163
164    /// Construct one response not-unique cardinality error.
165    #[must_use]
166    pub const fn not_unique(entity: &'static str, count: u32) -> Self {
167        Self::NotUnique { entity, count }
168    }
169}
170
171///
172/// Response
173///
174/// Generic response transport container for one row shape `R`.
175///
176
177#[derive(Debug)]
178pub struct Response<R: ResponseRow>(Vec<R>);
179
180///
181/// EntityResponse
182///
183/// Entity-row response transport alias.
184///
185
186pub type EntityResponse<E> = Response<Row<E>>;
187
188///
189/// ProjectionResponse
190///
191/// Scalar projection response transport alias.
192///
193
194pub type ProjectionResponse<E> = Response<ProjectedRow<E>>;
195
196impl<R: ResponseRow> Response<R> {
197    /// Construct one response from ordered rows.
198    #[must_use]
199    pub const fn new(rows: Vec<R>) -> Self {
200        Self(rows)
201    }
202
203    /// Return the number of rows.
204    #[must_use]
205    pub const fn len(&self) -> usize {
206        self.0.len()
207    }
208
209    /// Return the number of rows as a u32 API contract count.
210    #[must_use]
211    #[expect(clippy::cast_possible_truncation)]
212    pub const fn count(&self) -> u32 {
213        self.0.len() as u32
214    }
215
216    /// Return whether this response has no rows.
217    #[must_use]
218    pub const fn is_empty(&self) -> bool {
219        self.0.is_empty()
220    }
221
222    /// Consume and return all rows in response order.
223    #[must_use]
224    pub fn rows(self) -> Vec<R> {
225        self.0
226    }
227
228    /// Borrow rows as a slice in response order.
229    #[must_use]
230    pub const fn as_slice(&self) -> &[R] {
231        self.0.as_slice()
232    }
233
234    /// Borrow an iterator over rows in response order.
235    pub fn iter(&self) -> std::slice::Iter<'_, R> {
236        self.0.iter()
237    }
238}
239
240impl<E: EntityKind> Response<Row<E>> {
241    /// Consume and return all entities in response order.
242    #[must_use]
243    pub fn entities(self) -> Vec<E> {
244        self.0.into_iter().map(Row::entity).collect()
245    }
246
247    /// Borrow an iterator over row identifiers in response order.
248    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_ {
249        self.0.iter().map(Row::id)
250    }
251
252    /// Check whether the response contains the provided identifier.
253    pub fn contains_id(&self, id: &Id<E>) -> bool {
254        self.0.iter().any(|row| row.id() == *id)
255    }
256}
257
258impl<R: ResponseRow> IntoIterator for Response<R> {
259    type Item = R;
260    type IntoIter = std::vec::IntoIter<Self::Item>;
261
262    fn into_iter(self) -> Self::IntoIter {
263        self.0.into_iter()
264    }
265}
266
267impl<'a, R: ResponseRow> IntoIterator for &'a Response<R> {
268    type Item = &'a R;
269    type IntoIter = std::slice::Iter<'a, R>;
270
271    fn into_iter(self) -> Self::IntoIter {
272        self.iter()
273    }
274}
275
276///
277/// WriteBatchResponse
278///
279/// Result of a batch write operation.
280/// Provides explicit access to stored entities and their identifiers.
281///
282
283#[derive(Debug)]
284pub struct WriteBatchResponse<E> {
285    entities: Vec<E>,
286}
287
288impl<E> WriteBatchResponse<E> {
289    /// Construct a batch response from stored entities.
290    #[must_use]
291    pub const fn new(entities: Vec<E>) -> Self {
292        Self { entities }
293    }
294
295    /// Return the number of entries.
296    #[must_use]
297    pub const fn len(&self) -> usize {
298        self.entities.len()
299    }
300
301    /// Returns `true` if the batch is empty.
302    #[must_use]
303    pub const fn is_empty(&self) -> bool {
304        self.entities.is_empty()
305    }
306
307    /// Return all stored entities.
308    #[must_use]
309    pub fn entities(self) -> Vec<E> {
310        self.entities
311    }
312
313    /// Borrow an iterator over primary keys in stable batch order.
314    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_
315    where
316        E: EntityValue,
317    {
318        self.entities.iter().map(EntityValue::id)
319    }
320
321    /// Borrow an iterator over write entries in stable batch order.
322    pub fn iter(&self) -> std::slice::Iter<'_, E> {
323        self.entities.iter()
324    }
325}
326
327impl<E> IntoIterator for WriteBatchResponse<E> {
328    type Item = E;
329    type IntoIter = std::vec::IntoIter<E>;
330
331    fn into_iter(self) -> Self::IntoIter {
332        self.entities.into_iter()
333    }
334}
335
336impl<'a, E> IntoIterator for &'a WriteBatchResponse<E> {
337    type Item = &'a E;
338    type IntoIter = std::slice::Iter<'a, E>;
339
340    fn into_iter(self) -> Self::IntoIter {
341        self.iter()
342    }
343}