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