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