1mod chain;
2
3pub use chain::Chain;
4use std::fmt;
5
6pub trait ErrorContext<CO> {
8 fn push(&mut self, context: CO);
9}
10
11pub struct ErrorReporter<E, C>
19where
20 E: std::error::Error + Send + Sync + 'static,
21{
22 pub error: E,
23 pub context: C,
24}
25
26impl<E, C> ErrorReporter<E, C>
27where
28 E: std::error::Error + Send + Sync + 'static,
29{
30 pub fn chain(&self) -> Chain {
32 Chain::new(&self.error)
33 }
34}
35
36impl<C, E> From<E> for ErrorReporter<E, C>
37where
38 C: Default,
39 E: std::error::Error + Send + Sync + 'static,
40{
41 fn from(error: E) -> Self {
42 Self {
43 context: Default::default(),
44 error,
45 }
46 }
47}
48
49pub trait IntoErrorReporter<E, C, CO>
64where
65 E: std::error::Error + Send + Sync + 'static,
66{
67 fn ext_context(self, context: CO) -> ErrorReporter<E, C>;
68}
69
70impl<E, C, CO> IntoErrorReporter<E, C, CO> for E
71where
72 C: Default + ErrorContext<CO>,
73 E: std::error::Error + Send + Sync + 'static,
74{
75 fn ext_context(self, context: CO) -> ErrorReporter<E, C> {
76 let mut error = ErrorReporter::<E, C>::from(self);
77 error.context.push(context);
78 error
79 }
80}
81
82impl<C, CO, E> IntoErrorReporter<E, C, CO> for ErrorReporter<E, C>
83where
84 E: std::error::Error + Send + Sync + 'static,
85 C: ErrorContext<CO>,
86{
87 fn ext_context(mut self, context: CO) -> ErrorReporter<E, C> {
88 self.context.push(context);
89 self
90 }
91}
92
93pub struct Indented<'a, D> {
96 inner: &'a mut D,
97 ind: Option<usize>,
98 started: bool,
99}
100
101impl<'a, D> Indented<'a, D> {
102 pub fn numbered(inner: &'a mut D, ind: usize) -> Self {
105 Self {
106 inner,
107 ind: Some(ind),
108 started: false,
109 }
110 }
111}
112
113impl<T> fmt::Write for Indented<'_, T>
114where
115 T: fmt::Write,
116{
117 fn write_str(&mut self, s: &str) -> fmt::Result {
118 for (ind, mut line) in s.split('\n').enumerate() {
119 if !self.started {
120 line = line.trim_start();
122 if line.is_empty() {
124 continue;
125 }
126
127 self.started = true;
128 match self.ind {
129 Some(ind) => self.inner.write_fmt(format_args!("{: >5}: ", ind))?,
130 None => self.inner.write_fmt(format_args!(" "))?,
131 }
132 } else if ind > 0 {
133 self.inner.write_char('\n')?;
134 if self.ind.is_some() {
135 self.inner.write_fmt(format_args!(" "))?;
136 } else {
137 self.inner.write_fmt(format_args!(" "))?;
138 }
139 }
140
141 self.inner.write_fmt(format_args!("{}", line))?;
142 }
143
144 Ok(())
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use std::fmt::Write as _;
152
153 #[test]
154 fn one_digit() {
155 let input = "verify\nthis";
156 let expected = " 2: verify\n this";
157 let mut output = String::new();
158
159 Indented {
160 inner: &mut output,
161 ind: Some(2),
162 started: false,
163 }
164 .write_str(input)
165 .unwrap();
166
167 assert_eq!(expected, output);
168 }
169
170 #[test]
171 fn two_digits() {
172 let input = "verify\nthis";
173 let expected = " 12: verify\n this";
174 let mut output = String::new();
175
176 Indented {
177 inner: &mut output,
178 ind: Some(12),
179 started: false,
180 }
181 .write_str(input)
182 .unwrap();
183
184 assert_eq!(expected, output);
185 }
186
187 #[test]
188 fn no_digits() {
189 let input = "verify\nthis";
190 let expected = " verify\n this";
191 let mut output = String::new();
192
193 Indented {
194 inner: &mut output,
195 ind: None,
196 started: false,
197 }
198 .write_str(input)
199 .unwrap();
200
201 assert_eq!(expected, output);
202 }
203}