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