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::{entity::EntityValue, prelude::*, 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
188impl<R: ResponseRow> Response<R> {
189    /// Construct one response from ordered rows.
190    #[must_use]
191    pub const fn new(rows: Vec<R>) -> Self {
192        Self(rows)
193    }
194
195    /// Return the number of rows.
196    #[must_use]
197    pub const fn len(&self) -> usize {
198        self.0.len()
199    }
200
201    /// Return the number of rows as a u32 API contract count.
202    #[must_use]
203    #[expect(clippy::cast_possible_truncation)]
204    pub const fn count(&self) -> u32 {
205        self.0.len() as u32
206    }
207
208    /// Return whether this response has no rows.
209    #[must_use]
210    pub const fn is_empty(&self) -> bool {
211        self.0.is_empty()
212    }
213
214    /// Consume and return all rows in response order.
215    #[must_use]
216    pub fn rows(self) -> Vec<R> {
217        self.0
218    }
219
220    /// Borrow rows as a slice in response order.
221    #[must_use]
222    pub const fn as_slice(&self) -> &[R] {
223        self.0.as_slice()
224    }
225
226    /// Borrow an iterator over rows in response order.
227    pub fn iter(&self) -> std::slice::Iter<'_, R> {
228        self.0.iter()
229    }
230}
231
232impl<E: EntityKind> Response<Row<E>> {
233    /// Consume and return all entities in response order.
234    #[must_use]
235    pub fn entities(self) -> Vec<E> {
236        self.0.into_iter().map(Row::entity).collect()
237    }
238
239    /// Borrow an iterator over row identifiers in response order.
240    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_ {
241        self.0.iter().map(Row::id)
242    }
243
244    /// Check whether the response contains the provided identifier.
245    pub fn contains_id(&self, id: &Id<E>) -> bool {
246        self.0.iter().any(|row| row.id() == *id)
247    }
248}
249
250impl<R: ResponseRow> IntoIterator for Response<R> {
251    type Item = R;
252    type IntoIter = std::vec::IntoIter<Self::Item>;
253
254    fn into_iter(self) -> Self::IntoIter {
255        self.0.into_iter()
256    }
257}
258
259impl<'a, R: ResponseRow> IntoIterator for &'a Response<R> {
260    type Item = &'a R;
261    type IntoIter = std::slice::Iter<'a, R>;
262
263    fn into_iter(self) -> Self::IntoIter {
264        self.iter()
265    }
266}
267
268///
269/// WriteBatchResponse
270///
271/// Result of a batch write operation.
272/// Provides explicit access to stored entities and their identifiers.
273///
274
275#[derive(Debug)]
276pub struct WriteBatchResponse<E> {
277    entities: Vec<E>,
278}
279
280impl<E> WriteBatchResponse<E> {
281    /// Construct a batch response from stored entities.
282    #[must_use]
283    pub(in crate::db) const fn new(entities: Vec<E>) -> Self {
284        Self { entities }
285    }
286
287    /// Return the number of entries.
288    #[must_use]
289    pub const fn len(&self) -> usize {
290        self.entities.len()
291    }
292
293    /// Returns `true` if the batch is empty.
294    #[must_use]
295    pub const fn is_empty(&self) -> bool {
296        self.entities.is_empty()
297    }
298
299    /// Return all stored entities.
300    #[must_use]
301    pub fn entities(self) -> Vec<E> {
302        self.entities
303    }
304
305    /// Borrow an iterator over primary keys in stable batch order.
306    pub fn ids(&self) -> impl Iterator<Item = Id<E>> + '_
307    where
308        E: EntityValue,
309    {
310        self.entities.iter().map(EntityValue::id)
311    }
312
313    /// Borrow an iterator over write entries in stable batch order.
314    pub fn iter(&self) -> std::slice::Iter<'_, E> {
315        self.entities.iter()
316    }
317}
318
319impl<E> IntoIterator for WriteBatchResponse<E> {
320    type Item = E;
321    type IntoIter = std::vec::IntoIter<E>;
322
323    fn into_iter(self) -> Self::IntoIter {
324        self.entities.into_iter()
325    }
326}
327
328impl<'a, E> IntoIterator for &'a WriteBatchResponse<E> {
329    type Item = &'a E;
330    type IntoIter = std::slice::Iter<'a, E>;
331
332    fn into_iter(self) -> Self::IntoIter {
333        self.iter()
334    }
335}