avx_http/
error.rs

1//! Error types for avx-http
2
3use std::fmt;
4
5/// Result type alias for avx-http operations
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Error types for HTTP operations
9#[derive(Debug)]
10pub enum Error {
11    /// Invalid URL provided
12    InvalidUrl { url: String, reason: String },
13
14    /// Connection failed
15    ConnectionFailed { addr: String, source: std::io::Error },
16
17    /// Request timeout
18    Timeout { duration: std::time::Duration },
19
20    /// Invalid HTTP method
21    InvalidMethod { method: String },
22
23    /// Invalid header
24    InvalidHeader { name: String, value: String },
25
26    /// HTTP status error
27    StatusError { status: u16, body: String },
28
29    /// Body read error
30    BodyReadError { source: std::io::Error },
31
32    /// JSON serialization/deserialization error
33    JsonError { source: String },
34
35    /// Invalid configuration
36    InvalidConfig { message: String },
37
38    /// Authentication error
39    AuthError { message: String },
40
41    /// Internal error
42    Internal { message: String },
43}
44
45impl fmt::Display for Error {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        match self {
48            Error::InvalidUrl { url, reason } => {
49                write!(f, "Invalid URL '{}': {}", url, reason)
50            }
51            Error::ConnectionFailed { addr, source } => {
52                write!(f, "Failed to connect to {}: {}", addr, source)
53            }
54            Error::Timeout { duration } => {
55                write!(f, "Request timed out after {:?}", duration)
56            }
57            Error::InvalidMethod { method } => {
58                write!(f, "Invalid HTTP method: {}", method)
59            }
60            Error::InvalidHeader { name, value } => {
61                write!(f, "Invalid header '{}': {}", name, value)
62            }
63            Error::StatusError { status, body } => {
64                write!(f, "HTTP error {}: {}", status, body)
65            }
66            Error::BodyReadError { source } => {
67                write!(f, "Failed to read response body: {}", source)
68            }
69            Error::JsonError { source } => {
70                write!(f, "JSON error: {}", source)
71            }
72            Error::InvalidConfig { message } => {
73                write!(f, "Invalid configuration: {}", message)
74            }
75            Error::AuthError { message } => {
76                write!(f, "Authentication error: {}", message)
77            }
78            Error::Internal { message } => {
79                write!(f, "Internal error: {}", message)
80            }
81        }
82    }
83}
84
85impl std::error::Error for Error {
86    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
87        match self {
88            Error::ConnectionFailed { source, .. } => Some(source),
89            Error::BodyReadError { source } => Some(source),
90            _ => None,
91        }
92    }
93}
94
95impl From<std::io::Error> for Error {
96    fn from(err: std::io::Error) -> Self {
97        Error::BodyReadError { source: err }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_error_display() {
107        let err = Error::InvalidUrl {
108            url: "not-a-url".to_string(),
109            reason: "missing scheme".to_string(),
110        };
111        assert!(err.to_string().contains("Invalid URL"));
112        assert!(err.to_string().contains("not-a-url"));
113    }
114
115    #[test]
116    fn test_error_from_io() {
117        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
118        let err: Error = io_err.into();
119        assert!(matches!(err, Error::BodyReadError { .. }));
120    }
121
122    #[test]
123    fn test_timeout_error() {
124        let err = Error::Timeout {
125            duration: std::time::Duration::from_secs(30),
126        };
127        assert!(err.to_string().contains("timed out"));
128        assert!(err.to_string().contains("30s"));
129    }
130}