sett/remote/s3/
error.rs

1//! Error types for s3 operations
2
3pub use aws_sdk_s3;
4
5use crate::secret::SecretCorruptionError;
6
7/// Reexports for foreign error types
8mod aws {
9    pub use super::aws_sdk_s3::{
10        error::SdkError,
11        operation::{
12            complete_multipart_upload::CompleteMultipartUploadError,
13            create_multipart_upload::CreateMultipartUploadError, get_object::GetObjectError,
14            head_object::HeadObjectError, put_object::PutObjectError, upload_part::UploadPartError,
15        },
16        primitives::ByteStreamError,
17    };
18}
19
20/// S3 get object error alias
21pub type S3GetObjectError = aws::SdkError<aws::GetObjectError>;
22/// S3 byte stream error alias
23pub type S3ByteStreamError = aws::ByteStreamError;
24
25/// Error during retrieval of S3 credentials from an external provider.
26#[derive(Debug)]
27pub struct CredentialsRetrievalError {
28    pub(crate) message: String,
29}
30
31impl std::fmt::Display for CredentialsRetrievalError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(
34            f,
35            "Failed to retrieve S3 credentials from external provider: {}",
36            self.message
37        )
38    }
39}
40
41impl std::error::Error for CredentialsRetrievalError {}
42
43/// Error occurring when setting up a new S3 client.
44#[derive(Debug)]
45pub enum ClientError {
46    /// S3 credentials decoding error.
47    CredentialsCorruption(SecretCorruptionError),
48    /// Error during retrieval of S3 credentials from an external provider.
49    CredentialsRetrieval(CredentialsRetrievalError),
50    /// Setup of the proxy error.
51    Proxy(super::proxy::error::ConnectionError),
52}
53
54impl std::fmt::Display for ClientError {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            Self::CredentialsCorruption(e) => write!(f, "Credentials corruption: {e}"),
58            Self::CredentialsRetrieval(e) => {
59                write!(f, "{e}")
60            }
61            Self::Proxy(e) => write!(f, "Proxy setup failed: {e}"),
62        }
63    }
64}
65
66impl std::error::Error for ClientError {
67    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
68        match self {
69            Self::CredentialsCorruption(source) => Some(source),
70            Self::CredentialsRetrieval(source) => Some(source),
71            Self::Proxy(source) => Some(source),
72        }
73    }
74}
75
76impl From<SecretCorruptionError> for ClientError {
77    fn from(value: SecretCorruptionError) -> Self {
78        Self::CredentialsCorruption(value)
79    }
80}
81impl From<CredentialsRetrievalError> for ClientError {
82    fn from(value: CredentialsRetrievalError) -> Self {
83        Self::CredentialsRetrieval(value)
84    }
85}
86impl From<super::proxy::error::ConnectionError> for ClientError {
87    fn from(value: super::proxy::error::ConnectionError) -> Self {
88        Self::Proxy(value)
89    }
90}
91
92/// Error occurring while reading chunks from a stream
93#[derive(Debug)]
94pub enum ReadChunksError {
95    /// IO error
96    Io(tokio::io::Error),
97    /// Error while transferring bytes
98    Send(tokio::sync::mpsc::error::SendError<super::BytesMut>),
99    /// Data is too large to be split into the max number of chunks allowed
100    /// by the S3 protocol.
101    DataTooLarge,
102}
103
104impl std::fmt::Display for ReadChunksError {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match self {
107            Self::Io(e) => write!(f, "Error while reading chunks from data: {e}"),
108            Self::Send(e) => write!(
109                f,
110                "Error while transferring data to the S3 object store: {e}"
111            ),
112            Self::DataTooLarge => write!(
113                f,
114                "Data is too large to be split into the maximum number of \
115                chunks allowed by the S3 protocol. If not already the case, \
116                consider using a 64-bit system to increase supported data size"
117            ),
118        }
119    }
120}
121
122impl std::error::Error for ReadChunksError {
123    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
124        match self {
125            Self::Io(source) => Some(source),
126            Self::Send(source) => Some(source),
127            Self::DataTooLarge => None,
128        }
129    }
130}
131
132impl From<tokio::io::Error> for ReadChunksError {
133    fn from(value: tokio::io::Error) -> Self {
134        Self::Io(value)
135    }
136}
137
138impl From<tokio::sync::mpsc::error::SendError<super::BytesMut>> for ReadChunksError {
139    fn from(value: tokio::sync::mpsc::error::SendError<super::BytesMut>) -> Self {
140        Self::Send(value)
141    }
142}
143
144impl From<std::num::TryFromIntError> for ReadChunksError {
145    fn from(_: std::num::TryFromIntError) -> Self {
146        Self::DataTooLarge
147    }
148}
149
150type MetadataError = crate::package::error::MetadataError<
151    <std::path::PathBuf as crate::package::source::PackageStream>::Error,
152>;
153
154/// Error occurring while uploading data
155#[derive(Debug)]
156pub enum UploadError {
157    /// Error while creating a client object to connect with the S3 instance.
158    ClientError(ClientError),
159    /// IO error
160    Io(tokio::io::Error),
161    /// Invalid package
162    InvalidPackage(MetadataError),
163    /// Error during put
164    PutObject(Box<put::Error>),
165    /// Error while reading chunks
166    ReadChunks(ReadChunksError),
167    /// Async task join error
168    AsyncTask(tokio::task::JoinError),
169}
170
171impl std::fmt::Display for UploadError {
172    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173        write!(f, "Error while reading chunks")
174    }
175}
176
177impl std::error::Error for UploadError {
178    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
179        match self {
180            Self::ClientError(source) => Some(source),
181            Self::Io(source) => Some(source),
182            Self::InvalidPackage(source) => Some(source),
183            Self::PutObject(source) => Some(source),
184            Self::ReadChunks(source) => Some(source),
185            Self::AsyncTask(source) => Some(source),
186        }
187    }
188}
189
190impl From<ClientError> for UploadError {
191    fn from(value: ClientError) -> Self {
192        Self::ClientError(value)
193    }
194}
195impl From<tokio::io::Error> for UploadError {
196    fn from(value: tokio::io::Error) -> Self {
197        Self::Io(value)
198    }
199}
200impl From<MetadataError> for UploadError {
201    fn from(value: MetadataError) -> Self {
202        Self::InvalidPackage(value)
203    }
204}
205impl From<put::Error> for UploadError {
206    fn from(value: put::Error) -> Self {
207        Self::PutObject(Box::new(value))
208    }
209}
210impl From<ReadChunksError> for UploadError {
211    fn from(value: ReadChunksError) -> Self {
212        Self::ReadChunks(value)
213    }
214}
215impl From<tokio::task::JoinError> for UploadError {
216    fn from(value: tokio::task::JoinError) -> Self {
217        Self::AsyncTask(value)
218    }
219}
220
221/// Namespace for [put::Error]
222pub mod put {
223    use super::aws;
224
225    /// Error type for s3 put operations
226    #[non_exhaustive]
227    #[derive(Debug)]
228    pub enum Error {
229        /// Error during put.
230        Put(aws::SdkError<aws::PutObjectError>),
231        /// Error during multipart put
232        PutMultipart(aws::SdkError<aws::CreateMultipartUploadError>),
233        /// Error while uploading a part
234        UploadPart(aws::SdkError<aws::UploadPartError>),
235        /// Fetching multipart id fails
236        FetchMultipartId,
237        /// Fetching entity tag fails
238        FetchEntityTag,
239        /// When nothing has been uploaded
240        EmptyUpload,
241        /// Completing multipart upload fails
242        CompleteMultipartUpload(aws::SdkError<aws::CompleteMultipartUploadError>),
243        /// Semaphore acquisition error
244        Semaphore(tokio::sync::AcquireError),
245        /// Async task join error
246        Join(tokio::task::JoinError),
247    }
248    impl std::fmt::Display for Error {
249        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250            match self {
251                Self::Put(source) => std::fmt::Display::fmt(&super::Error(source), f),
252                Self::PutMultipart(source) => std::fmt::Display::fmt(&super::Error(source), f),
253                Self::FetchMultipartId => write!(f, "Multipart upload ID could not be fetched"),
254                Self::UploadPart(source) => source.fmt(f),
255                Self::Semaphore(_) => write!(f, "Semaphore error"),
256                Self::EmptyUpload => write!(f, "No parts have been uploaded"),
257                Self::FetchEntityTag => write!(
258                    f,
259                    "Could not retrieve the entity tag for the uploaded object"
260                ),
261                Self::CompleteMultipartUpload(source) => source.fmt(f),
262                Self::Join(source) => source.fmt(f),
263            }
264        }
265    }
266
267    impl From<aws::SdkError<aws::PutObjectError>> for Error {
268        fn from(value: aws::SdkError<aws::PutObjectError>) -> Self {
269            Self::Put(value)
270        }
271    }
272
273    impl From<aws::SdkError<aws::UploadPartError>> for Error {
274        fn from(value: aws::SdkError<aws::UploadPartError>) -> Self {
275            Self::UploadPart(value)
276        }
277    }
278
279    impl From<aws::SdkError<aws::CreateMultipartUploadError>> for Error {
280        fn from(value: aws::SdkError<aws::CreateMultipartUploadError>) -> Self {
281            Self::PutMultipart(value)
282        }
283    }
284
285    impl From<tokio::sync::AcquireError> for Error {
286        fn from(value: tokio::sync::AcquireError) -> Self {
287            Self::Semaphore(value)
288        }
289    }
290
291    impl From<aws::SdkError<aws::CompleteMultipartUploadError>> for Error {
292        fn from(value: aws::SdkError<aws::CompleteMultipartUploadError>) -> Self {
293            Self::CompleteMultipartUpload(value)
294        }
295    }
296
297    impl From<tokio::task::JoinError> for Error {
298        fn from(value: tokio::task::JoinError) -> Self {
299            Self::Join(value)
300        }
301    }
302
303    impl std::error::Error for Error {
304        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
305            match self {
306                Self::Put(source) => Some(source),
307                Self::PutMultipart(source) => Some(source),
308                Self::UploadPart(source) => Some(source),
309                Self::FetchMultipartId => None,
310                Self::Semaphore(source) => Some(source),
311                Self::EmptyUpload => None,
312                Self::FetchEntityTag => None,
313                Self::CompleteMultipartUpload(source) => Some(source),
314                Self::Join(source) => Some(source),
315            }
316        }
317    }
318}
319
320/// Namespace for [get::Error]
321pub mod get {
322    use super::aws;
323
324    /// Error type for s3 fetch operations
325    #[non_exhaustive]
326    #[derive(Debug)]
327    pub struct Error(pub aws::SdkError<aws::HeadObjectError>);
328    impl std::fmt::Display for Error {
329        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330            std::fmt::Display::fmt(&super::Error(self.source()), f)
331        }
332    }
333
334    impl Error {
335        /// Access the source of the error
336        pub fn source(&self) -> &aws::SdkError<aws::HeadObjectError> {
337            &self.0
338        }
339    }
340
341    impl From<aws::SdkError<aws::HeadObjectError>> for Error {
342        fn from(value: aws::SdkError<aws::HeadObjectError>) -> Self {
343            Self(value)
344        }
345    }
346
347    impl std::error::Error for Error {
348        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
349            Some(&self.0)
350        }
351    }
352}
353
354/// Generic errors for getting / putting an s3 object.
355#[derive(Debug)]
356struct Error<E>(E);
357
358impl<E> std::fmt::Display for Error<&aws::SdkError<E>>
359where
360    E: ExtractMessage + MainMessage,
361{
362    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363        match &self.0 {
364            aws::SdkError::DispatchFailure(e) => {
365                use std::error::Error;
366                if let Some(connector_error) = e.as_connector_error()
367                    && let Some(source) = connector_error.source()
368                {
369                    return write!(f, "could not connect to s3 store: {source}");
370                }
371            }
372            aws::SdkError::ServiceError(e) => {
373                if let Some(description) = e.err().extract_message() {
374                    return write!(f, "{}: {description}", E::MAIN_MESSAGE);
375                }
376                if let Some(description) = e.raw().headers().get("x-minio-error-desc") {
377                    return write!(f, "{}: {description}", E::MAIN_MESSAGE);
378                }
379            }
380            _ => {}
381        }
382        write!(f, "{}: {}", E::MAIN_MESSAGE, self.0)
383    }
384}
385
386trait MainMessage {
387    const MAIN_MESSAGE: &'static str;
388}
389
390impl MainMessage for aws::HeadObjectError {
391    const MAIN_MESSAGE: &'static str = "unable to access s3 object";
392}
393
394impl MainMessage for aws::PutObjectError {
395    const MAIN_MESSAGE: &'static str = "unable to put s3 object";
396}
397
398impl MainMessage for aws::CreateMultipartUploadError {
399    const MAIN_MESSAGE: &'static str = "unable to put s3 object";
400}
401
402trait ExtractMessage {
403    fn extract_message(&self) -> Option<&str>;
404}
405
406impl ExtractMessage for aws::HeadObjectError {
407    fn extract_message(&self) -> Option<&str> {
408        self.meta().message()
409    }
410}
411
412impl ExtractMessage for aws::PutObjectError {
413    fn extract_message(&self) -> Option<&str> {
414        self.meta().message()
415    }
416}
417
418impl ExtractMessage for aws::CreateMultipartUploadError {
419    fn extract_message(&self) -> Option<&str> {
420        self.meta().message()
421    }
422}
423
424/// Error occurring when trying to open an S3 stream
425#[derive(Debug)]
426pub enum OpenError {
427    /// I/O error
428    Io(tokio::io::Error),
429    /// Get error
430    Get(get::Error),
431    /// Zero object size
432    ObjectTooSmall,
433    /// No size info available
434    MissingSizeInfo,
435    /// Get object
436    GetObject(S3GetObjectError),
437    /// Read stream error
438    ReadStream(S3ByteStreamError),
439    /// S3 client error
440    Client(ClientError),
441}
442
443impl std::fmt::Display for OpenError {
444    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445        write!(
446            f,
447            "Error while opening zip reader{}",
448            match self {
449                Self::ObjectTooSmall => ": s3 object size is too small",
450                Self::MissingSizeInfo => ": unable to fetch s3 object size",
451                Self::ReadStream(_) => ": error while reading from stream",
452                _ => "",
453            }
454        )
455    }
456}
457
458impl std::error::Error for OpenError {
459    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
460        match self {
461            Self::Io(source) => Some(source),
462            Self::Get(source) => Some(source),
463            Self::ObjectTooSmall => None,
464            Self::MissingSizeInfo => None,
465            Self::GetObject(source) => Some(source),
466            Self::ReadStream(source) => Some(source),
467            Self::Client(source) => Some(source),
468        }
469    }
470}
471
472impl From<tokio::io::Error> for OpenError {
473    fn from(value: tokio::io::Error) -> Self {
474        Self::Io(value)
475    }
476}
477
478impl From<get::Error> for OpenError {
479    fn from(value: get::Error) -> Self {
480        Self::Get(value)
481    }
482}
483
484impl From<S3GetObjectError> for OpenError {
485    fn from(value: S3GetObjectError) -> Self {
486        Self::GetObject(value)
487    }
488}
489
490impl From<S3ByteStreamError> for OpenError {
491    fn from(value: S3ByteStreamError) -> Self {
492        Self::ReadStream(value)
493    }
494}
495
496impl From<ClientError> for OpenError {
497    fn from(value: ClientError) -> Self {
498        Self::Client(value)
499    }
500}