1use std::{
7 borrow::{Borrow, Cow},
8 convert::Infallible,
9 fmt,
10};
11
12pub type Result<T> = std::result::Result<T, Error>;
14
15#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum ErrorKind {
18 InvalidData,
20
21 Io,
23
24 Other,
26}
27
28impl fmt::Display for ErrorKind {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
32 ErrorKind::InvalidData => f.write_str("InvalidData"),
33 ErrorKind::Io => f.write_str("Io"),
34 ErrorKind::Other => f.write_str("Other"),
35 }
36 }
37}
38
39#[derive(Debug)]
41pub struct Error {
42 repr: Repr,
43}
44
45impl Error {
46 pub fn new<E>(kind: ErrorKind, error: E) -> Self
48 where
49 E: Into<Box<dyn std::error::Error + Send + Sync>>,
50 {
51 Self {
52 repr: Repr::Custom(Custom {
53 kind,
54 error: error.into(),
55 }),
56 }
57 }
58
59 pub fn kind(&self) -> &ErrorKind {
61 match &self.repr {
62 Repr::Simple(kind)
63 | Repr::SimpleMessage(kind, ..)
64 | Repr::Custom(Custom { kind, .. })
65 | Repr::CustomMessage(Custom { kind, .. }, ..) => kind,
66 }
67 }
68
69 pub fn message(&self) -> Option<&str> {
71 match &self.repr {
72 Repr::SimpleMessage(_, message) | Repr::CustomMessage(_, message) => {
73 Some(message.borrow())
74 }
75 _ => None,
76 }
77 }
78
79 #[must_use]
81 pub fn with_message<C>(kind: ErrorKind, message: C) -> Self
82 where
83 C: Into<Cow<'static, str>>,
84 {
85 Self {
86 repr: Repr::SimpleMessage(kind, message.into()),
87 }
88 }
89
90 #[must_use]
92 pub fn with_message_fn<F, C>(kind: ErrorKind, message: F) -> Self
93 where
94 Self: Sized,
95 F: FnOnce() -> C,
96 C: Into<Cow<'static, str>>,
97 {
98 Self::with_message(kind, message())
99 }
100
101 #[must_use]
103 pub fn with_error<E, C>(kind: ErrorKind, error: E, message: C) -> Self
104 where
105 E: Into<Box<dyn std::error::Error + Send + Sync>>,
106 C: Into<Cow<'static, str>>,
107 {
108 Self {
109 repr: Repr::CustomMessage(
110 Custom {
111 kind,
112 error: error.into(),
113 },
114 message.into(),
115 ),
116 }
117 }
118
119 #[must_use]
120 pub fn with_error_fn<E, F, C>(kind: ErrorKind, error: E, message: F) -> Self
122 where
123 E: Into<Box<dyn std::error::Error + Send + Sync>>,
124 F: FnOnce() -> C,
125 C: Into<Cow<'static, str>>,
126 {
127 Self::with_error(kind, error, message())
128 }
129}
130
131impl fmt::Display for Error {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 match &self.repr {
134 Repr::Simple(kind) => write!(f, "{kind}"),
135 Repr::SimpleMessage(_, message) => write!(f, "{message}"),
136 Repr::Custom(Custom { error, .. }) => write!(f, "{error}"),
137 Repr::CustomMessage(_, message) => write!(f, "{message}"),
138 }
139 }
140}
141
142impl std::error::Error for Error {
143 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
144 match &self.repr {
145 Repr::Custom(Custom { error, .. }) | Repr::CustomMessage(Custom { error, .. }, ..) => {
146 Some(&**error)
147 }
148 _ => None,
149 }
150 }
151}
152
153impl From<ErrorKind> for Error {
154 fn from(kind: ErrorKind) -> Self {
155 Self {
156 repr: Repr::Simple(kind),
157 }
158 }
159}
160
161impl From<String> for Error {
162 fn from(value: String) -> Self {
163 Self::with_message(ErrorKind::Other, value)
164 }
165}
166
167impl From<Infallible> for Error {
168 fn from(_: Infallible) -> Self {
169 panic!("inconceivable")
170 }
171}
172
173impl From<std::io::Error> for Error {
174 fn from(error: std::io::Error) -> Self {
175 Self::new(ErrorKind::Io, error)
176 }
177}
178
179impl From<std::num::ParseIntError> for Error {
180 fn from(error: std::num::ParseIntError) -> Self {
181 Self::new(ErrorKind::InvalidData, error)
182 }
183}
184
185impl From<std::env::VarError> for Error {
186 fn from(error: std::env::VarError) -> Self {
187 Self::new(ErrorKind::Other, error)
188 }
189}
190
191impl From<azure_core::Error> for Error {
192 fn from(error: azure_core::Error) -> Self {
193 Self::new(ErrorKind::Other, error)
194 }
195}
196
197impl From<dotenvy::Error> for Error {
198 fn from(error: dotenvy::Error) -> Self {
199 Self::new(ErrorKind::Other, error)
200 }
201}
202
203impl From<openssl::error::ErrorStack> for Error {
204 fn from(error: openssl::error::ErrorStack) -> Self {
205 Self::new(ErrorKind::Other, error)
206 }
207}
208
209impl From<serde_json::Error> for Error {
210 fn from(error: serde_json::Error) -> Self {
211 Self::new(ErrorKind::Io, error)
212 }
213}
214
215impl From<url::ParseError> for Error {
216 fn from(error: url::ParseError) -> Self {
217 Self::new(ErrorKind::InvalidData, error)
218 }
219}
220
221#[derive(Debug)]
222enum Repr {
223 Simple(ErrorKind),
224 SimpleMessage(ErrorKind, Cow<'static, str>),
225 Custom(Custom),
226 CustomMessage(Custom, Cow<'static, str>),
227}
228
229#[derive(Debug)]
230struct Custom {
231 kind: ErrorKind,
232 error: Box<dyn std::error::Error + Send + Sync>,
233}
234
235pub trait ResultExt<T>: private::Sealed {
237 fn with_kind(self, kind: ErrorKind) -> Result<T>;
239
240 fn with_context<C>(self, kind: ErrorKind, message: C) -> Result<T>
242 where
243 Self: Sized,
244 C: Into<Cow<'static, str>>;
245
246 fn with_context_fn<F, C>(self, kind: ErrorKind, f: F) -> Result<T>
248 where
249 Self: Sized,
250 F: FnOnce() -> C,
251 C: Into<Cow<'static, str>>;
252}
253
254impl<T, E> ResultExt<T> for std::result::Result<T, E>
255where
256 E: std::error::Error + Send + Sync + 'static,
257{
258 fn with_kind(self, kind: ErrorKind) -> Result<T> {
259 self.map_err(|err| Error::new(kind, err))
260 }
261
262 fn with_context<C>(self, kind: ErrorKind, message: C) -> Result<T>
263 where
264 Self: Sized,
265 C: Into<Cow<'static, str>>,
266 {
267 self.map_err(|err| Error::with_error(kind, Box::new(err), message))
268 }
269
270 fn with_context_fn<F, C>(self, kind: ErrorKind, f: F) -> Result<T>
271 where
272 Self: Sized,
273 F: FnOnce() -> C,
274 C: Into<Cow<'static, str>>,
275 {
276 self.with_context(kind, f())
277 }
278}
279
280mod private {
281 pub trait Sealed {}
282
283 impl<T, E> Sealed for std::result::Result<T, E> where E: std::error::Error + Send + Sync + 'static {}
284}