icydb_core/db/
response.rs1use crate::{prelude::*, view::View};
2use thiserror::Error as ThisError;
3
4pub type Row<E> = (<E as EntityIdentity>::Id, E);
9
10#[derive(Debug, ThisError)]
15pub enum ResponseError {
16 #[error("expected exactly one row, found 0 (entity {entity})")]
17 NotFound { entity: &'static str },
18
19 #[error("expected exactly one row, found {count} (entity {entity})")]
20 NotUnique { entity: &'static str, count: u32 },
21}
22
23impl ResponseError {
24 const fn not_found<E: EntityKind>() -> Self {
25 Self::NotFound { entity: E::PATH }
26 }
27
28 const fn not_unique<E: EntityKind>(count: u32) -> Self {
29 Self::NotUnique {
30 entity: E::PATH,
31 count,
32 }
33 }
34}
35
36#[derive(Debug)]
43pub struct Response<E: EntityKind>(pub Vec<Row<E>>);
44
45impl<E: EntityKind> Response<E> {
46 #[must_use]
51 #[expect(clippy::cast_possible_truncation)]
52 pub const fn count(&self) -> u32 {
53 self.0.len() as u32
54 }
55
56 #[must_use]
57 pub const fn is_empty(&self) -> bool {
58 self.0.is_empty()
59 }
60
61 pub const fn require_one(&self) -> Result<(), ResponseError> {
66 match self.count() {
67 1 => Ok(()),
68 0 => Err(ResponseError::not_found::<E>()),
69 n => Err(ResponseError::not_unique::<E>(n)),
70 }
71 }
72
73 pub const fn require_some(&self) -> Result<(), ResponseError> {
74 if self.is_empty() {
75 Err(ResponseError::not_found::<E>())
76 } else {
77 Ok(())
78 }
79 }
80
81 #[expect(clippy::cast_possible_truncation)]
86 pub fn try_row(self) -> Result<Option<Row<E>>, ResponseError> {
87 match self.0.len() {
88 0 => Ok(None),
89 1 => Ok(Some(self.0.into_iter().next().unwrap())),
90 n => Err(ResponseError::not_unique::<E>(n as u32)),
91 }
92 }
93
94 pub fn row(self) -> Result<Row<E>, ResponseError> {
95 self.try_row()?.ok_or_else(ResponseError::not_found::<E>)
96 }
97
98 #[must_use]
99 pub fn rows(self) -> Vec<Row<E>> {
100 self.0
101 }
102
103 pub fn try_entity(self) -> Result<Option<E>, ResponseError> {
108 Ok(self.try_row()?.map(|(_, e)| e))
109 }
110
111 pub fn entity(self) -> Result<E, ResponseError> {
112 self.row().map(|(_, e)| e)
113 }
114
115 #[must_use]
116 pub fn entities(self) -> Vec<E> {
117 self.0.into_iter().map(|(_, e)| e).collect()
118 }
119
120 #[must_use]
125 pub fn id(&self) -> Option<E::Id> {
126 self.0.first().map(|(id, _)| *id)
127 }
128
129 pub fn id_strict(self) -> Result<E::Id, ResponseError> {
130 self.row().map(|(id, _)| id)
131 }
132
133 #[must_use]
134 pub fn ids(&self) -> Vec<E::Id> {
135 self.0.iter().map(|(id, _)| *id).collect()
136 }
137
138 pub fn contains_id(&self, id: &E::Id) -> bool {
139 self.0.iter().any(|(k, _)| k == id)
140 }
141
142 pub fn view(&self) -> Result<View<E>, ResponseError> {
147 self.require_one()?;
148 Ok(self.0[0].1.to_view())
149 }
150
151 pub fn view_opt(&self) -> Result<Option<View<E>>, ResponseError> {
152 match self.count() {
153 0 => Ok(None),
154 1 => Ok(Some(self.0[0].1.to_view())),
155 n => Err(ResponseError::not_unique::<E>(n)),
156 }
157 }
158
159 #[must_use]
160 pub fn views(&self) -> Vec<View<E>> {
161 self.0.iter().map(|(_, e)| e.to_view()).collect()
162 }
163}
164
165impl<E: EntityKind> IntoIterator for Response<E> {
166 type Item = Row<E>;
167 type IntoIter = std::vec::IntoIter<Self::Item>;
168
169 fn into_iter(self) -> Self::IntoIter {
170 self.0.into_iter()
171 }
172}