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