Skip to main content

backbone_bucket/
error.rs

1//! Top-level error type for the bucket module.
2//!
3//! `BucketError` is the module's unified error surface — storage failures,
4//! authorization denials, config/signing issues, and missing objects all
5//! collapse into one enum so consumers get precise `match` coverage.
6//!
7//! Converts into `backbone_core::ServiceError` for compatibility with the
8//! framework error plumbing used by the generated CRUD stack.
9
10use std::io;
11
12use thiserror::Error;
13
14/// Unified error surface for storage, serving, and signing.
15#[derive(Debug, Error)]
16pub enum BucketError {
17    /// Object not found in storage or database.
18    #[error("not found")]
19    NotFound,
20
21    /// Caller is not permitted to perform the requested action.
22    #[error("forbidden")]
23    Forbidden,
24
25    /// Request could not be authenticated.
26    #[error("unauthenticated")]
27    Unauthenticated,
28
29    /// Presigned URL has expired or is malformed.
30    #[error("invalid or expired signature")]
31    InvalidSignature,
32
33    /// Configured backend cannot satisfy this operation (e.g. `public_url`
34    /// on a backend without a public bucket configured).
35    #[error("operation not supported by backend: {0}")]
36    Unsupported(String),
37
38    /// Missing or malformed configuration.
39    #[error("configuration error: {0}")]
40    Config(String),
41
42    /// Storage backend I/O error.
43    #[error("storage I/O error: {0}")]
44    Io(#[from] io::Error),
45
46    /// S3/MinIO-side error.
47    #[error("s3 error: {0}")]
48    S3(String),
49
50    /// URL construction / parsing error.
51    #[error("url error: {0}")]
52    Url(String),
53
54    /// Request payload is invalid (missing required fields, malformed
55    /// input). Maps to HTTP 400.
56    #[error("invalid input: {0}")]
57    InvalidInput(String),
58
59    /// Operation conflicts with current resource state (e.g. uploading
60    /// to a readonly/archived bucket). Maps to HTTP 409.
61    #[error("conflict: {0}")]
62    Conflict(String),
63
64    /// Payload exceeds the allowed size. Maps to HTTP 413.
65    #[error("payload too large: {0}")]
66    PayloadTooLarge(String),
67
68    /// MIME type is not in the bucket's allow-list. Maps to HTTP 415.
69    #[error("unsupported media type: {0}")]
70    UnsupportedMediaType(String),
71
72    /// Catch-all for errors outside the above categories.
73    #[error("{0}")]
74    Other(String),
75}
76
77pub type BucketResult<T> = Result<T, BucketError>;
78
79impl From<BucketError> for backbone_core::ServiceError {
80    fn from(err: BucketError) -> Self {
81        match err {
82            BucketError::NotFound => backbone_core::ServiceError::NotFound,
83            BucketError::Forbidden => {
84                backbone_core::ServiceError::Validation("forbidden".to_string())
85            }
86            BucketError::Unauthenticated => {
87                backbone_core::ServiceError::Validation("unauthenticated".to_string())
88            }
89            other => backbone_core::ServiceError::Internal(other.to_string()),
90        }
91    }
92}
93
94impl From<url::ParseError> for BucketError {
95    fn from(err: url::ParseError) -> Self {
96        BucketError::Url(err.to_string())
97    }
98}