1use std::{error::Error, fmt, io, iter};
2
3pub fn with(context: impl fmt::Display) -> impl FnOnce(io::Error) -> io::Error {
16 |e| self::context(e, context)
17}
18
19pub fn context(e: io::Error, context: impl fmt::Display) -> io::Error {
25 let kind = e.kind();
26 let stringified = e.to_string();
27 let source = match (
28 e.raw_os_error(),
29 stringified == kind.to_string(),
30 e.into_inner(),
31 ) {
32 (_, _, Some(source)) => Some(source),
34 (Some(code), _, None) => Some(Box::new(io::Error::from_raw_os_error(code)) as _),
36 (None, true, None) => None,
38 (None, false, None) => Some(Box::new(SimpleMessage(stringified)) as _),
40 };
41 io::Error::new(
42 kind,
43 Context {
44 context: context.to_string(),
45 source,
46 },
47 )
48}
49
50#[derive(Debug)]
51struct SimpleMessage(String);
52impl fmt::Display for SimpleMessage {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 self.0.fmt(f)
55 }
56}
57impl Error for SimpleMessage {}
58
59#[derive(Debug)]
60struct Context {
61 context: String,
62 source: Option<Box<dyn Error + Send + Sync + 'static>>,
63}
64impl Error for Context {
65 fn source(&self) -> Option<&(dyn Error + 'static)> {
66 match &self.source {
67 Some(it) => Some(it.as_ref()),
68 None => None,
69 }
70 }
71}
72impl fmt::Display for Context {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 self.context.fmt(f)?;
75 if f.alternate() {
76 for parent in Chain::new(self.source()) {
77 write!(f, ": {}", parent)?
78 }
79 }
80 Ok(())
81 }
82}
83
84#[derive(Debug)]
86struct Chain<'a> {
87 #[allow(clippy::type_complexity)]
88 inner: iter::Successors<&'a dyn Error, fn(&&'a dyn Error) -> Option<&'a dyn Error>>,
89}
90
91impl<'a> Chain<'a> {
92 fn new(root: Option<&'a dyn Error>) -> Self {
93 Self {
94 inner: iter::successors(root, |e| (*e).source()),
95 }
96 }
97}
98
99impl<'a> Iterator for Chain<'a> {
100 type Item = &'a dyn Error;
101
102 fn next(&mut self) -> Option<Self::Item> {
103 self.inner.next()
104 }
105}