avx_error/
lib.rs

1//! avx Error - AVL Platform error handling
2//! Replacement for anyhow/thiserror - 100% Rust std
3//!
4//! # Features
5//! - `derive`: Enable #[derive(Error)] macro
6//! - `context`: Enable Context trait for anyhow-style error handling
7//! - `full`: Enable all features
8
9#[cfg(feature = "derive")]
10pub use avx_error_derive::Error as ErrorDerive;
11
12use std::fmt;
13use std::error::Error as StdError;
14
15/// Generic error type for AVL Platform
16#[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    // Convenience constructors
61    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
116// Convert from std::io::Error
117impl 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
123// Convert from String
124impl 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
136/// Result type using our Error
137pub type Result<T> = std::result::Result<T, Error>;
138
139/// Macro para criar erros facilmente (similar ao anyhow!)
140#[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 para garantir condições (similar ao ensure!)
151#[macro_export]
152macro_rules! ensure {
153    ($cond:expr, $msg:expr) => {
154        if !$cond {
155            $crate::bail!($msg);
156        }
157    };
158}
159
160// ============================================================================
161// Context trait (feature = "context") - anyhow-style error handling
162// ============================================================================
163
164#[cfg(feature = "context")]
165pub trait Context<T, E> {
166    /// Adiciona contexto ao erro
167    fn context<C>(self, context: C) -> Result<T>
168    where
169        C: fmt::Display + Send + Sync + 'static;
170
171    /// Adiciona contexto lazy ao erro
172    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