Skip to main content

apiary_core/
error.rs

1//! Unified error types for Apiary.
2//!
3//! All errors in Apiary are represented by [`ApiaryError`], using `thiserror`
4//! for ergonomic error definitions. Library code never uses `unwrap()` —
5//! all fallible operations return `Result<T, ApiaryError>`.
6
7use crate::types::{BeeId, FrameId};
8use thiserror::Error;
9
10/// The unified error type for all Apiary operations.
11#[derive(Error, Debug)]
12pub enum ApiaryError {
13    /// A storage operation failed.
14    #[error("Storage error: {message}")]
15    Storage {
16        /// Human-readable description of the failure.
17        message: String,
18        /// The underlying error, if available.
19        #[source]
20        source: Option<Box<dyn std::error::Error + Send + Sync>>,
21    },
22
23    /// The requested key was not found in storage.
24    #[error("Not found: {key}")]
25    NotFound {
26        /// The storage key that was not found.
27        key: String,
28    },
29
30    /// A conditional write conflict occurred (another writer committed first).
31    #[error("Write conflict on key: {key}")]
32    WriteConflict {
33        /// The storage key where the conflict occurred.
34        key: String,
35    },
36
37    /// An entity with this name already exists.
38    #[error("{entity_type} already exists: {name}")]
39    AlreadyExists {
40        /// The type of entity (e.g., "Hive", "Frame").
41        entity_type: String,
42        /// The conflicting name.
43        name: String,
44    },
45
46    /// The requested entity was not found in the registry.
47    #[error("{entity_type} not found: {name}")]
48    EntityNotFound {
49        /// The type of entity (e.g., "Hive", "Frame").
50        entity_type: String,
51        /// The name that was not found.
52        name: String,
53    },
54
55    /// A schema validation error occurred during a write.
56    #[error("Schema error: {message}")]
57    Schema {
58        /// Description of the schema mismatch.
59        message: String,
60    },
61
62    /// A bee exceeded its memory budget.
63    #[error(
64        "Memory exceeded for bee {bee_id}: requested {requested} bytes, budget {budget} bytes"
65    )]
66    MemoryExceeded {
67        /// The bee that exceeded its budget.
68        bee_id: BeeId,
69        /// The memory budget in bytes.
70        budget: u64,
71        /// The amount of memory requested in bytes.
72        requested: u64,
73    },
74
75    /// A task exceeded its timeout.
76    #[error("Task timeout: {message}")]
77    TaskTimeout {
78        /// Description of the timed-out task.
79        message: String,
80    },
81
82    /// A task was abandoned after exceeding the retry limit.
83    #[error("Task abandoned: {message}")]
84    TaskAbandoned {
85        /// Description including attempt history.
86        message: String,
87    },
88
89    /// Invalid configuration was provided.
90    #[error("Configuration error: {message}")]
91    Config {
92        /// Description of the configuration problem.
93        message: String,
94    },
95
96    /// A frame path could not be resolved.
97    #[error("Cannot resolve frame path '{path}': {reason}")]
98    Resolution {
99        /// The path that could not be resolved.
100        path: String,
101        /// The reason resolution failed.
102        reason: String,
103    },
104
105    /// An unsupported operation was attempted.
106    #[error("Unsupported: {message}")]
107    Unsupported {
108        /// Description of the unsupported operation.
109        message: String,
110    },
111
112    /// A ledger operation failed.
113    #[error("Ledger error for frame {frame_id}: {message}")]
114    Ledger {
115        /// The frame whose ledger had an error.
116        frame_id: FrameId,
117        /// Description of the ledger error.
118        message: String,
119    },
120
121    /// An internal error (bug).
122    #[error("Internal error: {message}")]
123    Internal {
124        /// Description of the internal error.
125        message: String,
126    },
127
128    /// A conflict occurred during a DDL operation after max retries.
129    #[error("Conflict: {message}")]
130    Conflict {
131        /// Description of the conflict.
132        message: String,
133    },
134
135    /// Serialization or deserialization error.
136    #[error("Serialization error: {0}")]
137    Serialization(String),
138}
139
140impl ApiaryError {
141    /// Create a storage error from a message and source error.
142    pub fn storage(
143        message: impl Into<String>,
144        source: impl std::error::Error + Send + Sync + 'static,
145    ) -> Self {
146        Self::Storage {
147            message: message.into(),
148            source: Some(Box::new(source)),
149        }
150    }
151
152    /// Create a storage error from a message only.
153    pub fn storage_msg(message: impl Into<String>) -> Self {
154        Self::Storage {
155            message: message.into(),
156            source: None,
157        }
158    }
159}