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: u64 },
22}
23
24///
25/// Response
26/// Materialized query result: ordered `(Key, Entity)` pairs.
27///
28
29#[derive(Debug)]
30pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
31
32impl<E: EntityKind> Response<E> {
33    // ------------------------------------------------------------------
34    // Introspection
35    // ------------------------------------------------------------------
36
37    #[must_use]
38    pub const fn count(&self) -> u64 {
39        self.0.len() as u64
40    }
41
42    #[must_use]
43    pub const fn is_empty(&self) -> bool {
44        self.0.is_empty()
45    }
46
47    // ------------------------------------------------------------------
48    // Cardinality enforcement (domain-level)
49    // ------------------------------------------------------------------
50
51    pub const fn require_one(&self) -> Result<(), ResponseError> {
52        match self.count() {
53            1 => Ok(()),
54            0 => Err(ResponseError::NotFound { entity: E::PATH }),
55            n => Err(ResponseError::NotUnique {
56                entity: E::PATH,
57                count: n,
58            }),
59        }
60    }
61
62    pub const fn require_some(&self) -> Result<(), ResponseError> {
63        if self.is_empty() {
64            Err(ResponseError::NotFound { entity: E::PATH })
65        } else {
66            Ok(())
67        }
68    }
69
70    // ------------------------------------------------------------------
71    // Rows
72    // ------------------------------------------------------------------
73
74    pub fn row(self) -> Result<Row<E>, ResponseError> {
75        self.require_one()?;
76        Ok(self.0.into_iter().next().unwrap())
77    }
78
79    pub fn try_row(self) -> Result<Option<Row<E>>, ResponseError> {
80        match self.count() {
81            0 => Ok(None),
82            1 => Ok(Some(self.0.into_iter().next().unwrap())),
83            n => Err(ResponseError::NotUnique {
84                entity: E::PATH,
85                count: n,
86            }),
87        }
88    }
89
90    #[must_use]
91    pub fn rows(self) -> Vec<Row<E>> {
92        self.0
93    }
94
95    // ------------------------------------------------------------------
96    // Entities
97    // ------------------------------------------------------------------
98
99    pub fn entity(self) -> Result<E, ResponseError> {
100        self.row().map(|(_, e)| e)
101    }
102
103    pub fn try_entity(self) -> Result<Option<E>, ResponseError> {
104        Ok(self.try_row()?.map(|(_, e)| e))
105    }
106
107    #[must_use]
108    pub fn entities(self) -> Vec<E> {
109        self.0.into_iter().map(|(_, e)| e).collect()
110    }
111
112    // ------------------------------------------------------------------
113    // Store keys (delete ergonomics)
114    // ------------------------------------------------------------------
115
116    #[must_use]
117    pub fn key(&self) -> Option<Key> {
118        self.0.first().map(|(k, _)| *k)
119    }
120
121    pub fn key_strict(self) -> Result<Key, ResponseError> {
122        self.row().map(|(k, _)| k)
123    }
124
125    pub fn try_key(self) -> Result<Option<Key>, ResponseError> {
126        Ok(self.try_row()?.map(|(k, _)| k))
127    }
128
129    #[must_use]
130    pub fn keys(&self) -> Vec<Key> {
131        self.0.iter().map(|(k, _)| *k).collect()
132    }
133
134    #[must_use]
135    pub fn contains_key(&self, key: &Key) -> bool {
136        self.0.iter().any(|(k, _)| k == key)
137    }
138
139    // ------------------------------------------------------------------
140    // Primary keys (domain-level, strict)
141    // ------------------------------------------------------------------
142
143    pub fn primary_key(self) -> Result<E::PrimaryKey, ResponseError> {
144        Ok(self.entity()?.primary_key())
145    }
146
147    pub fn try_primary_key(self) -> Result<Option<E::PrimaryKey>, ResponseError> {
148        Ok(self.try_entity()?.map(|e| e.primary_key()))
149    }
150
151    #[must_use]
152    pub fn primary_keys(self) -> Vec<E::PrimaryKey> {
153        self.entities()
154            .into_iter()
155            .map(|e| e.primary_key())
156            .collect()
157    }
158
159    // ------------------------------------------------------------------
160    // Views (first-class, canonical)
161    // ------------------------------------------------------------------
162
163    pub fn view(&self) -> Result<View<E>, ResponseError> {
164        self.require_one()?;
165        Ok(self
166            .0
167            .first()
168            .expect("require_one guarantees a row")
169            .1
170            .to_view())
171    }
172
173    pub fn view_opt(&self) -> Result<Option<View<E>>, ResponseError> {
174        match self.count() {
175            0 => Ok(None),
176            1 => Ok(Some(self.0[0].1.to_view())),
177            n => Err(ResponseError::NotUnique {
178                entity: E::PATH,
179                count: n,
180            }),
181        }
182    }
183
184    #[must_use]
185    pub fn views(&self) -> Vec<View<E>> {
186        self.0.iter().map(|(_, e)| e.to_view()).collect()
187    }
188}
189
190impl<E: EntityKind> IntoIterator for Response<E> {
191    type Item = Row<E>;
192    type IntoIter = std::vec::IntoIter<Self::Item>;
193
194    fn into_iter(self) -> Self::IntoIter {
195        self.0.into_iter()
196    }
197}