1use std::fmt;
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, Error>;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ErrorCode {
12 KeyNotFound,
14 KeyExpired,
16 InvalidKeyState,
18 CryptoFailure,
20 StorageFailure,
22 InsufficientEntropy,
24 RotationFailure,
26 SerializationFailure,
28 IoFailure,
30 AuthenticationFailure,
32 ConfigurationError,
34}
35
36impl fmt::Display for ErrorCode {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 Self::KeyNotFound => write!(f, "KEY_NOT_FOUND"),
40 Self::KeyExpired => write!(f, "KEY_EXPIRED"),
41 Self::InvalidKeyState => write!(f, "INVALID_KEY_STATE"),
42 Self::CryptoFailure => write!(f, "CRYPTO_FAILURE"),
43 Self::StorageFailure => write!(f, "STORAGE_FAILURE"),
44 Self::InsufficientEntropy => write!(f, "INSUFFICIENT_ENTROPY"),
45 Self::RotationFailure => write!(f, "ROTATION_FAILURE"),
46 Self::SerializationFailure => write!(f, "SERIALIZATION_FAILURE"),
47 Self::IoFailure => write!(f, "IO_FAILURE"),
48 Self::AuthenticationFailure => write!(f, "AUTHENTICATION_FAILURE"),
49 Self::ConfigurationError => write!(f, "CONFIGURATION_ERROR"),
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct ErrorContext {
57 pub operation: String,
59 pub key_id: Option<String>,
61 pub details: Option<String>,
63}
64
65impl ErrorContext {
66 pub fn new<S: Into<String>>(operation: S) -> Self {
68 Self {
69 operation: operation.into(),
70 key_id: None,
71 details: None,
72 }
73 }
74
75 pub fn with_key_id<S: Into<String>>(mut self, key_id: S) -> Self {
77 self.key_id = Some(key_id.into());
78 self
79 }
80
81 pub fn with_details<S: Into<String>>(mut self, details: S) -> Self {
83 self.details = Some(details.into());
84 self
85 }
86}
87
88#[derive(Debug, Error)]
90pub enum Error {
91 #[error("key not found: {key_id} (operation: {operation})")]
93 KeyNotFound {
94 key_id: String,
96 operation: String,
98 },
99
100 #[error("key has expired: {key_id} (expired at: {expired_at:?})")]
102 KeyExpired {
103 key_id: String,
105 expired_at: std::time::SystemTime,
107 },
108
109 #[error("invalid key state: {state} for operation '{operation}' on key {key_id}")]
111 InvalidKeyState {
112 key_id: String,
114 state: String,
116 operation: String,
118 },
119
120 #[error("cryptographic error during {operation}: {message}")]
122 CryptoError {
123 operation: String,
125 message: String,
127 key_id: Option<String>,
129 },
130
131 #[error("storage error during {operation}: {message}")]
133 StorageError {
134 operation: String,
136 message: String,
138 path: Option<String>,
140 },
141
142 #[error("insufficient entropy for operation: {operation}")]
144 InsufficientEntropy {
145 operation: String,
147 },
148
149 #[error("rotation failed for key {key_id}: {reason}")]
151 RotationFailed {
152 key_id: String,
154 reason: String,
156 },
157
158 #[error("serialization error during {operation}: {message}")]
160 SerializationError {
161 operation: String,
163 message: String,
165 },
166
167 #[error("I/O error during {operation}: {source}")]
169 IoError {
170 operation: String,
172 #[source]
174 source: std::io::Error,
175 },
176
177 #[error("authentication failed: {reason}")]
179 AuthenticationFailed {
180 reason: String,
182 attempts: Option<u32>,
184 },
185
186 #[error("configuration error: {message}")]
188 ConfigurationError {
189 message: String,
191 },
192}
193
194impl Error {
195 pub fn code(&self) -> ErrorCode {
197 match self {
198 Self::KeyNotFound { .. } => ErrorCode::KeyNotFound,
199 Self::KeyExpired { .. } => ErrorCode::KeyExpired,
200 Self::InvalidKeyState { .. } => ErrorCode::InvalidKeyState,
201 Self::CryptoError { .. } => ErrorCode::CryptoFailure,
202 Self::StorageError { .. } => ErrorCode::StorageFailure,
203 Self::InsufficientEntropy { .. } => ErrorCode::InsufficientEntropy,
204 Self::RotationFailed { .. } => ErrorCode::RotationFailure,
205 Self::SerializationError { .. } => ErrorCode::SerializationFailure,
206 Self::IoError { .. } => ErrorCode::IoFailure,
207 Self::AuthenticationFailed { .. } => ErrorCode::AuthenticationFailure,
208 Self::ConfigurationError { .. } => ErrorCode::ConfigurationError,
209 }
210 }
211
212 pub fn crypto<S: Into<String>>(operation: S, message: S) -> Self {
214 Self::CryptoError {
215 operation: operation.into(),
216 message: message.into(),
217 key_id: None,
218 }
219 }
220
221 pub fn crypto_with_key<S: Into<String>>(operation: S, message: S, key_id: S) -> Self {
223 Self::CryptoError {
224 operation: operation.into(),
225 message: message.into(),
226 key_id: Some(key_id.into()),
227 }
228 }
229
230 pub fn storage<S: Into<String>>(operation: S, message: S) -> Self {
232 Self::StorageError {
233 operation: operation.into(),
234 message: message.into(),
235 path: None,
236 }
237 }
238
239 pub fn storage_with_path<S: Into<String>>(operation: S, message: S, path: S) -> Self {
241 Self::StorageError {
242 operation: operation.into(),
243 message: message.into(),
244 path: Some(path.into()),
245 }
246 }
247
248 pub fn is_retryable(&self) -> bool {
250 matches!(self, Self::StorageError { .. } | Self::IoError { .. })
251 }
252
253 pub fn is_auth_failure(&self) -> bool {
255 matches!(self, Self::AuthenticationFailed { .. })
256 }
257}
258
259impl From<std::io::Error> for Error {
261 fn from(err: std::io::Error) -> Self {
262 Self::IoError {
263 operation: "unknown".to_string(),
264 source: err,
265 }
266 }
267}