1use std::{
16 backtrace::Backtrace,
17 fmt::{Debug, Display},
18 sync::Arc,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum ErrorKind {
24 Io,
26 External,
28 Config,
30 ChannelClosed,
32 TaskCancelled,
34 Join,
36 Parse,
38 BufferSizeLimit,
44 ChecksumMismatch,
46 MagicMismatch,
48 OutOfRange,
50 NoSpace,
52 Closed,
54 Recover,
56 Unsupported,
58}
59
60impl ErrorKind {
61 pub fn into_static(self) -> &'static str {
63 self.into()
64 }
65}
66
67impl Display for ErrorKind {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{}", self.into_static())
70 }
71}
72
73impl From<ErrorKind> for &'static str {
74 fn from(v: ErrorKind) -> &'static str {
75 match v {
76 ErrorKind::Io => "I/O error",
77 ErrorKind::External => "External error",
78 ErrorKind::Config => "Config error",
79 ErrorKind::ChannelClosed => "Channel closed",
80 ErrorKind::TaskCancelled => "Task cancelled",
81 ErrorKind::Join => "Join error",
82 ErrorKind::Parse => "Parse error",
83 ErrorKind::BufferSizeLimit => "Buffer size limit exceeded",
84 ErrorKind::ChecksumMismatch => "Checksum mismatch",
85 ErrorKind::MagicMismatch => "Magic mismatch",
86 ErrorKind::OutOfRange => "Out of range",
87 ErrorKind::NoSpace => "No space",
88 ErrorKind::Closed => "Closed",
89 ErrorKind::Recover => "Recover error",
90 ErrorKind::Unsupported => "Unsupported operation",
91 }
92 }
93}
94
95pub struct Error {
164 kind: ErrorKind,
165 message: String,
166
167 context: Vec<(&'static str, String)>,
168
169 source: Option<Arc<anyhow::Error>>,
170 backtrace: Option<Arc<Backtrace>>,
171}
172
173impl Debug for Error {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 if f.alternate() {
177 let mut de = f.debug_struct("Error");
178 de.field("kind", &self.kind);
179 de.field("message", &self.message);
180 de.field("context", &self.context);
181 de.field("source", &self.source);
182 de.field("backtrace", &self.backtrace);
183 return de.finish();
184 }
185
186 write!(f, "{}", self.kind)?;
187 if !self.message.is_empty() {
188 write!(f, " => {}", self.message)?;
189 }
190 writeln!(f)?;
191
192 if !self.context.is_empty() {
193 writeln!(f)?;
194 writeln!(f, "Context:")?;
195 for (k, v) in self.context.iter() {
196 writeln!(f, " {}: {}", k, v)?;
197 }
198 }
199
200 if let Some(source) = &self.source {
201 writeln!(f)?;
202 writeln!(f, "Source:")?;
203 writeln!(f, " {source:#}")?;
204 }
205
206 if let Some(backtrace) = &self.backtrace {
207 writeln!(f)?;
208 writeln!(f, "Backtrace:")?;
209 writeln!(f, "{backtrace}")?;
210 }
211
212 Ok(())
213 }
214}
215
216impl Display for Error {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 write!(f, "{}", self.kind)?;
219
220 if !self.context.is_empty() {
221 write!(f, ", context: {{ ")?;
222 let mut iter = self.context.iter().peekable();
223 while let Some((k, v)) = iter.next() {
224 write!(f, "{}: {}", k, v)?;
225 if iter.peek().is_some() {
226 write!(f, ", ")?;
227 }
228 }
229 write!(f, " }}")?;
230 }
231
232 if !self.message.is_empty() {
233 write!(f, " => {}", self.message)?;
234 }
235
236 if let Some(source) = &self.source {
237 write!(f, ", source: {source}")?;
238 }
239
240 Ok(())
241 }
242}
243
244impl std::error::Error for Error {
245 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
246 self.source.as_ref().map(|v| v.as_ref().as_ref())
247 }
248}
249
250impl Clone for Error {
254 fn clone(&self) -> Self {
255 Self {
256 kind: self.kind,
257 message: self.message.clone(),
258 context: self.context.clone(),
259 source: self.source.clone(),
260 backtrace: self.backtrace.clone(),
261 }
262 }
263}
264
265impl Error {
266 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
278 Self {
279 kind,
280 message: message.into(),
281 context: Vec::new(),
282 source: None,
283 backtrace: Some(Arc::new(Backtrace::capture())),
284 }
285 }
286
287 pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
289 self.context.push((key, value.to_string()));
290 self
291 }
292
293 pub fn with_source(mut self, source: impl Into<anyhow::Error>) -> Self {
299 debug_assert!(self.source.is_none(), "the source error has been set");
300 self.source = Some(Arc::new(source.into()));
301 self
302 }
303
304 pub fn kind(&self) -> ErrorKind {
306 self.kind
307 }
308
309 pub fn message(&self) -> &str {
311 &self.message
312 }
313
314 pub fn context(&self) -> &Vec<(&'static str, String)> {
316 &self.context
317 }
318
319 pub fn backtrace(&self) -> Option<&Backtrace> {
321 self.backtrace.as_deref()
322 }
323
324 pub fn source(&self) -> Option<&anyhow::Error> {
326 self.source.as_deref()
327 }
328
329 pub fn downcast_ref<E>(&self) -> Option<&E>
331 where
332 E: std::error::Error + Send + Sync + 'static,
333 {
334 self.source.as_deref().and_then(|e| e.downcast_ref::<E>())
335 }
336}
337
338pub type Result<T> = std::result::Result<T, Error>;
340
341impl Error {
343 pub fn raw_os_io_error(raw: i32) -> Self {
345 let source = std::io::Error::from_raw_os_error(raw);
346 Self::io_error(source)
347 }
348
349 pub fn io_error(source: std::io::Error) -> Self {
351 match source.kind() {
352 std::io::ErrorKind::WriteZero => Error::new(ErrorKind::BufferSizeLimit, "coding error").with_source(source),
353 _ => Error::new(ErrorKind::Io, "coding error").with_source(source),
354 }
355 }
356
357 #[cfg(feature = "serde")]
359 pub fn bincode_error(source: bincode::Error) -> Self {
360 match *source {
361 bincode::ErrorKind::SizeLimit => Error::new(ErrorKind::BufferSizeLimit, "coding error").with_source(source),
362 bincode::ErrorKind::Io(e) => Self::io_error(e),
363 _ => Error::new(ErrorKind::External, "coding error").with_source(source),
364 }
365 }
366
367 pub fn no_space(capacity: usize, allocated: usize, required: usize) -> Self {
369 Error::new(ErrorKind::NoSpace, "not enough space left")
370 .with_context("capacity", capacity)
371 .with_context("allocated", allocated)
372 .with_context("required", required)
373 }
374}
375
376impl From<std::io::Error> for Error {
377 fn from(e: std::io::Error) -> Self {
378 Self::io_error(e)
379 }
380}
381
382#[cfg(feature = "serde")]
383impl From<bincode::Error> for Error {
384 fn from(e: bincode::Error) -> Self {
385 Self::bincode_error(e)
386 }
387}
388
389#[cfg(test)]
390mod tests {
391
392 use super::*;
393
394 fn is_send_sync_static<T: Send + Sync + 'static>() {}
395
396 #[test]
397 fn test_send_sync_static() {
398 is_send_sync_static::<Error>();
399 }
400
401 #[derive(Debug, Clone, PartialEq, Eq)]
402 struct TestError(String);
403
404 impl std::fmt::Display for TestError {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 write!(f, "TestError: {}", self.0)
407 }
408 }
409
410 impl std::error::Error for TestError {}
411
412 #[test]
413 fn test_error_display() {
414 let io_error = std::io::Error::other("some I/O error");
415 let err = Error::new(ErrorKind::Io, "an I/O error occurred")
416 .with_source(io_error)
417 .with_context("k1", "v1")
418 .with_context("k2", "v2");
419
420 assert_eq!(
421 "I/O error, context: { k1: v1, k2: v2 } => an I/O error occurred, source: some I/O error",
422 err.to_string()
423 );
424 }
425
426 #[test]
427 fn test_error_downcast() {
428 let inner = TestError("Error or not error, that is a question.".to_string());
429 let err = Error::new(ErrorKind::External, "").with_source(inner.clone());
430
431 let downcasted = err.downcast_ref::<TestError>().unwrap();
432 assert_eq!(downcasted, &inner);
433 }
434
435 #[test]
436 fn test_error_format() {
437 let e = Error::new(ErrorKind::External, "external error")
438 .with_context("k1", "v2")
439 .with_context("k2", "v2")
440 .with_source(TestError("test error".into()));
441
442 println!("========== BEGIN DISPLAY FORMAT ==========");
443 println!("{e}");
444 println!("========== END DISPLAY FORMAT ==========");
445
446 println!();
447
448 println!("========== BEGIN DEBUG FORMAT ==========");
449 println!("{e:?}");
450 println!("========== END DEBUG FORMAT ==========");
451
452 println!();
453
454 println!("========== BEGIN DEBUG FORMAT (PRETTY) ==========");
455 println!("{e:#?}");
456 println!("========== END DEBUG FORMAT (PRETTY) ==========");
457 }
458}