icydb_core/db/
response.rs1use crate::{
2 error::{ErrorClass, ErrorOrigin, InternalError},
3 prelude::*,
4 view::View,
5};
6use thiserror::Error as ThisError;
7
8pub type Row<E> = (Key, E);
13
14#[derive(Debug, ThisError)]
20pub enum ResponseError {
21 #[error("expected exactly one row, found 0 (entity {entity})")]
22 NotFound { entity: &'static str },
23
24 #[error("expected exactly one row, found {count} (entity {entity})")]
25 NotUnique { entity: &'static str, count: u32 },
26}
27
28impl ResponseError {
29 pub(crate) const fn class(&self) -> ErrorClass {
30 match self {
31 Self::NotFound { .. } => ErrorClass::NotFound,
32 Self::NotUnique { .. } => ErrorClass::Conflict,
33 }
34 }
35}
36
37impl From<ResponseError> for InternalError {
38 fn from(err: ResponseError) -> Self {
39 Self::new(err.class(), ErrorOrigin::Response, err.to_string())
40 }
41}
42
43#[derive(Debug)]
51pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
52
53impl<E: EntityKind> Response<E> {
54 #[must_use]
59 #[allow(clippy::cast_possible_truncation)]
60 pub const fn count(&self) -> u32 {
61 self.0.len() as u32
62 }
63
64 #[must_use]
65 pub const fn is_empty(&self) -> bool {
66 self.0.is_empty()
67 }
68
69 pub fn require_one(&self) -> Result<(), InternalError> {
74 match self.count() {
75 1 => Ok(()),
76 0 => Err(ResponseError::NotFound { entity: E::PATH }.into()),
77 n => Err(ResponseError::NotUnique {
78 entity: E::PATH,
79 count: n,
80 }
81 .into()),
82 }
83 }
84
85 pub fn require_some(&self) -> Result<(), InternalError> {
86 if self.is_empty() {
87 Err(ResponseError::NotFound { entity: E::PATH }.into())
88 } else {
89 Ok(())
90 }
91 }
92
93 pub fn row(self) -> Result<Row<E>, InternalError> {
98 self.require_one()?;
99 Ok(self.0.into_iter().next().unwrap())
100 }
101
102 #[allow(clippy::cast_possible_truncation)]
103 pub fn try_row(self) -> Result<Option<Row<E>>, InternalError> {
104 match self.0.len() {
105 0 => Ok(None),
106 1 => Ok(Some(self.0.into_iter().next().unwrap())),
107 n => Err(ResponseError::NotUnique {
108 entity: E::PATH,
109 count: n as u32,
110 }
111 .into()),
112 }
113 }
114
115 #[must_use]
116 pub fn rows(self) -> Vec<Row<E>> {
117 self.0
118 }
119
120 pub fn entity(self) -> Result<E, InternalError> {
125 self.row().map(|(_, e)| e)
126 }
127
128 pub fn try_entity(self) -> Result<Option<E>, InternalError> {
129 Ok(self.try_row()?.map(|(_, e)| e))
130 }
131
132 #[must_use]
133 pub fn entities(self) -> Vec<E> {
134 self.0.into_iter().map(|(_, e)| e).collect()
135 }
136
137 #[must_use]
142 pub fn key(&self) -> Option<Key> {
143 self.0.first().map(|(k, _)| *k)
144 }
145
146 pub fn key_strict(self) -> Result<Key, InternalError> {
147 self.row().map(|(k, _)| k)
148 }
149
150 pub fn try_key(self) -> Result<Option<Key>, InternalError> {
151 Ok(self.try_row()?.map(|(k, _)| k))
152 }
153
154 #[must_use]
155 pub fn keys(&self) -> Vec<Key> {
156 self.0.iter().map(|(k, _)| *k).collect()
157 }
158
159 #[must_use]
160 pub fn contains_key(&self, key: &Key) -> bool {
161 self.0.iter().any(|(k, _)| k == key)
162 }
163
164 pub fn view(&self) -> Result<View<E>, InternalError> {
170 self.require_one()?;
171 let view = self
172 .0
173 .first()
174 .map(|(_, entity)| entity.to_view())
175 .expect("require_one ensures one row");
176
177 Ok(view)
178 }
179
180 #[allow(clippy::cast_possible_truncation)]
182 pub fn view_opt(&self) -> Result<Option<View<E>>, InternalError> {
183 match self.0.len() {
184 0 => Ok(None),
185 1 => Ok(self.0.first().map(|(_, entity)| entity.to_view())),
186 n => Err(ResponseError::NotUnique {
187 entity: E::PATH,
188 count: n as u32,
189 }
190 .into()),
191 }
192 }
193
194 pub fn try_view(&self) -> Result<Option<View<E>>, InternalError> {
196 self.view_opt()
197 }
198
199 #[must_use]
201 pub fn views(&self) -> Vec<View<E>> {
202 self.0.iter().map(|(_, entity)| entity.to_view()).collect()
203 }
204
205 #[must_use]
210 pub fn first(self) -> Option<Row<E>> {
211 self.0.into_iter().next()
212 }
213
214 #[must_use]
215 pub fn first_entity(self) -> Option<E> {
216 self.first().map(|(_, e)| e)
217 }
218
219 #[must_use]
220 pub fn first_pk(self) -> Option<E::PrimaryKey> {
221 self.first_entity().map(|e| e.primary_key())
222 }
223}
224
225impl<E: EntityKind> IntoIterator for Response<E> {
226 type Item = Row<E>;
227 type IntoIter = std::vec::IntoIter<Self::Item>;
228
229 fn into_iter(self) -> Self::IntoIter {
230 self.0.into_iter()
231 }
232}
233
234pub trait ResponseExt<E: EntityKind> {
243 fn entities(self) -> Result<Vec<E>, InternalError>;
246 fn entity(self) -> Result<E, InternalError>;
247 fn try_entity(self) -> Result<Option<E>, InternalError>;
248
249 fn count(self) -> Result<u32, InternalError>;
252}
253
254impl<E: EntityKind> ResponseExt<E> for Result<Response<E>, InternalError> {
255 fn entities(self) -> Result<Vec<E>, InternalError> {
256 Ok(self?.entities())
257 }
258
259 fn entity(self) -> Result<E, InternalError> {
260 self?.entity()
261 }
262
263 fn try_entity(self) -> Result<Option<E>, InternalError> {
264 self?.try_entity()
265 }
266
267 fn count(self) -> Result<u32, InternalError> {
268 Ok(self?.count())
269 }
270}