1use std::{collections::BTreeSet, convert::Infallible};
2
3use ankurah_proto::{CollectionId, DecodeError, EntityId, EventId};
4use thiserror::Error;
5
6use crate::{connector::SendError, policy::AccessDenied};
7
8#[derive(Error, Debug)]
9#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
10#[cfg_attr(feature = "uniffi", uniffi(flat_error))]
11pub enum RetrievalError {
12 #[error("access denied")]
13 AccessDenied(AccessDenied),
14 #[error("Parse error: {0}")]
15 ParseError(ankql::error::ParseError),
16 #[error("Entity not found: {0:?}")]
17 EntityNotFound(EntityId),
18 #[error("Event not found: {0:?}")]
19 EventNotFound(EventId),
20 #[error("Storage error: {0}")]
21 StorageError(Box<dyn std::error::Error + Send + Sync + 'static>),
22 #[error("Collection not found: {0}")]
23 CollectionNotFound(CollectionId),
24 #[error("Update failed: {0}")]
25 FailedUpdate(Box<dyn std::error::Error + Send + Sync + 'static>),
26 #[error("Deserialization error: {0}")]
27 DeserializationError(bincode::Error),
28 #[error("No durable peers available for fetch operation")]
29 NoDurablePeers,
30 #[error("Other error: {0}")]
31 Other(String),
32 #[error("bucket name must only contain valid characters")]
33 InvalidBucketName,
34 #[error("ankql filter: {0}")]
35 AnkqlFilter(crate::selection::filter::Error),
36 #[error("Future join: {0}")]
37 FutureJoin(tokio::task::JoinError),
38 #[error("{0}")]
39 Anyhow(anyhow::Error),
40 #[error("Decode error: {0}")]
41 DecodeError(DecodeError),
42 #[error("State error: {0}")]
43 StateError(StateError),
44 #[error("Mutation error: {0}")]
45 MutationError(Box<MutationError>),
46 #[error("Property error: {0}")]
47 PropertyError(Box<crate::property::PropertyError>),
48 #[error("Request error: {0}")]
49 RequestError(RequestError),
50 #[error("Apply error: {0}")]
51 ApplyError(ApplyError),
52}
53
54impl From<RequestError> for RetrievalError {
55 fn from(err: RequestError) -> Self { RetrievalError::RequestError(err) }
56}
57
58impl From<crate::property::PropertyError> for RetrievalError {
59 fn from(err: crate::property::PropertyError) -> Self { RetrievalError::PropertyError(Box::new(err)) }
60}
61
62impl From<tokio::task::JoinError> for RetrievalError {
63 fn from(err: tokio::task::JoinError) -> Self { RetrievalError::FutureJoin(err) }
64}
65
66impl From<MutationError> for RetrievalError {
67 fn from(err: MutationError) -> Self { RetrievalError::MutationError(Box::new(err)) }
68}
69
70impl RetrievalError {
71 pub fn storage(err: impl std::error::Error + Send + Sync + 'static) -> Self { RetrievalError::StorageError(Box::new(err)) }
72}
73
74impl From<bincode::Error> for RetrievalError {
75 fn from(e: bincode::Error) -> Self { RetrievalError::DeserializationError(e) }
76}
77
78impl From<crate::selection::filter::Error> for RetrievalError {
79 fn from(err: crate::selection::filter::Error) -> Self { RetrievalError::AnkqlFilter(err) }
80}
81
82impl From<anyhow::Error> for RetrievalError {
83 fn from(err: anyhow::Error) -> Self { RetrievalError::Anyhow(err) }
84}
85
86impl From<Infallible> for RetrievalError {
87 fn from(_: Infallible) -> Self { unreachable!("Infallible can never be constructed") }
88}
89
90#[derive(Error, Debug)]
91pub enum RequestError {
92 #[error("Peer not connected")]
93 PeerNotConnected,
94 #[error("Connection lost")]
95 ConnectionLost,
96 #[error("Server error: {0}")]
97 ServerError(String),
98 #[error("Send error: {0}")]
99 SendError(SendError),
100 #[error("Internal channel closed")]
101 InternalChannelClosed,
102 #[error("Unexpected response: {0:?}")]
103 UnexpectedResponse(ankurah_proto::NodeResponseBody),
104 #[error("Access denied: {0}")]
105 AccessDenied(AccessDenied),
106}
107
108impl From<AccessDenied> for RequestError {
109 fn from(err: AccessDenied) -> Self { RequestError::AccessDenied(err) }
110}
111
112impl From<SendError> for RequestError {
113 fn from(err: SendError) -> Self { RequestError::SendError(err) }
114}
115
116#[derive(Error, Debug)]
117pub enum SubscriptionError {
118 #[error("predicate not found")]
119 PredicateNotFound,
120 #[error("already subscribed to predicate")]
121 PredicateAlreadySubscribed,
122 #[error("subscription not found")]
123 SubscriptionNotFound,
124}
125
126impl From<DecodeError> for RetrievalError {
127 fn from(err: DecodeError) -> Self { RetrievalError::DecodeError(err) }
128}
129
130#[derive(Error, Debug)]
131#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
132#[cfg_attr(feature = "uniffi", uniffi(flat_error))]
133pub enum MutationError {
134 #[error("access denied")]
135 AccessDenied(AccessDenied),
136 #[error("already exists")]
137 AlreadyExists,
138 #[error("retrieval error: {0}")]
139 RetrievalError(RetrievalError),
140 #[error("state error: {0}")]
141 StateError(StateError),
142 #[error("failed update: {0}")]
143 UpdateFailed(Box<dyn std::error::Error + Send + Sync + 'static>),
144 #[error("failed step: {0}: {1}")]
145 FailedStep(&'static str, String),
146 #[error("failed to set property: {0}: {1}")]
147 FailedToSetProperty(&'static str, String),
148 #[error("general error: {0}")]
149 General(Box<dyn std::error::Error + Send + Sync + 'static>),
150 #[error("no durable peers available")]
151 NoDurablePeers,
152 #[error("decode error: {0}")]
153 DecodeError(DecodeError),
154 #[error("lineage error: {0}")]
155 LineageError(LineageError),
156 #[error("peer rejected transaction")]
157 PeerRejected,
158 #[error("invalid event")]
159 InvalidEvent,
160 #[error("invalid update")]
161 InvalidUpdate(&'static str),
162 #[error("property error: {0}")]
163 PropertyError(crate::property::PropertyError),
164 #[error("future join: {0}")]
165 FutureJoin(tokio::task::JoinError),
166 #[error("anyhow error: {0}")]
167 Anyhow(anyhow::Error),
168 #[error("TOCTOU attempts exhausted")]
169 TOCTOUAttemptsExhausted,
170}
171
172impl From<tokio::task::JoinError> for MutationError {
173 fn from(err: tokio::task::JoinError) -> Self { MutationError::FutureJoin(err) }
174}
175
176impl From<anyhow::Error> for MutationError {
177 fn from(err: anyhow::Error) -> Self { MutationError::Anyhow(err) }
178}
179
180#[derive(Debug)]
181pub enum LineageError {
182 Disjoint,
184 BudgetExceeded { original_budget: usize, subject_frontier: BTreeSet<EventId>, other_frontier: BTreeSet<EventId> },
186}
187
188impl std::fmt::Display for LineageError {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 match self {
191 LineageError::Disjoint => write!(f, "disjoint (different genesis events)"),
192 LineageError::BudgetExceeded { original_budget, subject_frontier, other_frontier } => {
193 let subject: Vec<_> = subject_frontier.iter().map(|id| id.to_base64_short()).collect();
194 let other: Vec<_> = other_frontier.iter().map(|id| id.to_base64_short()).collect();
195 write!(f, "budget exceeded ({}): subject[{}] other[{}]", original_budget, subject.join(", "), other.join(", "))
196 }
197 }
198 }
199}
200
201impl std::error::Error for LineageError {}
202
203impl From<LineageError> for MutationError {
204 fn from(err: LineageError) -> Self { MutationError::LineageError(err) }
205}
206
207impl From<DecodeError> for MutationError {
208 fn from(err: DecodeError) -> Self { MutationError::DecodeError(err) }
209}
210
211#[cfg(feature = "wasm")]
212impl From<MutationError> for wasm_bindgen::JsValue {
213 fn from(err: MutationError) -> Self { err.to_string().into() }
214}
215#[cfg(feature = "wasm")]
216impl From<RetrievalError> for wasm_bindgen::JsValue {
217 fn from(err: RetrievalError) -> Self { err.to_string().into() }
218}
219
220impl From<AccessDenied> for MutationError {
221 fn from(err: AccessDenied) -> Self { MutationError::AccessDenied(err) }
222}
223
224impl From<bincode::Error> for MutationError {
225 fn from(e: bincode::Error) -> Self { MutationError::StateError(StateError::SerializationError(e)) }
226}
227
228impl From<RetrievalError> for MutationError {
229 fn from(err: RetrievalError) -> Self {
230 match err {
231 RetrievalError::AccessDenied(a) => MutationError::AccessDenied(a),
232 _ => MutationError::RetrievalError(err),
233 }
234 }
235}
236impl From<AccessDenied> for RetrievalError {
237 fn from(err: AccessDenied) -> Self { RetrievalError::AccessDenied(err) }
238}
239
240impl From<SubscriptionError> for RetrievalError {
241 fn from(err: SubscriptionError) -> Self { RetrievalError::Anyhow(anyhow::anyhow!("Subscription error: {:?}", err)) }
242}
243
244#[derive(Error, Debug)]
245pub enum StateError {
246 #[error("serialization error: {0}")]
247 SerializationError(Box<dyn std::error::Error + Send + Sync + 'static>),
248 #[error("DDL error: {0}")]
249 DDLError(Box<dyn std::error::Error + Send + Sync + 'static>),
250 #[error("DMLError: {0}")]
251 DMLError(Box<dyn std::error::Error + Send + Sync + 'static>),
252}
253
254impl From<bincode::Error> for StateError {
255 fn from(e: bincode::Error) -> Self { StateError::SerializationError(Box::new(e)) }
256}
257
258impl From<StateError> for MutationError {
259 fn from(err: StateError) -> Self { MutationError::StateError(err) }
260}
261
262impl From<crate::property::PropertyError> for MutationError {
263 fn from(err: crate::property::PropertyError) -> Self { MutationError::PropertyError(err) }
264}
265
266impl From<StateError> for RetrievalError {
267 fn from(err: StateError) -> Self { RetrievalError::StateError(err) }
268}
269
270#[derive(Error, Debug)]
271pub enum ValidationError {
272 #[error("Deserialization error: {0}")]
273 Deserialization(Box<dyn std::error::Error + Send + Sync + 'static>),
274 #[error("Validation failed: {0}")]
275 ValidationFailed(String),
276 #[error("Serialization error: {0}")]
277 Serialization(String),
278 #[error("Rejected: {0}")]
279 Rejected(&'static str),
280}
281
282#[derive(Debug)]
284pub enum ApplyError {
285 Items(Vec<ApplyErrorItem>),
286 CollectionNotFound(CollectionId),
287 RetrievalError(Box<RetrievalError>),
288 MutationError(Box<MutationError>),
289}
290
291impl std::fmt::Display for ApplyError {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 match self {
294 ApplyError::Items(errors) => {
295 write!(f, "Failed to apply {} delta(s)", errors.len())?;
296 for (i, err) in errors.iter().enumerate() {
297 write!(f, "\n [{}] {}", i + 1, err)?;
298 }
299 Ok(())
300 }
301 ApplyError::CollectionNotFound(id) => write!(f, "Collection not found: {}", id),
302 ApplyError::RetrievalError(e) => write!(f, "Retrieval error: {}", e),
303 ApplyError::MutationError(e) => write!(f, "Mutation error: {}", e),
304 }
305 }
306}
307
308impl std::error::Error for ApplyError {
309 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
310 match self {
311 ApplyError::RetrievalError(e) => Some(e),
312 ApplyError::MutationError(e) => Some(e),
313 _ => None,
314 }
315 }
316}
317
318#[derive(Debug)]
320pub struct ApplyErrorItem {
321 pub entity_id: EntityId,
322 pub collection: CollectionId,
323 pub cause: MutationError,
324}
325
326impl std::fmt::Display for ApplyErrorItem {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 write!(f, "Failed to apply delta for entity {} in collection {}: {}", self.entity_id.to_base64_short(), self.collection, self.cause)
329 }
330}
331
332impl From<RetrievalError> for ApplyError {
333 fn from(err: RetrievalError) -> Self { ApplyError::RetrievalError(Box::new(err)) }
334}
335
336impl From<MutationError> for ApplyError {
337 fn from(err: MutationError) -> Self { ApplyError::MutationError(Box::new(err)) }
338}
339
340impl From<ApplyError> for RetrievalError {
341 fn from(err: ApplyError) -> Self { RetrievalError::ApplyError(err) }
342}