Skip to main content

marple_db/
errors.rs

1use reqwest::{Method, StatusCode};
2use std::num::TryFromIntError;
3use thiserror::Error;
4
5/// Error type returned by the MarpleDB SDK.
6///
7/// `Transport` means no usable HTTP response was received, `Api` means the
8/// MarpleDB API returned a non-success status, and `Storage` covers direct
9/// pre-signed storage uploads/downloads.
10///
11/// ```
12/// # fn handle(error: marple_db::Error) {
13/// match error {
14///     marple_db::Error::Api { status, body, .. } => {
15///         eprintln!("API returned {status}: {body}");
16///     }
17///     error if error.status().is_some() => {
18///         eprintln!("HTTP-like error: {:?}", error.status());
19///     }
20///     error => eprintln!("{error}"),
21/// }
22/// # }
23/// ```
24#[non_exhaustive]
25#[derive(Debug, Error)]
26pub enum Error {
27    /// The SDK was configured with invalid input.
28    #[error("invalid configuration: {0}")]
29    Config(String),
30
31    /// Building an HTTP header failed.
32    #[error("invalid HTTP header value")]
33    Header(#[from] reqwest::header::InvalidHeaderValue),
34
35    /// A request failed before receiving an API response.
36    #[error("HTTP transport error on {method} {endpoint}")]
37    Transport {
38        /// HTTP method used for the request.
39        method: Method,
40        /// API endpoint or URL being requested.
41        endpoint: String,
42        /// Underlying reqwest error.
43        #[source]
44        source: reqwest::Error,
45    },
46
47    /// The MarpleDB API returned a non-success HTTP status.
48    #[error("MarpleDB API returned {status} on {method} {endpoint}: {body}")]
49    Api {
50        /// HTTP method used for the request.
51        method: Method,
52        /// API endpoint being requested.
53        endpoint: String,
54        /// Response status code.
55        status: StatusCode,
56        /// Response body text.
57        body: String,
58    },
59
60    /// Direct storage upload or download failed.
61    #[error("storage transfer failed: {context}")]
62    Storage {
63        /// Human-readable storage operation context.
64        context: String,
65        /// HTTP status code when the storage service responded with one.
66        status: Option<StatusCode>,
67        /// Response body text when available.
68        body: Option<String>,
69        /// Underlying reqwest error when the request failed before a response.
70        #[source]
71        source: Option<reqwest::Error>,
72    },
73
74    /// A stream with the requested name was not found.
75    #[error("stream {name:?} not found")]
76    StreamNotFound {
77        /// Requested stream name.
78        name: String,
79    },
80
81    /// A stream with the requested id was not found.
82    #[error("stream {id} not found")]
83    StreamIdNotFound {
84        /// Requested stream id.
85        id: i32,
86    },
87
88    /// The dataset has no original-file backup available for download.
89    #[error("dataset {id} has no backup available")]
90    NoBackup {
91        /// Dataset id.
92        id: i32,
93    },
94
95    /// Import polling reached its timeout before a terminal status.
96    #[error("ingestion timed out after {timeout_secs}s, last status: {last_status}")]
97    ImportTimeout {
98        /// Timeout in seconds.
99        timeout_secs: u64,
100        /// Last observed import status.
101        last_status: String,
102    },
103
104    /// Import polling reached a failed terminal status.
105    #[error("ingestion failed for dataset {id}: {message}")]
106    ImportFailed {
107        /// Dataset id.
108        id: i32,
109        /// Failure message from the API, if present.
110        message: String,
111    },
112
113    /// The API returned a response that does not match the SDK protocol.
114    #[error("invalid server response: {0}")]
115    Protocol(String),
116
117    /// Local filesystem I/O failed.
118    #[error("I/O error")]
119    Io(#[from] std::io::Error),
120
121    /// URL parsing failed.
122    #[error("URL parse error")]
123    Url(#[from] url::ParseError),
124
125    /// JSON serialization or deserialization failed.
126    #[error("JSON error")]
127    Json(#[from] serde_json::Error),
128
129    /// Integer conversion failed.
130    #[error("integer conversion failed")]
131    IntegerConversion(#[from] TryFromIntError),
132}
133
134impl Error {
135    /// Returns the HTTP status for API or storage responses that provided one.
136    pub fn status(&self) -> Option<StatusCode> {
137        match self {
138            Self::Api { status, .. } => Some(*status),
139            Self::Storage { status, .. } => *status,
140            _ => None,
141        }
142    }
143}
144
145/// Result type returned by the MarpleDB SDK.
146pub type Result<T> = std::result::Result<T, Error>;