objectstore_service/
error.rs

1//! Error types for service and backend operations.
2//!
3//! [`Error`] covers I/O, serialization, HTTP, metadata, authentication,
4//! and backend-specific failures. [`Result`] is the corresponding alias.
5
6use std::any::Any;
7
8use thiserror::Error as ThisError;
9
10/// Error type for service operations.
11#[derive(Debug, ThisError)]
12pub enum Error {
13    /// IO errors related to payload streaming or file operations.
14    #[error("i/o error: {0}")]
15    Io(#[from] std::io::Error),
16
17    /// Errors related to de/serialization.
18    #[error("serde error: {context}")]
19    Serde {
20        /// Context describing what was being serialized/deserialized.
21        context: String,
22        /// The underlying serde error.
23        #[source]
24        cause: serde_json::Error,
25    },
26
27    /// All errors stemming from the reqwest client, used in multiple backends to send requests to
28    /// e.g. GCP APIs.
29    /// These can be network errors encountered when sending the requests, but can also indicate
30    /// errors returned by the API itself.
31    #[error("reqwest error: {context}")]
32    Reqwest {
33        /// Context describing the request that failed.
34        context: String,
35        /// The underlying reqwest error.
36        #[source]
37        cause: reqwest::Error,
38    },
39
40    /// Errors related to de/serialization and parsing of object metadata.
41    #[error("metadata error: {0}")]
42    Metadata(#[from] objectstore_types::metadata::Error),
43
44    /// Errors encountered when attempting to authenticate with GCP.
45    #[error("GCP authentication error: {0}")]
46    GcpAuth(#[from] gcp_auth::Error),
47
48    /// A spawned service task panicked.
49    #[error("service task failed: {0}")]
50    Panic(String),
51
52    /// A spawned service task was dropped before it could deliver its result.
53    ///
54    /// This is an unexpected condition that can occur when the runtime drops the task for unknown
55    /// reasons.
56    #[error("task dropped")]
57    Dropped,
58
59    /// The service has reached its concurrency limit and cannot accept more operations.
60    #[error("concurrency limit reached")]
61    AtCapacity,
62
63    /// Any other error stemming from one of the storage backends, which might be specific to that
64    /// backend or to a certain operation.
65    #[error("storage backend error: {context}")]
66    Generic {
67        /// Context describing the operation that failed.
68        context: String,
69        /// The underlying error, if available.
70        #[source]
71        cause: Option<Box<dyn std::error::Error + Send + Sync>>,
72    },
73}
74
75impl Error {
76    /// Creates an [`Error::Panic`] from a panic payload, extracting the message.
77    pub fn panic(payload: Box<dyn Any + Send>) -> Self {
78        let msg = if let Some(s) = payload.downcast_ref::<&str>() {
79            (*s).to_owned()
80        } else if let Some(s) = payload.downcast_ref::<String>() {
81            s.clone()
82        } else {
83            "unknown panic".to_owned()
84        };
85        Self::Panic(msg)
86    }
87
88    /// Creates an [`Error::Reqwest`] from a reqwest error with context.
89    pub fn reqwest(context: impl Into<String>, cause: reqwest::Error) -> Self {
90        Self::Reqwest {
91            context: context.into(),
92            cause,
93        }
94    }
95
96    /// Creates an [`Error::Generic`] with a context string and no cause.
97    pub fn generic(context: impl Into<String>) -> Self {
98        Self::Generic {
99            context: context.into(),
100            cause: None,
101        }
102    }
103}
104
105/// Result type for service operations.
106pub type Result<T, E = Error> = std::result::Result<T, E>;