use std::future::Future;
use std::path::Path;
use aws_sdk_s3::error::DisplayErrorContext;
use aws_sdk_s3::error::ProvideErrorMetadata;
use aws_sdk_s3::error::SdkError;
use aws_sdk_s3::operation::RequestId;
use aws_sdk_s3::operation::RequestIdExt;
use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::types::Object;
use tokio_stream::Stream;
use crate::checksum::ObjectHash;
use crate::uri::Host;
use crate::uri::S3Uri;
use crate::Res;
pub mod client;
mod host;
mod object;
mod s3;
mod workflow;
pub(super) fn describe_sdk_error<E>(err: SdkError<E>) -> String
where
E: ProvideErrorMetadata + std::error::Error + Send + Sync + 'static,
{
let request_id = err.request_id().unwrap_or("-").to_string();
let extended_id = err.extended_request_id().unwrap_or("-").to_string();
let service_head = err.as_service_error().and_then(|svc| {
let code = ProvideErrorMetadata::code(svc);
let msg = ProvideErrorMetadata::message(svc);
match (code, msg) {
(Some(c), Some(m)) if !m.is_empty() => Some(format!("{c}: {m}")),
(Some(c), _) => Some(c.to_string()),
(None, Some(m)) if !m.is_empty() => Some(m.to_string()),
(None, _) => None,
}
});
if let Some(head) = service_head {
return format!("{head} (x-amz-request-id: {request_id})");
}
if let Some(raw) = err.raw_response() {
let status = raw.status().as_u16();
return format!(
"HTTP {status} (no error body; x-amz-request-id: {request_id}, x-amz-id-2: {extended_id})"
);
}
DisplayErrorContext(err).to_string()
}
pub use client::HttpClient;
pub use host::{fetch_host_config, HostChecksums, HostConfig};
pub use s3::RemoteS3;
pub use workflow::resolve_workflow;
#[cfg(test)]
pub mod mocks;
pub struct RemoteObjectStream {
pub body: ByteStream,
pub uri: S3Uri,
}
pub type StreamObjectChunk = Vec<Res<Object>>;
pub type StreamItem = Res<StreamObjectChunk>;
pub trait ObjectsStream: Stream<Item = StreamItem> {}
impl<T: Stream<Item = StreamItem>> ObjectsStream for T {}
pub trait Remote {
fn exists(&self, host: &Option<Host>, s3_uri: &S3Uri)
-> impl Future<Output = Res<bool>> + Send;
fn get_object_stream(
&self,
host: &Option<Host>,
s3_uri: &S3Uri,
) -> impl Future<Output = Res<RemoteObjectStream>> + Send;
fn resolve_url(
&self,
host: &Option<Host>,
s3_uri: &S3Uri,
) -> impl Future<Output = Res<S3Uri>> + Send;
fn put_object(
&self,
host: &Option<Host>,
s3_uri: &S3Uri,
contents: impl Into<ByteStream>,
) -> impl Future<Output = Res>;
fn upload_file(
&self,
host_config: &HostConfig,
source_path: impl AsRef<Path>,
dest_uri: &S3Uri,
size: u64,
) -> impl Future<Output = Res<(S3Uri, ObjectHash)>>;
fn host_config(&self, host: &Option<Host>) -> impl Future<Output = Res<HostConfig>> + Send;
}