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