Skip to main content

haste_document_storage/
error.rs

1use std::fmt::{self, Display};
2
3use haste_fhir_operation_error::derive::OperationOutcomeError;
4
5/// Failure of a [`DocumentStorage`](crate::DocumentStorage) call.
6#[derive(OperationOutcomeError, Debug)]
7pub enum StorageError {
8    /// The key breaks the rules documented on [`DocumentKey`](crate::DocumentKey).
9    #[error(code = "invalid", diagnostic = "Invalid document key: {arg0}")]
10    InvalidKey(String),
11
12    /// The document cannot be stored as given, for example because its
13    /// content type is not a valid header value.
14    #[error(code = "invalid", diagnostic = "Invalid document: {arg0}")]
15    InvalidDocument(String),
16
17    /// The backend could not be set up from its configuration or credentials.
18    #[error(
19        code = "exception",
20        diagnostic = "Document storage is misconfigured: {arg0}"
21    )]
22    Configuration(String),
23
24    /// The backend rejected the server's credentials.
25    #[error(
26        code = "exception",
27        diagnostic = "Document storage rejected the credentials: {arg0}"
28    )]
29    Unauthorized(String),
30
31    /// The credentials are valid but lack permission for the operation.
32    #[error(
33        code = "exception",
34        diagnostic = "Document storage denied access: {arg0}"
35    )]
36    Forbidden(String),
37
38    /// Nothing is stored under the key (or the bucket itself is missing).
39    #[error(code = "not-found", diagnostic = "Document '{arg0}' was not found")]
40    NotFound(String),
41
42    /// The request never completed: connection, timeout or body transfer failure.
43    #[error(
44        code = "transient",
45        diagnostic = "Document storage request failed: {arg0}"
46    )]
47    Transport(String),
48
49    /// The backend answered with an unexpected status or body.
50    #[error(
51        code = "exception",
52        diagnostic = "Document storage responded with status {arg0}: {arg1}"
53    )]
54    Backend(u16, String),
55}
56
57impl StorageError {
58    /// Whether retrying the same request may succeed.
59    #[must_use]
60    pub fn is_transient(&self) -> bool {
61        match self {
62            Self::Transport(_) => true,
63            Self::Backend(status, _) => matches!(status, 408 | 429 | 500 | 502 | 503 | 504),
64            _ => false,
65        }
66    }
67}
68
69impl Display for StorageError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            Self::InvalidKey(reason) => write!(f, "invalid document key: {reason}"),
73            Self::InvalidDocument(reason) => write!(f, "invalid document: {reason}"),
74            Self::Configuration(reason) => write!(f, "document storage misconfigured: {reason}"),
75            Self::Unauthorized(reason) => write!(f, "credentials rejected: {reason}"),
76            Self::Forbidden(reason) => write!(f, "access denied: {reason}"),
77            Self::NotFound(key) => write!(f, "document '{key}' not found"),
78            Self::Transport(reason) => write!(f, "request failed: {reason}"),
79            Self::Backend(status, reason) => write!(f, "backend responded {status}: {reason}"),
80        }
81    }
82}
83
84impl std::error::Error for StorageError {}