1use std::io;
9use thiserror::Error;
10
11pub type Result<T> = std::result::Result<T, Error>;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(u16)]
26pub enum ErrorCode {
27 InvalidHash = 1001,
29 InvalidKey = 1002,
30 InvalidSignature = 1003,
31 InvalidEvent = 1004,
32 InvalidBlock = 1005,
33 InvalidProof = 1006,
34 InvalidTimestamp = 1007,
35 InvalidFormat = 1008,
36 InvalidInput = 1009,
37
38 EventNotFound = 2001,
40 BlockNotFound = 2002,
41 NodeNotFound = 2003,
42 PeakNotFound = 2004,
43
44 DuplicateEvent = 3001,
46 DuplicateBlock = 3002,
47 ChainFork = 3003,
48
49 Unauthorized = 4001,
51 Forbidden = 4002,
52
53 StorageRead = 5001,
55 StorageWrite = 5002,
56 StorageCorruption = 5003,
57 StorageInit = 5004,
58
59 Serialization = 6001,
61 Deserialization = 6002,
62 Internal = 6003,
63
64 ConnectionFailed = 7001,
66 Timeout = 7002,
67 ProtocolError = 7003,
68}
69
70impl ErrorCode {
71 pub fn code(self) -> u16 {
73 self as u16
74 }
75
76 pub fn is_client_error(self) -> bool {
78 (1000..5000).contains(&self.code())
79 }
80
81 pub fn is_server_error(self) -> bool {
83 self.code() >= 5000
84 }
85
86 pub fn is_retryable(self) -> bool {
88 matches!(
89 self,
90 ErrorCode::StorageRead
91 | ErrorCode::StorageWrite
92 | ErrorCode::ConnectionFailed
93 | ErrorCode::Timeout
94 )
95 }
96}
97
98impl std::fmt::Display for ErrorCode {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 write!(f, "E{:04}", self.code())
101 }
102}
103
104#[derive(Debug, Error)]
106pub enum Error {
107 #[error("[{code}] invalid input: {message}")]
112 InvalidInput { code: ErrorCode, message: String },
113
114 #[error("[{code}] invalid hash: {message}")]
116 InvalidHash {
117 code: ErrorCode,
118 message: String,
119 #[source]
120 source: Option<Box<dyn std::error::Error + Send + Sync>>,
121 },
122
123 #[error("[{code}] invalid key: {message}")]
125 InvalidKey {
126 code: ErrorCode,
127 message: String,
128 #[source]
129 source: Option<Box<dyn std::error::Error + Send + Sync>>,
130 },
131
132 #[error("[{code}] signature verification failed")]
134 InvalidSignature { code: ErrorCode },
135
136 #[error("[{code}] invalid event: {message}")]
138 InvalidEvent { code: ErrorCode, message: String },
139
140 #[error("[{code}] invalid block: {message}")]
142 InvalidBlock { code: ErrorCode, message: String },
143
144 #[error("[{code}] invalid proof: {message}")]
146 InvalidProof { code: ErrorCode, message: String },
147
148 #[error("[{code}] not found: {message}")]
153 NotFound { code: ErrorCode, message: String },
154
155 #[error("[{code}] duplicate: {message}")]
160 Duplicate { code: ErrorCode, message: String },
161
162 #[error("[{code}] storage error: {message}")]
167 Storage {
168 code: ErrorCode,
169 message: String,
170 #[source]
171 source: Option<Box<dyn std::error::Error + Send + Sync>>,
172 },
173
174 #[error("[{code}] serialization error: {message}")]
179 Serialization {
180 code: ErrorCode,
181 message: String,
182 #[source]
183 source: Option<Box<dyn std::error::Error + Send + Sync>>,
184 },
185
186 #[error("[{code}] internal error: {message}")]
191 Internal { code: ErrorCode, message: String },
192}
193
194impl Error {
195 pub fn code(&self) -> ErrorCode {
197 match self {
198 Error::InvalidInput { code, .. } => *code,
199 Error::InvalidHash { code, .. } => *code,
200 Error::InvalidKey { code, .. } => *code,
201 Error::InvalidSignature { code } => *code,
202 Error::InvalidEvent { code, .. } => *code,
203 Error::InvalidBlock { code, .. } => *code,
204 Error::InvalidProof { code, .. } => *code,
205 Error::NotFound { code, .. } => *code,
206 Error::Duplicate { code, .. } => *code,
207 Error::Storage { code, .. } => *code,
208 Error::Serialization { code, .. } => *code,
209 Error::Internal { code, .. } => *code,
210 }
211 }
212
213 pub fn is_client_error(&self) -> bool {
215 self.code().is_client_error()
216 }
217
218 pub fn is_server_error(&self) -> bool {
220 self.code().is_server_error()
221 }
222
223 pub fn is_retryable(&self) -> bool {
225 self.code().is_retryable()
226 }
227}
228
229impl Error {
234 pub fn invalid_input(message: impl Into<String>) -> Self {
236 Error::InvalidInput {
237 code: ErrorCode::InvalidInput,
238 message: message.into(),
239 }
240 }
241
242 pub fn invalid_hash(message: impl Into<String>) -> Self {
244 Error::InvalidHash {
245 code: ErrorCode::InvalidHash,
246 message: message.into(),
247 source: None,
248 }
249 }
250
251 pub fn invalid_key(message: impl Into<String>) -> Self {
253 Error::InvalidKey {
254 code: ErrorCode::InvalidKey,
255 message: message.into(),
256 source: None,
257 }
258 }
259
260 pub fn invalid_signature() -> Self {
262 Error::InvalidSignature {
263 code: ErrorCode::InvalidSignature,
264 }
265 }
266
267 pub fn invalid_event(message: impl Into<String>) -> Self {
269 Error::InvalidEvent {
270 code: ErrorCode::InvalidEvent,
271 message: message.into(),
272 }
273 }
274
275 pub fn invalid_block(message: impl Into<String>) -> Self {
277 Error::InvalidBlock {
278 code: ErrorCode::InvalidBlock,
279 message: message.into(),
280 }
281 }
282
283 pub fn invalid_proof(message: impl Into<String>) -> Self {
285 Error::InvalidProof {
286 code: ErrorCode::InvalidProof,
287 message: message.into(),
288 }
289 }
290
291 pub fn event_not_found(message: impl Into<String>) -> Self {
293 Error::NotFound {
294 code: ErrorCode::EventNotFound,
295 message: message.into(),
296 }
297 }
298
299 pub fn block_not_found(message: impl Into<String>) -> Self {
301 Error::NotFound {
302 code: ErrorCode::BlockNotFound,
303 message: message.into(),
304 }
305 }
306
307 pub fn not_found(message: impl Into<String>) -> Self {
309 Error::NotFound {
310 code: ErrorCode::NodeNotFound,
311 message: message.into(),
312 }
313 }
314
315 pub fn duplicate(message: impl Into<String>) -> Self {
317 Error::Duplicate {
318 code: ErrorCode::DuplicateEvent,
319 message: message.into(),
320 }
321 }
322
323 pub fn storage(message: impl Into<String>) -> Self {
325 Error::Storage {
326 code: ErrorCode::StorageRead,
327 message: message.into(),
328 source: None,
329 }
330 }
331
332 pub fn internal(message: impl Into<String>) -> Self {
334 Error::Internal {
335 code: ErrorCode::Internal,
336 message: message.into(),
337 }
338 }
339}
340
341impl From<bincode::Error> for Error {
346 fn from(e: bincode::Error) -> Self {
347 Error::Serialization {
348 code: ErrorCode::Serialization,
349 message: e.to_string(),
350 source: Some(Box::new(e)),
351 }
352 }
353}
354
355impl From<serde_json::Error> for Error {
356 fn from(e: serde_json::Error) -> Self {
357 Error::Serialization {
358 code: ErrorCode::Serialization,
359 message: e.to_string(),
360 source: Some(Box::new(e)),
361 }
362 }
363}
364
365impl From<io::Error> for Error {
366 fn from(e: io::Error) -> Self {
367 Error::Storage {
368 code: ErrorCode::StorageRead,
369 message: e.to_string(),
370 source: Some(Box::new(e)),
371 }
372 }
373}
374
375impl From<hex::FromHexError> for Error {
376 fn from(e: hex::FromHexError) -> Self {
377 Error::InvalidHash {
378 code: ErrorCode::InvalidHash,
379 message: e.to_string(),
380 source: Some(Box::new(e)),
381 }
382 }
383}
384
385#[allow(unused_macros)]
394macro_rules! impl_from_string {
395 ($variant:ident, $code:expr) => {
396 impl From<&str> for $variant {
397 fn from(s: &str) -> Self {
398 $variant(s.to_string())
399 }
400 }
401 };
402}
403
404#[doc(hidden)]
408pub struct InvalidHashCompat(pub String);
409#[doc(hidden)]
410pub struct InvalidKeyCompat(pub String);
411#[doc(hidden)]
412pub struct InvalidEventCompat(pub String);
413#[doc(hidden)]
414pub struct InvalidBlockCompat(pub String);
415#[doc(hidden)]
416pub struct InvalidProofCompat(pub String);
417#[doc(hidden)]
418pub struct StorageCompat(pub String);
419#[doc(hidden)]
420pub struct NotFoundCompat(pub String);
421#[doc(hidden)]
422pub struct DuplicateCompat(pub String);
423#[doc(hidden)]
424pub struct SerializationCompat(pub String);
425#[doc(hidden)]
426pub struct InternalCompat(pub String);
427
428impl From<InvalidHashCompat> for Error {
429 fn from(c: InvalidHashCompat) -> Self {
430 Error::invalid_hash(c.0)
431 }
432}
433
434impl From<InvalidKeyCompat> for Error {
435 fn from(c: InvalidKeyCompat) -> Self {
436 Error::invalid_key(c.0)
437 }
438}
439
440impl From<InvalidEventCompat> for Error {
441 fn from(c: InvalidEventCompat) -> Self {
442 Error::invalid_event(c.0)
443 }
444}
445
446impl From<InvalidBlockCompat> for Error {
447 fn from(c: InvalidBlockCompat) -> Self {
448 Error::invalid_block(c.0)
449 }
450}
451
452impl From<InvalidProofCompat> for Error {
453 fn from(c: InvalidProofCompat) -> Self {
454 Error::invalid_proof(c.0)
455 }
456}
457
458impl From<StorageCompat> for Error {
459 fn from(c: StorageCompat) -> Self {
460 Error::storage(c.0)
461 }
462}
463
464impl From<NotFoundCompat> for Error {
465 fn from(c: NotFoundCompat) -> Self {
466 Error::not_found(c.0)
467 }
468}
469
470impl From<DuplicateCompat> for Error {
471 fn from(c: DuplicateCompat) -> Self {
472 Error::duplicate(c.0)
473 }
474}
475
476impl From<SerializationCompat> for Error {
477 fn from(c: SerializationCompat) -> Self {
478 Error::Serialization {
479 code: ErrorCode::Serialization,
480 message: c.0,
481 source: None,
482 }
483 }
484}
485
486impl From<InternalCompat> for Error {
487 fn from(c: InternalCompat) -> Self {
488 Error::internal(c.0)
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495
496 #[test]
497 fn test_error_codes() {
498 assert_eq!(ErrorCode::InvalidHash.code(), 1001);
499 assert_eq!(ErrorCode::EventNotFound.code(), 2001);
500 assert_eq!(ErrorCode::StorageRead.code(), 5001);
501 }
502
503 #[test]
504 fn test_error_categorization() {
505 assert!(ErrorCode::InvalidHash.is_client_error());
506 assert!(!ErrorCode::InvalidHash.is_server_error());
507
508 assert!(ErrorCode::StorageRead.is_server_error());
509 assert!(!ErrorCode::StorageRead.is_client_error());
510 }
511
512 #[test]
513 fn test_retryable() {
514 assert!(ErrorCode::StorageRead.is_retryable());
515 assert!(ErrorCode::Timeout.is_retryable());
516 assert!(!ErrorCode::InvalidHash.is_retryable());
517 }
518
519 #[test]
520 fn test_error_display() {
521 let e = Error::invalid_hash("bad hex");
522 assert!(e.to_string().contains("E1001"));
523 assert!(e.to_string().contains("bad hex"));
524 }
525
526 #[test]
527 fn test_error_code_display() {
528 assert_eq!(ErrorCode::InvalidHash.to_string(), "E1001");
529 assert_eq!(ErrorCode::Internal.to_string(), "E6003");
530 }
531
532 #[test]
533 fn test_from_bincode() {
534 let bad_data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; let bincode_err: bincode::Error = bincode::deserialize::<String>(&bad_data).unwrap_err();
537 let err: Error = bincode_err.into();
538 assert_eq!(err.code(), ErrorCode::Serialization);
539 assert!(err.is_server_error());
540 }
541
542 #[test]
543 fn test_error_constructors() {
544 let e = Error::invalid_event("missing field");
545 assert_eq!(e.code(), ErrorCode::InvalidEvent);
546 assert!(e.is_client_error());
547
548 let e = Error::storage("disk full");
549 assert_eq!(e.code(), ErrorCode::StorageRead);
550 assert!(e.is_server_error());
551 }
552}