ankurah_core/
error.rs

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