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