icydb_core/db/response/
mod.rs1mod paged;
9
10use crate::{
11 prelude::*,
12 traits::{AsView, EntityValue},
13 types::Id,
14};
15use thiserror::Error as ThisError;
16
17pub use paged::{PagedLoadExecution, PagedLoadExecutionWithTrace};
19
20pub type Row<E> = (Id<E>, E);
25
26#[derive(Debug, ThisError)]
31pub enum ResponseError {
32 #[error("expected exactly one row, found 0 (entity {entity})")]
33 NotFound { entity: &'static str },
34
35 #[error("expected exactly one row, found {count} (entity {entity})")]
36 NotUnique { entity: &'static str, count: u32 },
37}
38
39impl ResponseError {
40 const fn not_found<E: EntityKind>() -> Self {
41 Self::NotFound { entity: E::PATH }
42 }
43
44 const fn not_unique<E: EntityKind>(count: u32) -> Self {
45 Self::NotUnique {
46 entity: E::PATH,
47 count,
48 }
49 }
50}
51
52#[derive(Debug)]
61pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
62
63impl<E: EntityKind> Response<E> {
64 #[must_use]
69 #[expect(clippy::cast_possible_truncation)]
70 pub const fn count(&self) -> u32 {
71 self.0.len() as u32
72 }
73
74 #[must_use]
75 pub const fn is_empty(&self) -> bool {
76 self.0.is_empty()
77 }
78
79 pub const fn require_one(&self) -> Result<(), ResponseError> {
84 match self.count() {
85 1 => Ok(()),
86 0 => Err(ResponseError::not_found::<E>()),
87 n => Err(ResponseError::not_unique::<E>(n)),
88 }
89 }
90
91 pub const fn require_some(&self) -> Result<(), ResponseError> {
92 if self.is_empty() {
93 Err(ResponseError::not_found::<E>())
94 } else {
95 Ok(())
96 }
97 }
98
99 #[expect(clippy::cast_possible_truncation)]
104 pub fn try_row(self) -> Result<Option<Row<E>>, ResponseError> {
105 match self.0.len() {
106 0 => Ok(None),
107 1 => Ok(Some(self.0.into_iter().next().unwrap())),
108 n => Err(ResponseError::not_unique::<E>(n as u32)),
109 }
110 }
111
112 pub fn row(self) -> Result<Row<E>, ResponseError> {
113 self.try_row()?.ok_or_else(ResponseError::not_found::<E>)
114 }
115
116 #[must_use]
117 pub fn rows(self) -> Vec<Row<E>> {
118 self.0
119 }
120
121 pub fn try_entity(self) -> Result<Option<E>, ResponseError> {
126 Ok(self.try_row()?.map(|(_, e)| e))
127 }
128
129 pub fn entity(self) -> Result<E, ResponseError> {
130 self.row().map(|(_, e)| e)
131 }
132
133 #[must_use]
134 pub fn entities(self) -> Vec<E> {
135 self.0.into_iter().map(|(_, e)| e).collect()
136 }
137
138 #[must_use]
146 pub fn id(&self) -> Option<Id<E>> {
147 self.0.first().map(|(id, _)| *id)
148 }
149
150 pub fn require_id(self) -> Result<Id<E>, ResponseError> {
152 self.row().map(|(id, _)| id)
153 }
154
155 #[must_use]
157 pub fn ids(&self) -> Vec<Id<E>> {
158 self.0.iter().map(|(id, _)| *id).collect()
159 }
160
161 pub fn contains_id(&self, id: &Id<E>) -> bool {
163 self.0.iter().any(|(k, _)| k == id)
164 }
165
166 pub fn view(&self) -> Result<<E as AsView>::ViewType, ResponseError> {
171 self.require_one()?;
172 Ok(self.0[0].1.as_view())
173 }
174
175 pub fn view_opt(&self) -> Result<Option<<E as AsView>::ViewType>, ResponseError> {
176 match self.count() {
177 0 => Ok(None),
178 1 => Ok(Some(self.0[0].1.as_view())),
179 n => Err(ResponseError::not_unique::<E>(n)),
180 }
181 }
182
183 #[must_use]
184 pub fn views(&self) -> Vec<<E as AsView>::ViewType> {
185 self.0.iter().map(|(_, e)| e.as_view()).collect()
186 }
187}
188
189impl<E: EntityKind> IntoIterator for Response<E> {
190 type Item = Row<E>;
191 type IntoIter = std::vec::IntoIter<Self::Item>;
192
193 fn into_iter(self) -> Self::IntoIter {
194 self.0.into_iter()
195 }
196}
197
198#[derive(Debug)]
206pub struct WriteResponse<E> {
207 entity: E,
208}
209
210impl<E> WriteResponse<E> {
211 #[must_use]
213 pub const fn new(entity: E) -> Self {
214 Self { entity }
215 }
216
217 #[must_use]
219 pub fn entity(self) -> E {
220 self.entity
221 }
222
223 #[must_use]
225 pub fn id(&self) -> Id<E>
226 where
227 E: EntityValue,
228 {
229 self.entity.id()
230 }
231
232 #[must_use]
234 pub fn view(&self) -> <E as AsView>::ViewType
235 where
236 E: AsView,
237 {
238 self.entity.as_view()
239 }
240}
241
242#[derive(Debug)]
250pub struct WriteBatchResponse<E> {
251 entries: Vec<WriteResponse<E>>,
252}
253
254impl<E> WriteBatchResponse<E> {
255 #[must_use]
257 pub fn new(entities: Vec<E>) -> Self {
258 Self {
259 entries: entities.into_iter().map(WriteResponse::new).collect(),
260 }
261 }
262
263 #[must_use]
265 pub fn entries(&self) -> &[WriteResponse<E>] {
266 &self.entries
267 }
268
269 #[must_use]
271 pub const fn len(&self) -> usize {
272 self.entries.len()
273 }
274
275 #[must_use]
277 pub const fn is_empty(&self) -> bool {
278 self.entries.is_empty()
279 }
280
281 #[must_use]
283 pub fn entities(self) -> Vec<E> {
284 self.entries
285 .into_iter()
286 .map(WriteResponse::entity)
287 .collect()
288 }
289
290 #[must_use]
292 pub fn ids(&self) -> Vec<Id<E>>
293 where
294 E: EntityValue,
295 {
296 self.entries.iter().map(WriteResponse::id).collect()
297 }
298
299 #[must_use]
301 pub fn views(&self) -> Vec<<E as AsView>::ViewType>
302 where
303 E: AsView,
304 {
305 self.entries.iter().map(WriteResponse::view).collect()
306 }
307}
308
309impl<E> IntoIterator for WriteBatchResponse<E> {
310 type Item = WriteResponse<E>;
311 type IntoIter = std::vec::IntoIter<WriteResponse<E>>;
312
313 fn into_iter(self) -> Self::IntoIter {
314 self.entries.into_iter()
315 }
316}
317
318impl<E> WriteBatchResponse<E> {
319 pub fn iter(&self) -> std::slice::Iter<'_, WriteResponse<E>> {
320 self.entries.iter()
321 }
322}
323
324impl<'a, E> IntoIterator for &'a WriteBatchResponse<E> {
325 type Item = &'a WriteResponse<E>;
326 type IntoIter = std::slice::Iter<'a, WriteResponse<E>>;
327
328 fn into_iter(self) -> Self::IntoIter {
329 self.iter()
330 }
331}