fetchttp/
error.rs

1//! Error types for the fetch library.
2//!
3//! This module provides comprehensive error handling following the WHATWG Fetch
4//! specification. All errors implement standard Rust error traits and provide
5//! detailed error information.
6
7use std::fmt;
8
9/// A type error indicating invalid arguments or operations.
10///
11/// This error type corresponds to JavaScript's `TypeError` and is used for
12/// validation failures, invalid arguments, and other type-related errors.
13///
14/// # Examples
15///
16/// ```rust
17/// use fetchttp::TypeError;
18///
19/// let error = TypeError::new("Invalid header name");
20/// println!("Error: {}", error);
21/// ```
22#[derive(Debug, Clone)]
23pub struct TypeError {
24    message: String,
25}
26
27impl TypeError {
28    /// Create a new TypeError with the given message.
29    pub fn new(message: &str) -> Self {
30        Self {
31            message: message.to_string(),
32        }
33    }
34
35    /// Get the error message.
36    pub fn message(&self) -> &str {
37        &self.message
38    }
39}
40
41impl fmt::Display for TypeError {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        write!(f, "TypeError: {}", self.message)
44    }
45}
46
47impl std::error::Error for TypeError {}
48
49/// A network error indicating connection or protocol failures.
50///
51/// This error type represents network-level failures such as DNS resolution
52/// errors, connection timeouts, TLS errors, and other transport-related issues.
53///
54/// # Examples
55///
56/// ```rust
57/// use fetchttp::NetworkError;
58///
59/// let error = NetworkError::new("Connection refused");
60/// println!("Error: {}", error);
61/// ```
62#[derive(Debug, Clone)]
63pub struct NetworkError {
64    message: String,
65}
66
67impl NetworkError {
68    /// Create a new NetworkError with the given message.
69    pub fn new(message: &str) -> Self {
70        Self {
71            message: message.to_string(),
72        }
73    }
74
75    /// Get the error message.
76    pub fn message(&self) -> &str {
77        &self.message
78    }
79}
80
81impl fmt::Display for NetworkError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "NetworkError: {}", self.message)
84    }
85}
86
87impl std::error::Error for NetworkError {}
88
89/// An abort error indicating the operation was cancelled.
90///
91/// This error type is used when a request is cancelled via an [`AbortSignal`].
92/// It corresponds to JavaScript's `AbortError`.
93///
94/// [`AbortSignal`]: crate::AbortSignal
95///
96/// # Examples
97///
98/// ```rust
99/// use fetchttp::AbortError;
100///
101/// let error = AbortError::new("The operation was aborted");
102/// println!("Error: {}", error);
103/// ```
104#[derive(Debug, Clone)]
105pub struct AbortError {
106    message: String,
107}
108
109impl AbortError {
110    /// Create a new AbortError with the given message.
111    pub fn new(message: &str) -> Self {
112        Self {
113            message: message.to_string(),
114        }
115    }
116
117    /// Get the error message.
118    pub fn message(&self) -> &str {
119        &self.message
120    }
121}
122
123impl fmt::Display for AbortError {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        write!(f, "AbortError: {}", self.message)
126    }
127}
128
129impl std::error::Error for AbortError {}
130
131/// The main error type for fetch operations.
132///
133/// This enum encompasses all possible errors that can occur during fetch operations.
134/// It provides a unified error type while preserving the specific error information.
135///
136/// # Variants
137///
138/// * [`Type`] - Type-related errors (invalid arguments, validation failures)
139/// * [`Network`] - Network-related errors (connection, DNS, TLS failures)
140/// * [`Abort`] - Request was aborted via abort signal
141///
142/// [`Type`]: FetchError::Type
143/// [`Network`]: FetchError::Network
144/// [`Abort`]: FetchError::Abort
145///
146/// # Examples
147///
148/// ```rust
149/// use fetchttp::*;
150///
151/// async fn handle_fetch_error() {
152///     match fetch("https://example.com", None).await {
153///         Ok(response) => {
154///             println!("Success: {}", response.status());
155///         }
156///         Err(FetchError::Type(e)) => {
157///             eprintln!("Type error: {}", e);
158///         }
159///         Err(FetchError::Network(e)) => {
160///             eprintln!("Network error: {}", e);
161///         }
162///         Err(FetchError::Abort(e)) => {
163///             eprintln!("Request aborted: {}", e);
164///         }
165///     }
166/// }
167/// ```
168#[derive(Debug)]
169pub enum FetchError {
170    /// Type-related error (invalid arguments, validation failures)
171    Type(TypeError),
172    /// Network-related error (connection, DNS, TLS failures)
173    Network(NetworkError),
174    /// Request was aborted
175    Abort(AbortError),
176}
177
178impl fmt::Display for FetchError {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        match self {
181            Self::Type(e) => write!(f, "{}", e),
182            Self::Network(e) => write!(f, "{}", e),
183            Self::Abort(e) => write!(f, "{}", e),
184        }
185    }
186}
187
188impl std::error::Error for FetchError {
189    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
190        match self {
191            Self::Type(e) => Some(e),
192            Self::Network(e) => Some(e),
193            Self::Abort(e) => Some(e),
194        }
195    }
196}
197
198// Conversion implementations for easy error handling
199impl From<TypeError> for FetchError {
200    fn from(err: TypeError) -> Self {
201        Self::Type(err)
202    }
203}
204
205impl From<NetworkError> for FetchError {
206    fn from(err: NetworkError) -> Self {
207        Self::Network(err)
208    }
209}
210
211impl From<AbortError> for FetchError {
212    fn from(err: AbortError) -> Self {
213        Self::Abort(err)
214    }
215}
216
217// Conversions from external error types
218impl From<hyper::Error> for FetchError {
219    fn from(err: hyper::Error) -> Self {
220        Self::Network(NetworkError::new(&err.to_string()))
221    }
222}
223
224impl From<hyper_util::client::legacy::Error> for FetchError {
225    fn from(err: hyper_util::client::legacy::Error) -> Self {
226        Self::Network(NetworkError::new(&err.to_string()))
227    }
228}
229
230impl From<http::Error> for FetchError {
231    fn from(_: http::Error) -> Self {
232        Self::Network(NetworkError::new("HTTP error"))
233    }
234}
235
236impl From<url::ParseError> for FetchError {
237    fn from(_: url::ParseError) -> Self {
238        Self::Type(TypeError::new("Invalid URL"))
239    }
240}
241
242impl From<serde_json::Error> for FetchError {
243    fn from(_: serde_json::Error) -> Self {
244        Self::Type(TypeError::new("JSON parse error"))
245    }
246}
247
248/// Convenient Result type alias for fetch operations.
249///
250/// This type alias provides a shorter way to write `Result<T, FetchError>`.
251///
252/// # Examples
253///
254/// ```rust
255/// use fetchttp::{Result, Response};
256///
257/// async fn fetch_data() -> Result<Response> {
258///     fetchttp::fetch("https://api.example.com/data", None).await
259/// }
260/// ```
261pub type Result<T> = std::result::Result<T, FetchError>;
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    #[test]
268    fn test_error_display() {
269        let type_error = TypeError::new("test message");
270        assert_eq!(format!("{}", type_error), "TypeError: test message");
271
272        let network_error = NetworkError::new("connection failed");
273        assert_eq!(
274            format!("{}", network_error),
275            "NetworkError: connection failed"
276        );
277
278        let abort_error = AbortError::new("aborted");
279        assert_eq!(format!("{}", abort_error), "AbortError: aborted");
280    }
281
282    #[test]
283    fn test_fetch_error_conversions() {
284        let type_error = TypeError::new("test");
285        let fetch_error: FetchError = type_error.into();
286        assert!(matches!(fetch_error, FetchError::Type(_)));
287
288        let network_error = NetworkError::new("test");
289        let fetch_error: FetchError = network_error.into();
290        assert!(matches!(fetch_error, FetchError::Network(_)));
291
292        let abort_error = AbortError::new("test");
293        let fetch_error: FetchError = abort_error.into();
294        assert!(matches!(fetch_error, FetchError::Abort(_)));
295    }
296
297    #[test]
298    fn test_error_messages() {
299        let type_error = TypeError::new("invalid input");
300        assert_eq!(type_error.message(), "invalid input");
301
302        let network_error = NetworkError::new("timeout");
303        assert_eq!(network_error.message(), "timeout");
304
305        let abort_error = AbortError::new("cancelled");
306        assert_eq!(abort_error.message(), "cancelled");
307    }
308}