Skip to main content

http_cache/
error.rs

1use std::fmt;
2
3/// Generic error type for the `HttpCache` middleware.
4pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
5
6/// A `Result` typedef to use with the [`BoxError`] type
7pub type Result<T> = std::result::Result<T, BoxError>;
8
9/// Error type for unknown http versions
10#[derive(Debug, Default, Copy, Clone)]
11pub struct BadVersion;
12
13impl fmt::Display for BadVersion {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        f.pad("Unknown HTTP version")
16    }
17}
18
19impl std::error::Error for BadVersion {}
20
21/// Error type for bad header values
22#[derive(Debug, Default, Copy, Clone)]
23pub struct BadHeader;
24
25impl fmt::Display for BadHeader {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        f.pad("Error parsing header value")
28    }
29}
30
31impl std::error::Error for BadHeader {}
32
33/// Error type for request parsing failure
34#[derive(Debug, Default, Copy, Clone)]
35pub struct BadRequest;
36
37impl fmt::Display for BadRequest {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.pad("Request object is not cloneable. Are you passing a streaming body?")
40    }
41}
42
43impl std::error::Error for BadRequest {}
44
45/// Unified error type for HTTP cache operations that works across all client libraries.
46///
47/// This enum consolidates error handling patterns from all http-cache client crates
48/// (reqwest, surf, tower, ureq) while providing a clean, extensible interface.
49///
50/// # Examples
51///
52/// ```rust
53/// use http_cache::{HttpCacheError, BadRequest};
54///
55/// // Cache operation errors
56/// let cache_err = HttpCacheError::cache("Failed to read cache entry");
57///
58/// // Request parsing errors
59/// let request_err = HttpCacheError::from(BadRequest);
60///
61/// // HTTP processing errors
62/// let http_err = HttpCacheError::http("Invalid header format");
63///
64/// // Body processing errors  
65/// let body_err = HttpCacheError::body("Failed to collect request body");
66/// ```
67#[derive(Debug)]
68pub enum HttpCacheError {
69    /// HTTP client error (reqwest, surf, etc.)
70    Client(BoxError),
71    /// HTTP cache operation failed
72    Cache(String),
73    /// Request parsing failed (e.g., non-cloneable request)
74    BadRequest(BadRequest),
75    /// HTTP processing error (header parsing, version handling, etc.)
76    Http(BoxError),
77    /// Body processing error (collection, streaming, etc.)
78    Body(BoxError),
79    /// Streaming operation error (with detailed error kind)
80    Streaming(StreamingError),
81    /// Other generic error
82    Other(BoxError),
83}
84
85impl HttpCacheError {
86    /// Create a cache operation error
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// use http_cache::HttpCacheError;
92    ///
93    /// let err = HttpCacheError::cache("Cache entry not found");
94    /// ```
95    pub fn cache<S: Into<String>>(message: S) -> Self {
96        Self::Cache(message.into())
97    }
98
99    /// Create an HTTP processing error
100    ///
101    /// # Examples
102    ///
103    /// ```rust
104    /// use http_cache::HttpCacheError;
105    ///
106    /// let err = HttpCacheError::http("Invalid header format");
107    /// ```
108    pub fn http<E: Into<BoxError>>(error: E) -> Self {
109        Self::Http(error.into())
110    }
111
112    /// Create a body processing error
113    ///
114    /// # Examples
115    ///
116    /// ```rust
117    /// use http_cache::HttpCacheError;
118    ///
119    /// let err = HttpCacheError::body("Failed to collect request body");
120    /// ```
121    pub fn body<E: Into<BoxError>>(error: E) -> Self {
122        Self::Body(error.into())
123    }
124
125    /// Create a client error
126    ///
127    /// # Examples
128    ///
129    /// ```rust
130    /// use http_cache::HttpCacheError;
131    ///
132    /// let err = HttpCacheError::client("Network timeout");
133    /// ```
134    pub fn client<E: Into<BoxError>>(error: E) -> Self {
135        Self::Client(error.into())
136    }
137
138    /// Create a generic error
139    ///
140    /// # Examples
141    ///
142    /// ```rust
143    /// use http_cache::HttpCacheError;
144    ///
145    /// let err = HttpCacheError::other("Unexpected error occurred");
146    /// ```
147    pub fn other<E: Into<BoxError>>(error: E) -> Self {
148        Self::Other(error.into())
149    }
150
151    /// Returns true if this error is related to cache operations
152    pub fn is_cache_error(&self) -> bool {
153        matches!(self, Self::Cache(_))
154    }
155
156    /// Returns true if this error is related to client operations
157    pub fn is_client_error(&self) -> bool {
158        matches!(self, Self::Client(_))
159    }
160
161    /// Returns true if this error is related to streaming operations
162    pub fn is_streaming_error(&self) -> bool {
163        matches!(self, Self::Streaming(_))
164    }
165
166    /// Returns true if this error is a bad request
167    pub fn is_bad_request(&self) -> bool {
168        matches!(self, Self::BadRequest(_))
169    }
170}
171
172impl fmt::Display for HttpCacheError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            Self::Client(e) => write!(f, "HTTP client error: {e}"),
176            Self::Cache(msg) => write!(f, "Cache error: {msg}"),
177            Self::BadRequest(e) => write!(f, "Request error: {e}"),
178            Self::Http(e) => write!(f, "HTTP error: {e}"),
179            Self::Body(e) => write!(f, "Body processing error: {e}"),
180            Self::Streaming(e) => write!(f, "Streaming error: {e}"),
181            Self::Other(e) => write!(f, "Other error: {e}"),
182        }
183    }
184}
185
186impl std::error::Error for HttpCacheError {
187    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
188        match self {
189            Self::Client(e) => Some(e.as_ref()),
190            Self::Cache(_) => None,
191            Self::BadRequest(e) => Some(e),
192            Self::Http(e) => Some(e.as_ref()),
193            Self::Body(e) => Some(e.as_ref()),
194            Self::Streaming(e) => Some(e),
195            Self::Other(e) => Some(e.as_ref()),
196        }
197    }
198}
199
200// Comprehensive From implementations for common error types
201
202impl From<BadRequest> for HttpCacheError {
203    fn from(error: BadRequest) -> Self {
204        Self::BadRequest(error)
205    }
206}
207
208impl From<BadHeader> for HttpCacheError {
209    fn from(error: BadHeader) -> Self {
210        Self::Http(Box::new(error))
211    }
212}
213
214impl From<BadVersion> for HttpCacheError {
215    fn from(error: BadVersion) -> Self {
216        Self::Http(Box::new(error))
217    }
218}
219
220impl From<StreamingError> for HttpCacheError {
221    fn from(error: StreamingError) -> Self {
222        Self::Streaming(error)
223    }
224}
225
226impl From<BoxError> for HttpCacheError {
227    fn from(error: BoxError) -> Self {
228        Self::Other(error)
229    }
230}
231
232impl From<std::io::Error> for HttpCacheError {
233    fn from(error: std::io::Error) -> Self {
234        Self::Other(Box::new(error))
235    }
236}
237
238impl From<http::Error> for HttpCacheError {
239    fn from(error: http::Error) -> Self {
240        Self::Http(Box::new(error))
241    }
242}
243
244impl From<http::header::InvalidHeaderValue> for HttpCacheError {
245    fn from(error: http::header::InvalidHeaderValue) -> Self {
246        Self::Http(Box::new(error))
247    }
248}
249
250impl From<http::header::InvalidHeaderName> for HttpCacheError {
251    fn from(error: http::header::InvalidHeaderName) -> Self {
252        Self::Http(Box::new(error))
253    }
254}
255
256impl From<http::uri::InvalidUri> for HttpCacheError {
257    fn from(error: http::uri::InvalidUri) -> Self {
258        Self::Http(Box::new(error))
259    }
260}
261
262impl From<http::method::InvalidMethod> for HttpCacheError {
263    fn from(error: http::method::InvalidMethod) -> Self {
264        Self::Http(Box::new(error))
265    }
266}
267
268impl From<http::status::InvalidStatusCode> for HttpCacheError {
269    fn from(error: http::status::InvalidStatusCode) -> Self {
270        Self::Http(Box::new(error))
271    }
272}
273
274#[cfg(feature = "url-standard")]
275impl From<url::ParseError> for HttpCacheError {
276    fn from(error: url::ParseError) -> Self {
277        Self::Http(Box::new(error))
278    }
279}
280
281// Note: Client-specific error conversions (reqwest, surf, ureq, etc.)
282// are implemented in their respective http-cache-* crates to avoid
283// feature dependencies in the core http-cache crate.
284
285// Type alias for results using the unified error type
286/// A `Result` type alias for HTTP cache operations using [`HttpCacheError`]
287pub type HttpCacheResult<T> = std::result::Result<T, HttpCacheError>;
288
289/// Error type for streaming operations
290#[derive(Debug)]
291pub struct StreamingError {
292    inner: BoxError,
293    kind: StreamingErrorKind,
294}
295
296/// Different kinds of streaming errors for better error handling
297#[derive(Debug, Clone, Copy)]
298pub enum StreamingErrorKind {
299    /// I/O error (file operations, network)
300    Io,
301    /// Serialization/deserialization error
302    Serialization,
303    /// Lock contention or synchronization error
304    Concurrency,
305    /// Cache consistency error
306    Consistency,
307    /// Temporary file management error
308    TempFile,
309    /// Content addressing error (SHA256, file paths)
310    ContentAddressing,
311    /// Client library error (e.g., reqwest, surf)
312    Client,
313    /// Generic streaming error
314    Other,
315}
316
317impl StreamingError {
318    /// Create a new streaming error from any error type
319    pub fn new<E: Into<BoxError>>(error: E) -> Self {
320        Self { inner: error.into(), kind: StreamingErrorKind::Other }
321    }
322
323    /// Create a streaming error with a specific kind
324    pub fn with_kind<E: Into<BoxError>>(
325        error: E,
326        kind: StreamingErrorKind,
327    ) -> Self {
328        Self { inner: error.into(), kind }
329    }
330
331    /// Create an I/O error
332    pub fn io<E: Into<BoxError>>(error: E) -> Self {
333        Self::with_kind(error, StreamingErrorKind::Io)
334    }
335
336    /// Create a serialization error
337    pub fn serialization<E: Into<BoxError>>(error: E) -> Self {
338        Self::with_kind(error, StreamingErrorKind::Serialization)
339    }
340
341    /// Create a concurrency error
342    pub fn concurrency<E: Into<BoxError>>(error: E) -> Self {
343        Self::with_kind(error, StreamingErrorKind::Concurrency)
344    }
345
346    /// Create a consistency error
347    pub fn consistency<E: Into<BoxError>>(error: E) -> Self {
348        Self::with_kind(error, StreamingErrorKind::Consistency)
349    }
350
351    /// Create a temp file error
352    pub fn temp_file<E: Into<BoxError>>(error: E) -> Self {
353        Self::with_kind(error, StreamingErrorKind::TempFile)
354    }
355
356    /// Create a content addressing error
357    pub fn content_addressing<E: Into<BoxError>>(error: E) -> Self {
358        Self::with_kind(error, StreamingErrorKind::ContentAddressing)
359    }
360
361    /// Create a client error
362    pub fn client<E: Into<BoxError>>(error: E) -> Self {
363        Self::with_kind(error, StreamingErrorKind::Client)
364    }
365
366    /// Get the error kind
367    pub fn kind(&self) -> &StreamingErrorKind {
368        &self.kind
369    }
370}
371
372impl fmt::Display for StreamingError {
373    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374        write!(f, "Streaming error: {}", self.inner)
375    }
376}
377
378impl std::error::Error for StreamingError {
379    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
380        Some(&*self.inner)
381    }
382}
383
384impl From<BoxError> for StreamingError {
385    fn from(error: BoxError) -> Self {
386        Self::new(error)
387    }
388}
389
390impl From<std::convert::Infallible> for StreamingError {
391    fn from(never: std::convert::Infallible) -> Self {
392        match never {}
393    }
394}
395
396impl From<std::io::Error> for StreamingError {
397    fn from(error: std::io::Error) -> Self {
398        Self::new(error)
399    }
400}
401
402impl From<HttpCacheError> for StreamingError {
403    fn from(error: HttpCacheError) -> Self {
404        match error {
405            HttpCacheError::Streaming(streaming_err) => streaming_err,
406            _ => Self::new(Box::new(error)),
407        }
408    }
409}
410
411/// Streaming error type specifically for client-specific streaming operations
412///
413/// This type provides a more granular error classification for streaming operations
414/// while being compatible with the unified HttpCacheError system.
415///
416/// # Examples
417///
418/// ```rust
419/// use http_cache::{ClientStreamingError, HttpCacheError};
420///
421/// // Create a streaming error with specific client context
422/// let streaming_err = ClientStreamingError::client("reqwest", "Network timeout during streaming");
423/// let cache_err: HttpCacheError = streaming_err.into();
424/// ```
425#[derive(Debug)]
426pub enum ClientStreamingError {
427    /// Client-specific streaming error with context
428    Client {
429        /// The name of the client library (e.g., "reqwest", "tower")
430        client: String,
431        /// The underlying client error
432        error: BoxError,
433    },
434    /// HTTP cache streaming error (delegated to StreamingError)
435    HttpCache(StreamingError),
436    /// Other streaming error
437    Other(BoxError),
438}
439
440impl ClientStreamingError {
441    /// Create a client-specific streaming error
442    ///
443    /// # Examples
444    ///
445    /// ```rust
446    /// use http_cache::ClientStreamingError;
447    ///
448    /// let err = ClientStreamingError::client("reqwest", "Connection timeout");
449    /// ```
450    pub fn client<C, E>(client: C, error: E) -> Self
451    where
452        C: Into<String>,
453        E: Into<BoxError>,
454    {
455        Self::Client { client: client.into(), error: error.into() }
456    }
457
458    /// Create an HTTP cache streaming error
459    ///
460    /// # Examples
461    ///
462    /// ```rust
463    /// use http_cache::{ClientStreamingError, StreamingError};
464    ///
465    /// let streaming_err = StreamingError::io("File read failed");
466    /// let err = ClientStreamingError::http_cache(streaming_err);
467    /// ```
468    pub fn http_cache(error: StreamingError) -> Self {
469        Self::HttpCache(error)
470    }
471
472    /// Create a generic streaming error
473    ///
474    /// # Examples
475    ///
476    /// ```rust
477    /// use http_cache::ClientStreamingError;
478    ///
479    /// let err = ClientStreamingError::other("Unexpected streaming error");
480    /// ```
481    pub fn other<E: Into<BoxError>>(error: E) -> Self {
482        Self::Other(error.into())
483    }
484}
485
486impl fmt::Display for ClientStreamingError {
487    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488        match self {
489            Self::Client { client, error } => {
490                write!(f, "{} streaming error: {}", client, error)
491            }
492            Self::HttpCache(e) => {
493                write!(f, "HTTP cache streaming error: {}", e)
494            }
495            Self::Other(e) => write!(f, "Streaming error: {}", e),
496        }
497    }
498}
499
500impl std::error::Error for ClientStreamingError {
501    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
502        match self {
503            Self::Client { error, .. } => Some(error.as_ref()),
504            Self::HttpCache(e) => Some(e),
505            Self::Other(e) => Some(e.as_ref()),
506        }
507    }
508}
509
510impl From<StreamingError> for ClientStreamingError {
511    fn from(error: StreamingError) -> Self {
512        Self::HttpCache(error)
513    }
514}
515
516impl From<BoxError> for ClientStreamingError {
517    fn from(error: BoxError) -> Self {
518        Self::Other(error)
519    }
520}
521
522impl From<ClientStreamingError> for HttpCacheError {
523    fn from(error: ClientStreamingError) -> Self {
524        match error {
525            ClientStreamingError::HttpCache(streaming_err) => {
526                Self::Streaming(streaming_err)
527            }
528            ClientStreamingError::Client { error, .. } => Self::Client(error),
529            ClientStreamingError::Other(error) => Self::Other(error),
530        }
531    }
532}
533
534impl From<ClientStreamingError> for StreamingError {
535    fn from(error: ClientStreamingError) -> Self {
536        match error {
537            ClientStreamingError::HttpCache(streaming_err) => streaming_err,
538            ClientStreamingError::Client { client, error } => {
539                // Preserve client context by wrapping in a descriptive error
540                let client_error =
541                    format!("Client '{}' error: {}", client, error);
542                Self::client(client_error)
543            }
544            ClientStreamingError::Other(error) => Self::new(error),
545        }
546    }
547}