1#![doc = include_str!("../README.md")]
2#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7
8#[cfg(feature = "alloc")]
9use alloc::boxed::Box;
10use core::fmt;
11
12#[cfg(feature = "std")]
13pub trait Source: core::error::Error + Sync + Send + 'static {}
14
15#[cfg(feature = "std")]
16impl<T> Source for T where T: core::error::Error + Sync + Send + 'static {}
17
18#[cfg(not(feature = "std"))]
19pub trait Source: fmt::Display + fmt::Debug + Send + Sync + 'static {}
20
21#[cfg(not(feature = "std"))]
22impl<T> Source for T where T: fmt::Display + fmt::Debug + Send + Sync + 'static {}
23
24#[derive(Debug)]
25pub struct Error<Kind> {
26 pub context: &'static str,
27 pub kind: Kind,
28 #[cfg(feature = "std")]
29 source: Option<Box<dyn core::error::Error + Sync + Send>>,
30 #[cfg(all(not(feature = "std"), feature = "alloc"))]
31 source: Option<Box<dyn Source>>,
32}
33
34impl<Kind> Error<Kind> {
35 #[cold]
36 #[must_use]
37 pub fn new(context: &'static str, kind: Kind) -> Self {
38 Self {
39 context,
40 kind,
41 #[cfg(feature = "alloc")]
42 source: None,
43 }
44 }
45
46 #[cold]
47 #[must_use]
48 pub fn with_source<E>(self, source: E) -> Self
49 where
50 E: Source,
51 {
52 #[cfg(feature = "alloc")]
53 {
54 let mut this = self;
55 this.source = Some(Box::new(source));
56 this
57 }
58
59 #[cfg(not(feature = "alloc"))]
61 {
62 let _ = source;
63 self
64 }
65 }
66
67 pub fn into_other_kind<OtherKind>(self) -> Error<OtherKind>
68 where
69 Kind: Into<OtherKind>,
70 {
71 Error {
72 context: self.context,
73 kind: self.kind.into(),
74 #[cfg(any(feature = "std", feature = "alloc"))]
75 source: self.source,
76 }
77 }
78
79 pub fn kind(&self) -> &Kind {
80 &self.kind
81 }
82
83 pub fn report(&self) -> ErrorReport<'_, Kind> {
84 ErrorReport(self)
85 }
86}
87
88impl<Kind> fmt::Display for Error<Kind>
89where
90 Kind: fmt::Display,
91{
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "[{}] {}", self.context, self.kind)
94 }
95}
96
97#[cfg(feature = "std")]
98impl<Kind> core::error::Error for Error<Kind>
99where
100 Kind: core::error::Error,
101{
102 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
103 if let Some(source) = self.kind.source() {
104 Some(source)
105 } else {
106 if let Some(e) = &self.source {
108 Some(e.as_ref())
109 } else {
110 None
111 }
112 }
113 }
114}
115
116#[cfg(feature = "std")]
117impl<Kind> From<Error<Kind>> for std::io::Error
118where
119 Kind: core::error::Error + Send + Sync + 'static,
120{
121 fn from(error: Error<Kind>) -> Self {
122 Self::other(error)
123 }
124}
125
126pub struct ErrorReport<'a, Kind>(&'a Error<Kind>);
127
128#[cfg(feature = "std")]
129impl<Kind> fmt::Display for ErrorReport<'_, Kind>
130where
131 Kind: core::error::Error,
132{
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 use core::error::Error;
135
136 write!(f, "{}", self.0)?;
137
138 let mut next_source = self.0.source();
139
140 while let Some(e) = next_source {
141 write!(f, ", caused by: {e}")?;
142 next_source = e.source();
143 }
144
145 Ok(())
146 }
147}
148
149#[cfg(not(feature = "std"))]
150impl<E> fmt::Display for ErrorReport<'_, E>
151where
152 E: fmt::Display,
153{
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "{}", self.0)?;
156
157 #[cfg(feature = "alloc")]
158 if let Some(source) = &self.0.source {
159 write!(f, ", caused by: {source}")?;
160 }
161
162 Ok(())
163 }
164}