Skip to main content

icydb_core/db/
response.rs

1use crate::{prelude::*, view::View};
2use thiserror::Error as ThisError;
3
4///
5/// Row
6///
7
8pub type Row<E> = (Key, E);
9
10///
11/// ResponseError
12/// Errors related to interpreting a materialized response.
13///
14
15#[derive(Debug, ThisError)]
16pub enum ResponseError {
17    #[error("expected exactly one row, found 0 (entity {entity})")]
18    NotFound { entity: &'static str },
19
20    #[error("expected exactly one row, found {count} (entity {entity})")]
21    NotUnique { entity: &'static str, count: u32 },
22}
23
24impl ResponseError {
25    const fn not_found<E: EntityKind>() -> Self {
26        Self::NotFound { entity: E::PATH }
27    }
28
29    const fn not_unique<E: EntityKind>(count: u32) -> Self {
30        Self::NotUnique {
31            entity: E::PATH,
32            count,
33        }
34    }
35}
36
37///
38/// Response
39///
40/// Materialized query result: ordered `(Key, Entity)` pairs.
41///
42/// Invariants:
43/// - Rows are already ordered according to the query plan.
44/// - Cardinality is not enforced unless explicitly requested.
45/// - This type performs no lazy evaluation.
46///
47
48#[derive(Debug)]
49pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
50
51impl<E: EntityKind> Response<E> {
52    // ------------------------------------------------------------------
53    // Introspection
54    // ------------------------------------------------------------------
55
56    #[must_use]
57    #[allow(clippy::cast_possible_truncation)]
58    pub const fn count(&self) -> u32 {
59        self.0.len() as u32
60    }
61
62    #[must_use]
63    pub const fn is_empty(&self) -> bool {
64        self.0.is_empty()
65    }
66
67    // ------------------------------------------------------------------
68    // Cardinality enforcement (domain-level)
69    // ------------------------------------------------------------------
70
71    pub const fn require_one(&self) -> Result<(), ResponseError> {
72        match self.count() {
73            1 => Ok(()),
74            0 => Err(ResponseError::NotFound { entity: E::PATH }),
75            n => Err(ResponseError::NotUnique {
76                entity: E::PATH,
77                count: n,
78            }),
79        }
80    }
81
82    pub const fn require_some(&self) -> Result<(), ResponseError> {
83        if self.is_empty() {
84            Err(ResponseError::NotFound { entity: E::PATH })
85        } else {
86            Ok(())
87        }
88    }
89
90    // ------------------------------------------------------------------
91    // Rows (primitive: try_row)
92    // ------------------------------------------------------------------
93
94    #[allow(clippy::cast_possible_truncation)]
95    pub fn try_row(self) -> Result<Option<Row<E>>, ResponseError> {
96        match self.0.len() {
97            0 => Ok(None),
98            1 => Ok(Some(self.0.into_iter().next().unwrap())),
99            n => Err(ResponseError::not_unique::<E>(n as u32)),
100        }
101    }
102
103    pub fn row(self) -> Result<Row<E>, ResponseError> {
104        self.try_row()?.ok_or_else(ResponseError::not_found::<E>)
105    }
106
107    #[must_use]
108    pub fn rows(self) -> Vec<Row<E>> {
109        self.0
110    }
111
112    // ------------------------------------------------------------------
113    // Entities
114    // ------------------------------------------------------------------
115
116    pub fn try_entity(self) -> Result<Option<E>, ResponseError> {
117        Ok(self.try_row()?.map(|(_, e)| e))
118    }
119
120    pub fn entity(self) -> Result<E, ResponseError> {
121        self.row().map(|(_, e)| e)
122    }
123
124    #[must_use]
125    pub fn entities(self) -> Vec<E> {
126        self.0.into_iter().map(|(_, e)| e).collect()
127    }
128
129    // ------------------------------------------------------------------
130    // Store keys (delete ergonomics)
131    // ------------------------------------------------------------------
132
133    #[must_use]
134    pub fn key(&self) -> Option<Key> {
135        self.0.first().map(|(k, _)| *k)
136    }
137
138    pub fn key_strict(self) -> Result<Key, ResponseError> {
139        self.row().map(|(k, _)| k)
140    }
141
142    pub fn try_key(self) -> Result<Option<Key>, ResponseError> {
143        Ok(self.try_row()?.map(|(k, _)| k))
144    }
145
146    #[must_use]
147    pub fn keys(&self) -> Vec<Key> {
148        self.0.iter().map(|(k, _)| *k).collect()
149    }
150
151    #[must_use]
152    pub fn contains_key(&self, key: &Key) -> bool {
153        self.0.iter().any(|(k, _)| k == key)
154    }
155
156    // ------------------------------------------------------------------
157    // Primary keys (domain-level, strict)
158    // ------------------------------------------------------------------
159
160    pub fn primary_key(self) -> Result<E::PrimaryKey, ResponseError> {
161        Ok(self.entity()?.primary_key())
162    }
163
164    pub fn try_primary_key(self) -> Result<Option<E::PrimaryKey>, ResponseError> {
165        Ok(self.try_entity()?.map(|e| e.primary_key()))
166    }
167
168    #[must_use]
169    pub fn primary_keys(self) -> Vec<E::PrimaryKey> {
170        self.0.into_iter().map(|(_, e)| e.primary_key()).collect()
171    }
172
173    // ------------------------------------------------------------------
174    // Views (first-class, canonical)
175    // ------------------------------------------------------------------
176
177    pub fn view(&self) -> Result<View<E>, ResponseError> {
178        self.require_one()?;
179        Ok(self.0[0].1.to_view())
180    }
181
182    pub fn view_opt(&self) -> Result<Option<View<E>>, ResponseError> {
183        match self.count() {
184            0 => Ok(None),
185            1 => Ok(Some(self.0[0].1.to_view())),
186            n => Err(ResponseError::not_unique::<E>(n)),
187        }
188    }
189
190    #[must_use]
191    pub fn views(&self) -> Vec<View<E>> {
192        self.0.iter().map(|(_, e)| e.to_view()).collect()
193    }
194}
195
196impl<E: EntityKind> IntoIterator for Response<E> {
197    type Item = Row<E>;
198    type IntoIter = std::vec::IntoIter<Self::Item>;
199
200    fn into_iter(self) -> Self::IntoIter {
201        self.0.into_iter()
202    }
203}