umi_memory/storage/
error.rs1use thiserror::Error;
6
7#[derive(Debug, Clone, Error)]
9pub enum StorageError {
10 #[error("entity not found: {id}")]
12 NotFound {
13 id: String,
15 },
16
17 #[error("entity already exists: {id}")]
19 AlreadyExists {
20 id: String,
22 },
23
24 #[error("validation error: {message}")]
26 Validation {
27 message: String,
29 },
30
31 #[error("connection error: {message}")]
33 Connection {
34 message: String,
36 },
37
38 #[error("query error: {message}")]
40 Query {
41 message: String,
43 },
44
45 #[error("timeout after {duration_ms}ms")]
47 Timeout {
48 duration_ms: u64,
50 },
51
52 #[error("simulated fault: {fault_type}")]
54 SimulatedFault {
55 fault_type: String,
57 },
58
59 #[error("internal error: {message}")]
61 Internal {
62 message: String,
64 },
65
66 #[error("connection failed: {0}")]
68 ConnectionFailed(String),
69
70 #[error("write failed: {0}")]
72 WriteFailed(String),
73
74 #[error("read failed: {0}")]
76 ReadFailed(String),
77
78 #[error("serialization error: {0}")]
80 SerializationError(String),
81
82 #[error("deserialization error: {0}")]
84 DeserializationError(String),
85}
86
87impl StorageError {
88 #[must_use]
90 pub fn not_found(id: impl Into<String>) -> Self {
91 Self::NotFound { id: id.into() }
92 }
93
94 #[must_use]
96 pub fn already_exists(id: impl Into<String>) -> Self {
97 Self::AlreadyExists { id: id.into() }
98 }
99
100 #[must_use]
102 pub fn validation(message: impl Into<String>) -> Self {
103 Self::Validation {
104 message: message.into(),
105 }
106 }
107
108 #[must_use]
110 pub fn connection(message: impl Into<String>) -> Self {
111 Self::Connection {
112 message: message.into(),
113 }
114 }
115
116 #[must_use]
118 pub fn query(message: impl Into<String>) -> Self {
119 Self::Query {
120 message: message.into(),
121 }
122 }
123
124 #[must_use]
126 pub fn timeout(duration_ms: u64) -> Self {
127 Self::Timeout { duration_ms }
128 }
129
130 #[must_use]
132 pub fn simulated_fault(fault_type: impl Into<String>) -> Self {
133 Self::SimulatedFault {
134 fault_type: fault_type.into(),
135 }
136 }
137
138 #[must_use]
140 pub fn internal(message: impl Into<String>) -> Self {
141 Self::Internal {
142 message: message.into(),
143 }
144 }
145
146 #[must_use]
148 pub fn read(message: impl Into<String>) -> Self {
149 Self::Query {
150 message: format!("read: {}", message.into()),
151 }
152 }
153
154 #[must_use]
156 pub fn write(message: impl Into<String>) -> Self {
157 Self::Query {
158 message: format!("write: {}", message.into()),
159 }
160 }
161
162 #[must_use]
164 pub fn is_transient(&self) -> bool {
165 matches!(
166 self,
167 Self::Connection { .. } | Self::Timeout { .. } | Self::SimulatedFault { .. }
168 )
169 }
170}
171
172pub type StorageResult<T> = Result<T, StorageError>;
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_error_constructors() {
181 let err = StorageError::not_found("test-id");
182 assert!(matches!(err, StorageError::NotFound { id } if id == "test-id"));
183
184 let err = StorageError::validation("invalid content");
185 assert!(
186 matches!(err, StorageError::Validation { message } if message == "invalid content")
187 );
188 }
189
190 #[test]
191 fn test_is_transient() {
192 assert!(StorageError::connection("failed").is_transient());
193 assert!(StorageError::timeout(1000).is_transient());
194 assert!(StorageError::simulated_fault("network").is_transient());
195
196 assert!(!StorageError::not_found("id").is_transient());
197 assert!(!StorageError::validation("bad").is_transient());
198 }
199}