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
274impl From<url::ParseError> for HttpCacheError {
275    fn from(error: url::ParseError) -> Self {
276        Self::Http(Box::new(error))
277    }
278}
279
280// Note: Client-specific error conversions (reqwest, surf, ureq, etc.)
281// are implemented in their respective http-cache-* crates to avoid
282// feature dependencies in the core http-cache crate.
283
284// Type alias for results using the unified error type
285/// A `Result` type alias for HTTP cache operations using [`HttpCacheError`]
286pub type HttpCacheResult<T> = std::result::Result<T, HttpCacheError>;
287
288/// Error type for streaming operations
289#[derive(Debug)]
290pub struct StreamingError {
291    inner: BoxError,
292    kind: StreamingErrorKind,
293}
294
295/// Different kinds of streaming errors for better error handling
296#[derive(Debug, Clone, Copy)]
297pub enum StreamingErrorKind {
298    /// I/O error (file operations, network)
299    Io,
300    /// Serialization/deserialization error
301    Serialization,
302    /// Lock contention or synchronization error
303    Concurrency,
304    /// Cache consistency error
305    Consistency,
306    /// Temporary file management error
307    TempFile,
308    /// Content addressing error (SHA256, file paths)
309    ContentAddressing,
310    /// Client library error (e.g., reqwest, surf)
311    Client,
312    /// Generic streaming error
313    Other,
314}
315
316impl StreamingError {
317    /// Create a new streaming error from any error type
318    pub fn new<E: Into<BoxError>>(error: E) -> Self {
319        Self { inner: error.into(), kind: StreamingErrorKind::Other }
320    }
321
322    /// Create a streaming error with a specific kind
323    pub fn with_kind<E: Into<BoxError>>(
324        error: E,
325        kind: StreamingErrorKind,
326    ) -> Self {
327        Self { inner: error.into(), kind }
328    }
329
330    /// Create an I/O error
331    pub fn io<E: Into<BoxError>>(error: E) -> Self {
332        Self::with_kind(error, StreamingErrorKind::Io)
333    }
334
335    /// Create a serialization error
336    pub fn serialization<E: Into<BoxError>>(error: E) -> Self {
337        Self::with_kind(error, StreamingErrorKind::Serialization)
338    }
339
340    /// Create a concurrency error
341    pub fn concurrency<E: Into<BoxError>>(error: E) -> Self {
342        Self::with_kind(error, StreamingErrorKind::Concurrency)
343    }
344
345    /// Create a consistency error
346    pub fn consistency<E: Into<BoxError>>(error: E) -> Self {
347        Self::with_kind(error, StreamingErrorKind::Consistency)
348    }
349
350    /// Create a temp file error
351    pub fn temp_file<E: Into<BoxError>>(error: E) -> Self {
352        Self::with_kind(error, StreamingErrorKind::TempFile)
353    }
354
355    /// Create a content addressing error
356    pub fn content_addressing<E: Into<BoxError>>(error: E) -> Self {
357        Self::with_kind(error, StreamingErrorKind::ContentAddressing)
358    }
359
360    /// Create a client error
361    pub fn client<E: Into<BoxError>>(error: E) -> Self {
362        Self::with_kind(error, StreamingErrorKind::Client)
363    }
364
365    /// Get the error kind
366    pub fn kind(&self) -> &StreamingErrorKind {
367        &self.kind
368    }
369}
370
371impl fmt::Display for StreamingError {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        write!(f, "Streaming error: {}", self.inner)
374    }
375}
376
377impl std::error::Error for StreamingError {
378    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
379        Some(&*self.inner)
380    }
381}
382
383impl From<BoxError> for StreamingError {
384    fn from(error: BoxError) -> Self {
385        Self::new(error)
386    }
387}
388
389impl From<std::convert::Infallible> for StreamingError {
390    fn from(never: std::convert::Infallible) -> Self {
391        match never {}
392    }
393}
394
395impl From<std::io::Error> for StreamingError {
396    fn from(error: std::io::Error) -> Self {
397        Self::new(error)
398    }
399}
400
401impl From<HttpCacheError> for StreamingError {
402    fn from(error: HttpCacheError) -> Self {
403        match error {
404            HttpCacheError::Streaming(streaming_err) => streaming_err,
405            _ => Self::new(Box::new(error)),
406        }
407    }
408}
409
410/// Streaming error type specifically for client-specific streaming operations
411///
412/// This type provides a more granular error classification for streaming operations
413/// while being compatible with the unified HttpCacheError system.
414///
415/// # Examples
416///
417/// ```rust
418/// use http_cache::{ClientStreamingError, HttpCacheError};
419///
420/// // Create a streaming error with specific client context
421/// let streaming_err = ClientStreamingError::client("reqwest", "Network timeout during streaming");
422/// let cache_err: HttpCacheError = streaming_err.into();
423/// ```
424#[derive(Debug)]
425pub enum ClientStreamingError {
426    /// Client-specific streaming error with context
427    Client {
428        /// The name of the client library (e.g., "reqwest", "tower")
429        client: String,
430        /// The underlying client error
431        error: BoxError,
432    },
433    /// HTTP cache streaming error (delegated to StreamingError)
434    HttpCache(StreamingError),
435    /// Other streaming error
436    Other(BoxError),
437}
438
439impl ClientStreamingError {
440    /// Create a client-specific streaming error
441    ///
442    /// # Examples
443    ///
444    /// ```rust
445    /// use http_cache::ClientStreamingError;
446    ///
447    /// let err = ClientStreamingError::client("reqwest", "Connection timeout");
448    /// ```
449    pub fn client<C, E>(client: C, error: E) -> Self
450    where
451        C: Into<String>,
452        E: Into<BoxError>,
453    {
454        Self::Client { client: client.into(), error: error.into() }
455    }
456
457    /// Create an HTTP cache streaming error
458    ///
459    /// # Examples
460    ///
461    /// ```rust
462    /// use http_cache::{ClientStreamingError, StreamingError};
463    ///
464    /// let streaming_err = StreamingError::io("File read failed");
465    /// let err = ClientStreamingError::http_cache(streaming_err);
466    /// ```
467    pub fn http_cache(error: StreamingError) -> Self {
468        Self::HttpCache(error)
469    }
470
471    /// Create a generic streaming error
472    ///
473    /// # Examples
474    ///
475    /// ```rust
476    /// use http_cache::ClientStreamingError;
477    ///
478    /// let err = ClientStreamingError::other("Unexpected streaming error");
479    /// ```
480    pub fn other<E: Into<BoxError>>(error: E) -> Self {
481        Self::Other(error.into())
482    }
483}
484
485impl fmt::Display for ClientStreamingError {
486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487        match self {
488            Self::Client { client, error } => {
489                write!(f, "{} streaming error: {}", client, error)
490            }
491            Self::HttpCache(e) => {
492                write!(f, "HTTP cache streaming error: {}", e)
493            }
494            Self::Other(e) => write!(f, "Streaming error: {}", e),
495        }
496    }
497}
498
499impl std::error::Error for ClientStreamingError {
500    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
501        match self {
502            Self::Client { error, .. } => Some(error.as_ref()),
503            Self::HttpCache(e) => Some(e),
504            Self::Other(e) => Some(e.as_ref()),
505        }
506    }
507}
508
509impl From<StreamingError> for ClientStreamingError {
510    fn from(error: StreamingError) -> Self {
511        Self::HttpCache(error)
512    }
513}
514
515impl From<BoxError> for ClientStreamingError {
516    fn from(error: BoxError) -> Self {
517        Self::Other(error)
518    }
519}
520
521impl From<ClientStreamingError> for HttpCacheError {
522    fn from(error: ClientStreamingError) -> Self {
523        match error {
524            ClientStreamingError::HttpCache(streaming_err) => {
525                Self::Streaming(streaming_err)
526            }
527            ClientStreamingError::Client { error, .. } => Self::Client(error),
528            ClientStreamingError::Other(error) => Self::Other(error),
529        }
530    }
531}
532
533impl From<ClientStreamingError> for StreamingError {
534    fn from(error: ClientStreamingError) -> Self {
535        match error {
536            ClientStreamingError::HttpCache(streaming_err) => streaming_err,
537            ClientStreamingError::Client { client, error } => {
538                // Preserve client context by wrapping in a descriptive error
539                let client_error =
540                    format!("Client '{}' error: {}", client, error);
541                Self::client(client_error)
542            }
543            ClientStreamingError::Other(error) => Self::new(error),
544        }
545    }
546}