icydb_core/db/response/
mod.rs1mod paged;
6
7use crate::{
8 prelude::*,
9 traits::{AsView, EntityValue},
10 types::Id,
11};
12use thiserror::Error as ThisError;
13
14pub use paged::{PagedLoadExecution, PagedLoadExecutionWithTrace};
16
17pub type Row<E> = (Id<E>, E);
22
23#[derive(Debug, ThisError)]
28pub enum ResponseError {
29 #[error("expected exactly one row, found 0 (entity {entity})")]
30 NotFound { entity: &'static str },
31
32 #[error("expected exactly one row, found {count} (entity {entity})")]
33 NotUnique { entity: &'static str, count: u32 },
34}
35
36impl ResponseError {
37 const fn not_found<E: EntityKind>() -> Self {
38 Self::NotFound { entity: E::PATH }
39 }
40
41 const fn not_unique<E: EntityKind>(count: u32) -> Self {
42 Self::NotUnique {
43 entity: E::PATH,
44 count,
45 }
46 }
47}
48
49#[derive(Debug)]
58pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
59
60impl<E: EntityKind> Response<E> {
61 #[must_use]
67 #[expect(clippy::cast_possible_truncation)]
68 pub const fn count(&self) -> u32 {
69 self.0.len() as u32
70 }
71
72 #[must_use]
74 pub const fn is_empty(&self) -> bool {
75 self.0.is_empty()
76 }
77
78 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> {
93 if self.is_empty() {
94 Err(ResponseError::not_found::<E>())
95 } else {
96 Ok(())
97 }
98 }
99
100 #[expect(clippy::cast_possible_truncation)]
106 pub fn try_row(self) -> Result<Option<Row<E>>, ResponseError> {
107 match self.0.len() {
108 0 => Ok(None),
109 1 => Ok(Some(self.0.into_iter().next().unwrap())),
110 n => Err(ResponseError::not_unique::<E>(n as u32)),
111 }
112 }
113
114 pub fn row(self) -> Result<Row<E>, ResponseError> {
116 self.try_row()?.ok_or_else(ResponseError::not_found::<E>)
117 }
118
119 #[must_use]
121 pub fn rows(self) -> Vec<Row<E>> {
122 self.0
123 }
124
125 pub fn try_entity(self) -> Result<Option<E>, ResponseError> {
131 Ok(self.try_row()?.map(|(_, e)| e))
132 }
133
134 pub fn entity(self) -> Result<E, ResponseError> {
136 self.row().map(|(_, e)| e)
137 }
138
139 #[must_use]
141 pub fn entities(self) -> Vec<E> {
142 self.0.into_iter().map(|(_, e)| e).collect()
143 }
144
145 #[must_use]
153 pub fn id(&self) -> Option<Id<E>> {
154 self.0.first().map(|(id, _)| *id)
155 }
156
157 pub fn require_id(self) -> Result<Id<E>, ResponseError> {
159 self.row().map(|(id, _)| id)
160 }
161
162 #[must_use]
164 pub fn ids(&self) -> Vec<Id<E>> {
165 self.0.iter().map(|(id, _)| *id).collect()
166 }
167
168 pub fn contains_id(&self, id: &Id<E>) -> bool {
170 self.0.iter().any(|(k, _)| k == id)
171 }
172
173 pub fn view(&self) -> Result<<E as AsView>::ViewType, ResponseError> {
179 self.require_one()?;
180 Ok(self.0[0].1.as_view())
181 }
182
183 pub fn view_opt(&self) -> Result<Option<<E as AsView>::ViewType>, ResponseError> {
185 match self.count() {
186 0 => Ok(None),
187 1 => Ok(Some(self.0[0].1.as_view())),
188 n => Err(ResponseError::not_unique::<E>(n)),
189 }
190 }
191
192 #[must_use]
194 pub fn views(&self) -> Vec<<E as AsView>::ViewType> {
195 self.0.iter().map(|(_, e)| e.as_view()).collect()
196 }
197}
198
199impl<E: EntityKind> IntoIterator for Response<E> {
200 type Item = Row<E>;
201 type IntoIter = std::vec::IntoIter<Self::Item>;
202
203 fn into_iter(self) -> Self::IntoIter {
204 self.0.into_iter()
205 }
206}
207
208#[derive(Debug)]
216pub struct WriteResponse<E> {
217 entity: E,
218}
219
220impl<E> WriteResponse<E> {
221 #[must_use]
223 pub const fn new(entity: E) -> Self {
224 Self { entity }
225 }
226
227 #[must_use]
229 pub fn entity(self) -> E {
230 self.entity
231 }
232
233 #[must_use]
235 pub fn id(&self) -> Id<E>
236 where
237 E: EntityValue,
238 {
239 self.entity.id()
240 }
241
242 #[must_use]
244 pub fn view(&self) -> <E as AsView>::ViewType
245 where
246 E: AsView,
247 {
248 self.entity.as_view()
249 }
250}
251
252#[derive(Debug)]
260pub struct WriteBatchResponse<E> {
261 entries: Vec<WriteResponse<E>>,
262}
263
264impl<E> WriteBatchResponse<E> {
265 #[must_use]
267 pub fn new(entities: Vec<E>) -> Self {
268 Self {
269 entries: entities.into_iter().map(WriteResponse::new).collect(),
270 }
271 }
272
273 #[must_use]
275 pub fn entries(&self) -> &[WriteResponse<E>] {
276 &self.entries
277 }
278
279 #[must_use]
281 pub const fn len(&self) -> usize {
282 self.entries.len()
283 }
284
285 #[must_use]
287 pub const fn is_empty(&self) -> bool {
288 self.entries.is_empty()
289 }
290
291 #[must_use]
293 pub fn entities(self) -> Vec<E> {
294 self.entries
295 .into_iter()
296 .map(WriteResponse::entity)
297 .collect()
298 }
299
300 #[must_use]
302 pub fn ids(&self) -> Vec<Id<E>>
303 where
304 E: EntityValue,
305 {
306 self.entries.iter().map(WriteResponse::id).collect()
307 }
308
309 #[must_use]
311 pub fn views(&self) -> Vec<<E as AsView>::ViewType>
312 where
313 E: AsView,
314 {
315 self.entries.iter().map(WriteResponse::view).collect()
316 }
317}
318
319impl<E> IntoIterator for WriteBatchResponse<E> {
320 type Item = WriteResponse<E>;
321 type IntoIter = std::vec::IntoIter<WriteResponse<E>>;
322
323 fn into_iter(self) -> Self::IntoIter {
324 self.entries.into_iter()
325 }
326}
327
328impl<E> WriteBatchResponse<E> {
329 pub fn iter(&self) -> std::slice::Iter<'_, WriteResponse<E>> {
331 self.entries.iter()
332 }
333}
334
335impl<'a, E> IntoIterator for &'a WriteBatchResponse<E> {
336 type Item = &'a WriteResponse<E>;
337 type IntoIter = std::slice::Iter<'a, WriteResponse<E>>;
338
339 fn into_iter(self) -> Self::IntoIter {
340 self.iter()
341 }
342}