pub use aws_sdk_s3;
use crate::secret::SecretCorruptionError;
mod aws {
pub use super::aws_sdk_s3::{
error::SdkError,
operation::{
complete_multipart_upload::CompleteMultipartUploadError,
create_multipart_upload::CreateMultipartUploadError, get_object::GetObjectError,
head_object::HeadObjectError, put_object::PutObjectError, upload_part::UploadPartError,
},
primitives::ByteStreamError,
};
}
pub type S3GetObjectError = aws::SdkError<aws::GetObjectError>;
pub type S3ByteStreamError = aws::ByteStreamError;
#[derive(Debug)]
pub struct CredentialsRetrievalError {
pub(crate) message: String,
}
impl std::fmt::Display for CredentialsRetrievalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Failed to retrieve S3 credentials from external provider: {}",
self.message
)
}
}
impl std::error::Error for CredentialsRetrievalError {}
#[derive(Debug)]
pub enum ClientError {
CredentialsCorruption(SecretCorruptionError),
CredentialsRetrieval(CredentialsRetrievalError),
Proxy(super::proxy::error::ConnectionError),
}
impl std::fmt::Display for ClientError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CredentialsCorruption(e) => write!(f, "Credentials corruption: {e}"),
Self::CredentialsRetrieval(e) => {
write!(f, "{e}")
}
Self::Proxy(e) => write!(f, "Proxy setup failed: {e}"),
}
}
}
impl std::error::Error for ClientError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::CredentialsCorruption(source) => Some(source),
Self::CredentialsRetrieval(source) => Some(source),
Self::Proxy(source) => Some(source),
}
}
}
impl From<SecretCorruptionError> for ClientError {
fn from(value: SecretCorruptionError) -> Self {
Self::CredentialsCorruption(value)
}
}
impl From<CredentialsRetrievalError> for ClientError {
fn from(value: CredentialsRetrievalError) -> Self {
Self::CredentialsRetrieval(value)
}
}
impl From<super::proxy::error::ConnectionError> for ClientError {
fn from(value: super::proxy::error::ConnectionError) -> Self {
Self::Proxy(value)
}
}
#[derive(Debug)]
pub enum ReadChunksError {
Io(tokio::io::Error),
Send(tokio::sync::mpsc::error::SendError<super::BytesMut>),
DataTooLarge,
}
impl std::fmt::Display for ReadChunksError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "Error while reading chunks from data: {e}"),
Self::Send(e) => write!(
f,
"Error while transferring data to the S3 object store: {e}"
),
Self::DataTooLarge => write!(
f,
"Data is too large to be split into the maximum number of \
chunks allowed by the S3 protocol. If not already the case, \
consider using a 64-bit system to increase supported data size"
),
}
}
}
impl std::error::Error for ReadChunksError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(source) => Some(source),
Self::Send(source) => Some(source),
Self::DataTooLarge => None,
}
}
}
impl From<tokio::io::Error> for ReadChunksError {
fn from(value: tokio::io::Error) -> Self {
Self::Io(value)
}
}
impl From<tokio::sync::mpsc::error::SendError<super::BytesMut>> for ReadChunksError {
fn from(value: tokio::sync::mpsc::error::SendError<super::BytesMut>) -> Self {
Self::Send(value)
}
}
impl From<std::num::TryFromIntError> for ReadChunksError {
fn from(_: std::num::TryFromIntError) -> Self {
Self::DataTooLarge
}
}
type MetadataError = crate::package::error::MetadataError<
<std::path::PathBuf as crate::package::source::PackageStream>::Error,
>;
#[derive(Debug)]
pub enum UploadError {
ClientError(ClientError),
Io(tokio::io::Error),
InvalidPackage(MetadataError),
PutObject(Box<put::Error>),
ReadChunks(ReadChunksError),
AsyncTask(tokio::task::JoinError),
}
impl std::fmt::Display for UploadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error while reading chunks")
}
}
impl std::error::Error for UploadError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ClientError(source) => Some(source),
Self::Io(source) => Some(source),
Self::InvalidPackage(source) => Some(source),
Self::PutObject(source) => Some(source),
Self::ReadChunks(source) => Some(source),
Self::AsyncTask(source) => Some(source),
}
}
}
impl From<ClientError> for UploadError {
fn from(value: ClientError) -> Self {
Self::ClientError(value)
}
}
impl From<tokio::io::Error> for UploadError {
fn from(value: tokio::io::Error) -> Self {
Self::Io(value)
}
}
impl From<MetadataError> for UploadError {
fn from(value: MetadataError) -> Self {
Self::InvalidPackage(value)
}
}
impl From<put::Error> for UploadError {
fn from(value: put::Error) -> Self {
Self::PutObject(Box::new(value))
}
}
impl From<ReadChunksError> for UploadError {
fn from(value: ReadChunksError) -> Self {
Self::ReadChunks(value)
}
}
impl From<tokio::task::JoinError> for UploadError {
fn from(value: tokio::task::JoinError) -> Self {
Self::AsyncTask(value)
}
}
pub mod put {
use super::aws;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
Put(aws::SdkError<aws::PutObjectError>),
PutMultipart(aws::SdkError<aws::CreateMultipartUploadError>),
UploadPart(aws::SdkError<aws::UploadPartError>),
FetchMultipartId,
FetchEntityTag,
EmptyUpload,
CompleteMultipartUpload(aws::SdkError<aws::CompleteMultipartUploadError>),
Semaphore(tokio::sync::AcquireError),
Join(tokio::task::JoinError),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Put(source) => std::fmt::Display::fmt(&super::Error(source), f),
Self::PutMultipart(source) => std::fmt::Display::fmt(&super::Error(source), f),
Self::FetchMultipartId => write!(f, "Multipart upload ID could not be fetched"),
Self::UploadPart(source) => source.fmt(f),
Self::Semaphore(_) => write!(f, "Semaphore error"),
Self::EmptyUpload => write!(f, "No parts have been uploaded"),
Self::FetchEntityTag => write!(
f,
"Could not retrieve the entity tag for the uploaded object"
),
Self::CompleteMultipartUpload(source) => source.fmt(f),
Self::Join(source) => source.fmt(f),
}
}
}
impl From<aws::SdkError<aws::PutObjectError>> for Error {
fn from(value: aws::SdkError<aws::PutObjectError>) -> Self {
Self::Put(value)
}
}
impl From<aws::SdkError<aws::UploadPartError>> for Error {
fn from(value: aws::SdkError<aws::UploadPartError>) -> Self {
Self::UploadPart(value)
}
}
impl From<aws::SdkError<aws::CreateMultipartUploadError>> for Error {
fn from(value: aws::SdkError<aws::CreateMultipartUploadError>) -> Self {
Self::PutMultipart(value)
}
}
impl From<tokio::sync::AcquireError> for Error {
fn from(value: tokio::sync::AcquireError) -> Self {
Self::Semaphore(value)
}
}
impl From<aws::SdkError<aws::CompleteMultipartUploadError>> for Error {
fn from(value: aws::SdkError<aws::CompleteMultipartUploadError>) -> Self {
Self::CompleteMultipartUpload(value)
}
}
impl From<tokio::task::JoinError> for Error {
fn from(value: tokio::task::JoinError) -> Self {
Self::Join(value)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Put(source) => Some(source),
Self::PutMultipart(source) => Some(source),
Self::UploadPart(source) => Some(source),
Self::FetchMultipartId => None,
Self::Semaphore(source) => Some(source),
Self::EmptyUpload => None,
Self::FetchEntityTag => None,
Self::CompleteMultipartUpload(source) => Some(source),
Self::Join(source) => Some(source),
}
}
}
}
pub mod get {
use super::aws;
#[non_exhaustive]
#[derive(Debug)]
pub struct Error(pub aws::SdkError<aws::HeadObjectError>);
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&super::Error(self.source()), f)
}
}
impl Error {
pub fn source(&self) -> &aws::SdkError<aws::HeadObjectError> {
&self.0
}
}
impl From<aws::SdkError<aws::HeadObjectError>> for Error {
fn from(value: aws::SdkError<aws::HeadObjectError>) -> Self {
Self(value)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.0)
}
}
}
#[derive(Debug)]
struct Error<E>(E);
impl<E> std::fmt::Display for Error<&aws::SdkError<E>>
where
E: ExtractMessage + MainMessage,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
aws::SdkError::DispatchFailure(e) => {
use std::error::Error;
if let Some(connector_error) = e.as_connector_error()
&& let Some(source) = connector_error.source()
{
return write!(f, "could not connect to s3 store: {source}");
}
}
aws::SdkError::ServiceError(e) => {
if let Some(description) = e.err().extract_message() {
return write!(f, "{}: {description}", E::MAIN_MESSAGE);
}
if let Some(description) = e.raw().headers().get("x-minio-error-desc") {
return write!(f, "{}: {description}", E::MAIN_MESSAGE);
}
}
_ => {}
}
write!(f, "{}: {}", E::MAIN_MESSAGE, self.0)
}
}
trait MainMessage {
const MAIN_MESSAGE: &'static str;
}
impl MainMessage for aws::HeadObjectError {
const MAIN_MESSAGE: &'static str = "unable to access s3 object";
}
impl MainMessage for aws::PutObjectError {
const MAIN_MESSAGE: &'static str = "unable to put s3 object";
}
impl MainMessage for aws::CreateMultipartUploadError {
const MAIN_MESSAGE: &'static str = "unable to put s3 object";
}
trait ExtractMessage {
fn extract_message(&self) -> Option<&str>;
}
impl ExtractMessage for aws::HeadObjectError {
fn extract_message(&self) -> Option<&str> {
self.meta().message()
}
}
impl ExtractMessage for aws::PutObjectError {
fn extract_message(&self) -> Option<&str> {
self.meta().message()
}
}
impl ExtractMessage for aws::CreateMultipartUploadError {
fn extract_message(&self) -> Option<&str> {
self.meta().message()
}
}
#[derive(Debug)]
pub enum OpenError {
Io(tokio::io::Error),
Get(get::Error),
ObjectTooSmall,
MissingSizeInfo,
GetObject(S3GetObjectError),
ReadStream(S3ByteStreamError),
Client(ClientError),
}
impl std::fmt::Display for OpenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Error while opening zip reader{}",
match self {
Self::ObjectTooSmall => ": s3 object size is too small",
Self::MissingSizeInfo => ": unable to fetch s3 object size",
Self::ReadStream(_) => ": error while reading from stream",
_ => "",
}
)
}
}
impl std::error::Error for OpenError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(source) => Some(source),
Self::Get(source) => Some(source),
Self::ObjectTooSmall => None,
Self::MissingSizeInfo => None,
Self::GetObject(source) => Some(source),
Self::ReadStream(source) => Some(source),
Self::Client(source) => Some(source),
}
}
}
impl From<tokio::io::Error> for OpenError {
fn from(value: tokio::io::Error) -> Self {
Self::Io(value)
}
}
impl From<get::Error> for OpenError {
fn from(value: get::Error) -> Self {
Self::Get(value)
}
}
impl From<S3GetObjectError> for OpenError {
fn from(value: S3GetObjectError) -> Self {
Self::GetObject(value)
}
}
impl From<S3ByteStreamError> for OpenError {
fn from(value: S3ByteStreamError) -> Self {
Self::ReadStream(value)
}
}
impl From<ClientError> for OpenError {
fn from(value: ClientError) -> Self {
Self::Client(value)
}
}