Skip to main content

deaddrop_core/
error.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub enum ErrorCode {
6    Ddp1001InvalidFrame,
7    Ddp1002UnsupportedVersion,
8    Ddp1003UnknownCriticalExtension,
9    Ddp1004UnsupportedHash,
10    Ddp1005BadIdentifier,
11    Ddp1006LimitExceeded,
12    Ddp1007Expired,
13    Ddp1008NotRecipient,
14    Ddp1009Sealed,
15    Dds2001StoreFull,
16    Dds2002CorruptChunk,
17    Dds2003MissingChunk,
18    Dds2004CorruptMetadata,
19    Dda3001AuthFailed,
20    Dda3002Replay,
21    Ddr4001RouteUnavailable,
22    Ddr4002ReplicationExhausted,
23    Ddi5001UnknownContact,
24    Ddc6001Crypto,
25    Ddi7001Io,
26    Ddx0000Internal,
27}
28
29impl ErrorCode {
30    pub fn as_str(self) -> &'static str {
31        match self {
32            Self::Ddp1001InvalidFrame => "DDP1001_INVALID_FRAME",
33            Self::Ddp1002UnsupportedVersion => "DDP1002_UNSUPPORTED_VERSION",
34            Self::Ddp1003UnknownCriticalExtension => "DDP1003_UNKNOWN_CRITICAL_EXTENSION",
35            Self::Ddp1004UnsupportedHash => "DDP1004_UNSUPPORTED_HASH",
36            Self::Ddp1005BadIdentifier => "DDP1005_BAD_IDENTIFIER",
37            Self::Ddp1006LimitExceeded => "DDP1006_LIMIT_EXCEEDED",
38            Self::Ddp1007Expired => "DDP1007_EXPIRED",
39            Self::Ddp1008NotRecipient => "DDP1008_NOT_RECIPIENT",
40            Self::Ddp1009Sealed => "DDP1009_SEALED",
41            Self::Dds2001StoreFull => "DDS2001_STORE_FULL",
42            Self::Dds2002CorruptChunk => "DDS2002_CORRUPT_CHUNK",
43            Self::Dds2003MissingChunk => "DDS2003_MISSING_CHUNK",
44            Self::Dds2004CorruptMetadata => "DDS2004_CORRUPT_METADATA",
45            Self::Dda3001AuthFailed => "DDA3001_AUTH_FAILED",
46            Self::Dda3002Replay => "DDA3002_REPLAY",
47            Self::Ddr4001RouteUnavailable => "DDR4001_ROUTE_UNAVAILABLE",
48            Self::Ddr4002ReplicationExhausted => "DDR4002_REPLICATION_EXHAUSTED",
49            Self::Ddi5001UnknownContact => "DDI5001_UNKNOWN_CONTACT",
50            Self::Ddc6001Crypto => "DDC6001_CRYPTO",
51            Self::Ddi7001Io => "DDI7001_IO",
52            Self::Ddx0000Internal => "DDX0000_INTERNAL",
53        }
54    }
55
56    pub fn category(self) -> &'static str {
57        match self {
58            Self::Ddp1001InvalidFrame
59            | Self::Ddp1002UnsupportedVersion
60            | Self::Ddp1003UnknownCriticalExtension
61            | Self::Ddp1004UnsupportedHash
62            | Self::Ddp1005BadIdentifier
63            | Self::Ddp1006LimitExceeded
64            | Self::Ddp1007Expired
65            | Self::Ddp1008NotRecipient
66            | Self::Ddp1009Sealed => "protocol",
67            Self::Dds2001StoreFull
68            | Self::Dds2002CorruptChunk
69            | Self::Dds2003MissingChunk
70            | Self::Dds2004CorruptMetadata => "store",
71            Self::Dda3001AuthFailed | Self::Dda3002Replay => "auth",
72            Self::Ddr4001RouteUnavailable | Self::Ddr4002ReplicationExhausted => "routing",
73            Self::Ddi5001UnknownContact => "identity",
74            Self::Ddc6001Crypto => "crypto",
75            Self::Ddi7001Io => "io",
76            Self::Ddx0000Internal => "internal",
77        }
78    }
79
80    pub fn retryable(self) -> bool {
81        matches!(
82            self,
83            Self::Dds2001StoreFull | Self::Ddr4001RouteUnavailable | Self::Ddi7001Io
84        )
85    }
86}
87
88impl fmt::Display for ErrorCode {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.write_str(self.as_str())
91    }
92}
93
94#[derive(Debug, thiserror::Error)]
95pub enum DdError {
96    #[error("{code}: {message}")]
97    Coded {
98        code: ErrorCode,
99        message: String,
100        context: Option<String>,
101    },
102    #[error(transparent)]
103    Io(#[from] std::io::Error),
104}
105
106impl DdError {
107    pub fn protocol(code: ErrorCode, message: impl Into<String>) -> Self {
108        Self::Coded {
109            code,
110            message: message.into(),
111            context: None,
112        }
113    }
114
115    pub fn with_context(mut self, ctx: impl Into<String>) -> Self {
116        if let Self::Coded { context, .. } = &mut self {
117            *context = Some(ctx.into());
118        }
119        self
120    }
121
122    pub fn code(&self) -> ErrorCode {
123        match self {
124            Self::Coded { code, .. } => *code,
125            Self::Io(_) => ErrorCode::Ddi7001Io,
126        }
127    }
128
129    pub fn retryable(&self) -> bool {
130        self.code().retryable()
131    }
132
133    pub fn crypto(msg: impl Into<String>) -> Self {
134        Self::protocol(ErrorCode::Ddc6001Crypto, msg)
135    }
136
137    pub fn invalid_frame(msg: impl Into<String>) -> Self {
138        Self::protocol(ErrorCode::Ddp1001InvalidFrame, msg)
139    }
140}