aws_multipart_upload/
error.rs

1//! Types for working with errors.
2use crate::client::UploadId;
3use crate::client::part::PartNumber;
4use crate::codec::{EncodeError, EncodeErrorKind};
5use crate::uri::ObjectUri;
6
7use aws_sdk::error::SdkError;
8use std::error::Error as StdError;
9use std::fmt::{self, Display, Formatter};
10
11/// A specialized `Result` type for errors originating in this crate.
12pub type Result<T, E = Error> = ::std::result::Result<T, E>;
13
14/// The value returned when some operation in this crate fails.
15#[derive(Debug, thiserror::Error)]
16pub struct Error(pub(crate) ErrorRepr);
17
18impl Error {
19    /// Returns the details of the upload that failed if available.
20    pub fn failed_upload(&self) -> Option<&FailedUpload> {
21        if let ErrorRepr::UploadFailed { failed, .. } = &self.0 {
22            return Some(failed);
23        }
24        None
25    }
26
27    /// Returns the category under which this error falls.
28    pub fn kind(&self) -> ErrorKind {
29        match self.0 {
30            ErrorRepr::Sdk(_) => ErrorKind::Sdk,
31            ErrorRepr::Missing(_, _) => ErrorKind::Config,
32            ErrorRepr::Encoding(_, _) => ErrorKind::Encoding,
33            ErrorRepr::UploadFailed { .. } => ErrorKind::Upload,
34            ErrorRepr::DynStd(_) => ErrorKind::Unknown,
35            ErrorRepr::Other { kind, .. } => kind,
36        }
37    }
38
39    /// Convert an arbitrary [`std::error::Error`] to this error type.
40    pub fn from_dyn_std<E>(e: E) -> Self
41    where
42        E: StdError + 'static,
43    {
44        let err = Box::new(e);
45        Self(ErrorRepr::DynStd(err))
46    }
47
48    /// Create this error from a category and message.
49    pub fn other(kind: ErrorKind, msg: &'static str) -> Self {
50        Self(ErrorRepr::Other { kind, msg })
51    }
52}
53
54impl Display for Error {
55    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
56        self.0.fmt(f)
57    }
58}
59
60impl From<ErrorRepr> for Error {
61    fn from(value: ErrorRepr) -> Self {
62        Self(value)
63    }
64}
65
66impl<E: EncodeError> From<E> for Error {
67    fn from(value: E) -> Self {
68        ErrorRepr::from(value).into()
69    }
70}
71
72/// The category of the error.
73#[derive(Debug, Clone, Copy)]
74#[non_exhaustive]
75pub enum ErrorKind {
76    /// There was an error in configuration.
77    Config,
78    /// There was an error encoding an item in a part.
79    Encoding,
80    /// An error was returned by the underlying SDK.
81    Sdk,
82    /// There was an error operating the upload.
83    Upload,
84    /// The origin of the error is not known.
85    Unknown,
86}
87
88impl Display for ErrorKind {
89    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
90        match self {
91            Self::Config => write!(f, "config"),
92            Self::Encoding => write!(f, "encoding"),
93            Self::Sdk => write!(f, "sdk"),
94            Self::Upload => write!(f, "upload"),
95            Self::Unknown => write!(f, "unknown"),
96        }
97    }
98}
99
100/// The data of an upload that failed.
101///
102/// This may be found using [`Error::failed_upload`] on the error returned by
103/// some operation.
104///
105/// The data is what would be required to resume a multipart upload or abort it.
106#[derive(Debug, Clone)]
107pub struct FailedUpload {
108    /// The ID of the upload assigned on creation.
109    pub id: UploadId,
110    /// The destination URI of the upload.
111    pub uri: ObjectUri,
112    /// The part number that was in progress when the error occurred.
113    pub part: PartNumber,
114}
115
116impl FailedUpload {
117    pub(crate) fn new(id: &UploadId, uri: &ObjectUri, part: PartNumber) -> Self {
118        Self {
119            id: id.clone(),
120            uri: uri.clone(),
121            part,
122        }
123    }
124}
125
126impl Display for FailedUpload {
127    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
128        write!(
129            f,
130            r#"{{ "id": "{}", "uri": "{}", "part": "{}" }}"#,
131            &self.id, &self.uri, self.part
132        )
133    }
134}
135
136/// Appending upload data to the error if available.
137pub(crate) trait UploadContext<T> {
138    fn upload_ctx(self, id: &UploadId, uri: &ObjectUri, part: PartNumber) -> Result<T>;
139}
140
141impl<T, E> UploadContext<T> for Result<T, E>
142where
143    E: StdError + 'static,
144{
145    fn upload_ctx(self, id: &UploadId, uri: &ObjectUri, part: PartNumber) -> Result<T> {
146        match self {
147            Ok(t) => Ok(t),
148            Err(e) => {
149                let failed = FailedUpload::new(id, uri, part);
150                let err = ErrorRepr::UploadFailed {
151                    failed,
152                    source: Box::new(e),
153                };
154                Err(err.into())
155            }
156        }
157    }
158}
159
160/// Internal error representation.
161#[derive(Debug, thiserror::Error)]
162pub(crate) enum ErrorRepr {
163    #[error("{0} missing required field: {1}")]
164    Missing(&'static str, &'static str),
165    #[error("encoding error: {0} {1}")]
166    Encoding(String, EncodeErrorKind),
167    #[error("upload failed: {failed}: {source}")]
168    UploadFailed {
169        failed: FailedUpload,
170        source: Box<dyn StdError>,
171    },
172    #[error("error from aws_sdk: {0}")]
173    Sdk(#[source] Box<dyn StdError>),
174    #[error("{kind} error: {msg}")]
175    Other { kind: ErrorKind, msg: &'static str },
176    #[error(transparent)]
177    DynStd(Box<dyn StdError>),
178}
179
180impl<E, R> From<SdkError<E, R>> for ErrorRepr
181where
182    E: StdError + 'static,
183    R: std::fmt::Debug + 'static,
184{
185    fn from(value: SdkError<E, R>) -> Self {
186        Self::Sdk(Box::new(value))
187    }
188}
189
190impl<E: EncodeError> From<E> for ErrorRepr {
191    fn from(value: E) -> Self {
192        ErrorRepr::Encoding(value.message(), value.kind())
193    }
194}