use std::error::Error as StdError;
use std::fmt::Display;
use std::io;
use aws_smithy_types::error::display::DisplayErrorContext;
use aws_smithy_types::error::metadata::ProvideErrorMetadata;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid S3 URI `{uri}`: {reason}")]
InvalidS3Uri {
uri: String,
reason: String,
},
#[error("invalid local path `{path}`: {reason}")]
InvalidLocalPath {
path: String,
reason: String,
},
#[error("invalid option: {0}")]
InvalidOption(String),
#[error("invalid ZIP entry `{path}`: {reason}")]
InvalidZipEntry {
path: String,
reason: String,
},
#[error("duplicate ZIP file path `{0}`")]
DuplicateZipPath(String),
#[error("entry `{path}` is {size} bytes, larger than the S3 single PutObject limit")]
EntryTooLarge {
path: String,
size: u64,
},
#[error("S3 {operation} failed for s3://{bucket}/{key}: {message}")]
S3 {
operation: &'static str,
bucket: String,
key: String,
message: String,
},
#[error("conditional write failed for s3://{bucket}/{key}: {message}")]
ConditionalConflict {
bucket: String,
key: String,
message: String,
},
#[error("{original}; additionally failed to abort multipart upload: {abort}")]
MultipartAbort {
original: Box<Error>,
abort: Box<Error>,
},
#[error("ZIP operation failed: {0}")]
Zip(#[from] async_zip::error::ZipError),
#[error("I/O failed: {0}")]
Io(#[from] io::Error),
#[error("worker task failed: {0}")]
Join(#[from] tokio::task::JoinError),
#[error("AWS SDK build failed: {0}")]
Build(String),
}
pub(crate) fn aws_error_message(error: &(impl ProvideErrorMetadata + Display)) -> String {
match (error.code(), error.message()) {
(Some(code), Some(message)) if !message.is_empty() && message != code => {
format!("{code}: {message}")
}
(Some(code), _) => code.to_string(),
(None, Some(message)) if !message.is_empty() => message.to_string(),
_ => error.to_string(),
}
}
pub(crate) fn aws_error_context(error: &(impl ProvideErrorMetadata + StdError)) -> String {
format!("{}", DisplayErrorContext(error))
}
#[cfg(test)]
mod tests {
use aws_smithy_types::error::ErrorMetadata;
use super::*;
#[test]
fn aws_error_message_prefers_code_and_message() {
let metadata = ErrorMetadata::builder()
.code("NoSuchKey")
.message("The specified key does not exist.")
.build();
assert_eq!(
aws_error_message(&metadata),
"NoSuchKey: The specified key does not exist."
);
}
#[test]
fn aws_error_message_falls_back_to_display() {
let metadata = ErrorMetadata::builder().build();
assert_eq!(aws_error_message(&metadata), "Error");
}
#[test]
fn aws_error_context_includes_error_chain() {
let metadata = ErrorMetadata::builder()
.code("NoSuchKey")
.message("The specified key does not exist.")
.build();
let context = aws_error_context(&metadata);
assert!(context.contains("NoSuchKey"));
assert!(context.contains("The specified key does not exist."));
}
}