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>;