1use std::fmt;
16use std::io;
17
18pub struct Error {
20 message: String,
21 source: Option<anyhow::Error>,
22 context: Vec<(&'static str, String)>,
23}
24
25impl fmt::Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "{}", self.message)?;
28
29 if !self.context.is_empty() {
30 write!(f, ", context: {{ ")?;
31 write!(
32 f,
33 "{}",
34 self.context
35 .iter()
36 .map(|(k, v)| format!("{k}: {v}"))
37 .collect::<Vec<_>>()
38 .join(", ")
39 )?;
40 write!(f, " }}")?;
41 }
42
43 if let Some(source) = &self.source {
44 write!(f, ", source: {source}")?;
45 }
46
47 Ok(())
48 }
49}
50
51impl fmt::Debug for Error {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 if f.alternate() {
55 let mut de = f.debug_struct("Error");
56 de.field("message", &self.message);
57 de.field("context", &self.context);
58 de.field("source", &self.source);
59 return de.finish();
60 }
61
62 write!(f, "{}", self.message)?;
63 writeln!(f)?;
64
65 if !self.context.is_empty() {
66 writeln!(f)?;
67 writeln!(f, "Context:")?;
68 for (k, v) in self.context.iter() {
69 writeln!(f, " {k}: {v}")?;
70 }
71 }
72 if let Some(source) = &self.source {
73 writeln!(f)?;
74 writeln!(f, "Source:")?;
75 writeln!(f, " {source:#}")?;
76 }
77
78 Ok(())
79 }
80}
81
82impl std::error::Error for Error {
83 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84 self.source.as_ref().map(|v| v.as_ref())
85 }
86}
87
88impl Error {
89 pub fn new(message: impl Into<String>) -> Self {
91 Self {
92 message: message.into(),
93 source: None,
94 context: vec![],
95 }
96 }
97
98 pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
100 self.context.push((key, value.to_string()));
101 self
102 }
103
104 pub fn set_source(mut self, src: impl Into<anyhow::Error>) -> Self {
110 debug_assert!(self.source.is_none(), "the source error has been set");
111
112 self.source = Some(src.into());
113 self
114 }
115
116 pub fn from_io_error(err: io::Error) -> Error {
118 Error::new("failed to perform io").set_source(err)
119 }
120
121 pub fn from_fmt_error(err: fmt::Error) -> Error {
123 Error::new("failed to perform format").set_source(err)
124 }
125}