1#[cfg(feature = "derive")]
10pub use avx_error_derive::Error as ErrorDerive;
11
12use std::fmt;
13use std::error::Error as StdError;
14
15#[derive(Debug)]
17pub struct Error {
18 kind: ErrorKind,
19 message: String,
20 source: Option<Box<dyn StdError + Send + Sync>>,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ErrorKind {
25 Io,
26 Parse,
27 Network,
28 Database,
29 Auth,
30 NotFound,
31 InvalidInput,
32 InvalidState,
33 Internal,
34 Tls,
35 Serialization,
36 Other,
37}
38
39impl Error {
40 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
41 Self {
42 kind,
43 message: message.into(),
44 source: None,
45 }
46 }
47
48 pub fn with_source<E>(mut self, source: E) -> Self
49 where
50 E: StdError + Send + Sync + 'static,
51 {
52 self.source = Some(Box::new(source));
53 self
54 }
55
56 pub fn kind(&self) -> ErrorKind {
57 self.kind
58 }
59
60 pub fn io(message: impl Into<String>) -> Self {
62 Self::new(ErrorKind::Io, message)
63 }
64
65 pub fn parse(message: impl Into<String>) -> Self {
66 Self::new(ErrorKind::Parse, message)
67 }
68
69 pub fn network(message: impl Into<String>) -> Self {
70 Self::new(ErrorKind::Network, message)
71 }
72
73 pub fn database(message: impl Into<String>) -> Self {
74 Self::new(ErrorKind::Database, message)
75 }
76
77 pub fn auth(message: impl Into<String>) -> Self {
78 Self::new(ErrorKind::Auth, message)
79 }
80
81 pub fn not_found(message: impl Into<String>) -> Self {
82 Self::new(ErrorKind::NotFound, message)
83 }
84
85 pub fn invalid_input(message: impl Into<String>) -> Self {
86 Self::new(ErrorKind::InvalidInput, message)
87 }
88
89 pub fn invalid_state(message: impl Into<String>) -> Self {
90 Self::new(ErrorKind::InvalidState, message)
91 }
92
93 pub fn internal(message: impl Into<String>) -> Self {
94 Self::new(ErrorKind::Internal, message)
95 }
96}
97
98impl fmt::Display for Error {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 write!(f, "{}", self.message)?;
101 if let Some(ref source) = self.source {
102 write!(f, ": {}", source)?;
103 }
104 Ok(())
105 }
106}
107
108impl StdError for Error {
109 fn source(&self) -> Option<&(dyn StdError + 'static)> {
110 self.source
111 .as_ref()
112 .map(|e| e.as_ref() as &(dyn StdError + 'static))
113 }
114}
115
116impl From<std::io::Error> for Error {
118 fn from(err: std::io::Error) -> Self {
119 Error::io(err.to_string()).with_source(err)
120 }
121}
122
123impl From<String> for Error {
125 fn from(msg: String) -> Self {
126 Error::new(ErrorKind::Other, msg)
127 }
128}
129
130impl From<&str> for Error {
131 fn from(msg: &str) -> Self {
132 Error::new(ErrorKind::Other, msg)
133 }
134}
135
136pub type Result<T> = std::result::Result<T, Error>;
138
139#[macro_export]
141macro_rules! bail {
142 ($msg:expr) => {
143 return Err($crate::Error::new($crate::ErrorKind::Other, $msg))
144 };
145 ($kind:expr, $msg:expr) => {
146 return Err($crate::Error::new($kind, $msg))
147 };
148}
149
150#[macro_export]
152macro_rules! ensure {
153 ($cond:expr, $msg:expr) => {
154 if !$cond {
155 $crate::bail!($msg);
156 }
157 };
158}
159
160#[cfg(feature = "context")]
165pub trait Context<T, E> {
166 fn context<C>(self, context: C) -> Result<T>
168 where
169 C: fmt::Display + Send + Sync + 'static;
170
171 fn with_context<C, F>(self, f: F) -> Result<T>
173 where
174 C: fmt::Display + Send + Sync + 'static,
175 F: FnOnce() -> C;
176}
177
178#[cfg(feature = "context")]
179impl<T, E> Context<T, E> for std::result::Result<T, E>
180where
181 E: StdError + Send + Sync + 'static,
182{
183 fn context<C>(self, context: C) -> Result<T>
184 where
185 C: fmt::Display + Send + Sync + 'static,
186 {
187 self.map_err(|error| {
188 let mut err = Error::new(ErrorKind::Other, context.to_string());
189 err.source = Some(Box::new(error));
190 err
191 })
192 }
193
194 fn with_context<C, F>(self, f: F) -> Result<T>
195 where
196 C: fmt::Display + Send + Sync + 'static,
197 F: FnOnce() -> C,
198 {
199 self.map_err(|error| {
200 let context = f();
201 let mut err = Error::new(ErrorKind::Other, context.to_string());
202 err.source = Some(Box::new(error));
203 err
204 })
205 }
206}
207
208#[cfg(feature = "context")]
209impl<T> Context<T, Error> for Option<T> {
210 fn context<C>(self, context: C) -> Result<T>
211 where
212 C: fmt::Display + Send + Sync + 'static,
213 {
214 self.ok_or_else(|| Error::new(ErrorKind::NotFound, context.to_string()))
215 }
216
217 fn with_context<C, F>(self, f: F) -> Result<T>
218 where
219 C: fmt::Display + Send + Sync + 'static,
220 F: FnOnce() -> C,
221 {
222 self.ok_or_else(|| {
223 let context = f();
224 Error::new(ErrorKind::NotFound, context.to_string())
225 })
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn test_error_creation() {
235 let err = Error::not_found("Item not found");
236 assert_eq!(err.kind(), ErrorKind::NotFound);
237 assert_eq!(err.to_string(), "Item not found");
238 }
239
240 #[test]
241 fn test_error_with_source() {
242 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
243 let err = Error::io("Failed to read file").with_source(io_err);
244 assert!(err.source().is_some());
245 }
246}
247
248
249
250
251