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}