1use 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
11pub type Result<T, E = Error> = ::std::result::Result<T, E>;
13
14#[derive(Debug, thiserror::Error)]
16pub struct Error(pub(crate) ErrorRepr);
17
18impl Error {
19 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 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 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 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#[derive(Debug, Clone, Copy)]
74#[non_exhaustive]
75pub enum ErrorKind {
76 Config,
78 Encoding,
80 Sdk,
82 Upload,
84 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#[derive(Debug, Clone)]
107pub struct FailedUpload {
108 pub id: UploadId,
110 pub uri: ObjectUri,
112 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
136pub(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#[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}