mirror of
https://github.com/minio/minio-rs.git
synced 2026-01-22 07:32:06 +08:00
Duplicated code removed, and lazy response evaluation (#162)
* Duplicated code removed, and lazy response evaluation * moved Tokio runtime from general dependency to dev dependency
This commit is contained in:
parent
6f904b452a
commit
720943b4bb
1
.github/workflows/rust.yml
vendored
1
.github/workflows/rust.yml
vendored
@ -7,6 +7,7 @@ on:
|
||||
branches: [ "master" ]
|
||||
|
||||
env:
|
||||
RUST_LOG: debug
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
|
||||
@ -43,7 +43,7 @@ pub(crate) async fn bench_object_append(criterion: &mut Criterion) {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let offset_bytes: u64 = resp.size;
|
||||
let offset_bytes: u64 = resp.size().unwrap();
|
||||
AppendObject::new(
|
||||
ctx.client.clone(),
|
||||
ctx.bucket.clone(),
|
||||
|
||||
@ -17,6 +17,7 @@ mod common;
|
||||
|
||||
use crate::common::{create_bucket_if_not_exists, create_client_on_localhost};
|
||||
use minio::s3::Client;
|
||||
use minio::s3::response::a_response_traits::HasObjectSize;
|
||||
use minio::s3::response::{AppendObjectResponse, StatObjectResponse};
|
||||
use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
use minio::s3::types::S3Api;
|
||||
@ -54,19 +55,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.await?;
|
||||
|
||||
offset_bytes += data_size;
|
||||
if resp.object_size != offset_bytes {
|
||||
if resp.object_size() != offset_bytes {
|
||||
panic!(
|
||||
"from the append_object: size mismatch: expected {}, got {}",
|
||||
resp.object_size, offset_bytes
|
||||
resp.object_size(),
|
||||
offset_bytes
|
||||
)
|
||||
}
|
||||
//println!("Append response: {:#?}", resp);
|
||||
|
||||
let resp: StatObjectResponse = client.stat_object(bucket_name, object_name).send().await?;
|
||||
if resp.size != offset_bytes {
|
||||
if resp.size()? != offset_bytes {
|
||||
panic!(
|
||||
"from the stat_Object: size mismatch: expected {}, got {}",
|
||||
resp.size, offset_bytes
|
||||
resp.size()?,
|
||||
offset_bytes
|
||||
)
|
||||
}
|
||||
println!("{}/{}", i, n_segments);
|
||||
|
||||
@ -30,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let resp: GetBucketEncryptionResponse =
|
||||
client.get_bucket_encryption(bucket_name).send().await?;
|
||||
log::info!("encryption before: config={:?}", resp.config);
|
||||
log::info!("encryption before: config={:?}", resp.config());
|
||||
|
||||
let config = SseConfig::default();
|
||||
log::info!("going to set encryption config={:?}", config);
|
||||
@ -43,7 +43,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let resp: GetBucketEncryptionResponse =
|
||||
client.get_bucket_encryption(bucket_name).send().await?;
|
||||
log::info!("encryption after: config={:?}", resp.config);
|
||||
log::info!("encryption after: config={:?}", resp.config());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -33,8 +33,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
client.get_bucket_versioning(bucket_name).send().await?;
|
||||
log::info!(
|
||||
"versioning before: status={:?}, mfa_delete={:?}",
|
||||
resp.status,
|
||||
resp.mfa_delete
|
||||
resp.status(),
|
||||
resp.mfa_delete()
|
||||
);
|
||||
|
||||
let _resp: PutBucketVersioningResponse = client
|
||||
@ -48,8 +48,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
log::info!(
|
||||
"versioning after setting to Enabled: status={:?}, mfa_delete={:?}",
|
||||
resp.status,
|
||||
resp.mfa_delete
|
||||
resp.status(),
|
||||
resp.mfa_delete()
|
||||
);
|
||||
|
||||
let _resp: PutBucketVersioningResponse = client
|
||||
@ -63,8 +63,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
log::info!(
|
||||
"versioning after setting to Suspended: status={:?}, mfa_delete={:?}",
|
||||
resp.status,
|
||||
resp.mfa_delete
|
||||
resp.status(),
|
||||
resp.mfa_delete()
|
||||
);
|
||||
|
||||
let _resp: PutBucketVersioningResponse = client
|
||||
@ -78,8 +78,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
log::info!(
|
||||
"versioning after setting to None: status={:?}, mfa_delete={:?}",
|
||||
resp.status,
|
||||
resp.mfa_delete
|
||||
resp.status(),
|
||||
resp.mfa_delete()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -42,7 +42,7 @@ pub async fn create_bucket_if_not_exists(
|
||||
let resp: BucketExistsResponse = client.bucket_exists(bucket_name).send().await?;
|
||||
|
||||
// Make 'bucket_name' bucket if not exist.
|
||||
if !resp.exists {
|
||||
if !resp.exists() {
|
||||
client.create_bucket(bucket_name).send().await.unwrap();
|
||||
};
|
||||
Ok(())
|
||||
|
||||
@ -55,7 +55,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let get_object = client.get_object(bucket_name, object_name).send().await?;
|
||||
|
||||
get_object.content.to_file(Path::new(download_path)).await?;
|
||||
get_object
|
||||
.content()?
|
||||
.to_file(Path::new(download_path))
|
||||
.await?;
|
||||
|
||||
log::info!("Object '{object_name}' is successfully downloaded to file '{download_path}'.");
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
log::info!("Object prompt result: '{}'", resp.prompt_response);
|
||||
log::info!("Object prompt result: '{}'", resp.prompt_response()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let resp: BucketExistsResponse = client.bucket_exists(&args.bucket).send().await.unwrap();
|
||||
|
||||
if !resp.exists {
|
||||
if !resp.exists() {
|
||||
client.create_bucket(&args.bucket).send().await.unwrap();
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
//! .await
|
||||
//! .expect("request failed");
|
||||
//!
|
||||
//! println!("Bucket exists: {}", exists.exists);
|
||||
//! println!("Bucket exists: {}", exists.exists());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
@ -19,6 +19,7 @@ use crate::s3::builders::{
|
||||
};
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::a_response_traits::HasObjectSize;
|
||||
use crate::s3::response::{AppendObjectResponse, StatObjectResponse};
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::sse::Sse;
|
||||
@ -26,7 +27,6 @@ use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name};
|
||||
use http::Method;
|
||||
use std::sync::Arc;
|
||||
|
||||
// region: append-object
|
||||
|
||||
/// Argument builder for the [`AppendObject`](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-buckets-objects-append.html) S3 API operation.
|
||||
@ -234,7 +234,7 @@ impl AppendObjectContent {
|
||||
.await?;
|
||||
//println!("statObjectResponse={:#?}", resp);
|
||||
|
||||
let current_file_size = resp.size;
|
||||
let current_file_size = resp.size()?;
|
||||
|
||||
// In the first part read, if:
|
||||
//
|
||||
@ -322,7 +322,7 @@ impl AppendObjectContent {
|
||||
let resp: AppendObjectResponse = append_object.send().await?;
|
||||
//println!("AppendObjectResponse: object_size={:?}", resp.object_size);
|
||||
|
||||
next_offset_bytes = resp.object_size;
|
||||
next_offset_bytes = resp.object_size();
|
||||
|
||||
// Finally check if we are done.
|
||||
if buffer_size < part_size {
|
||||
|
||||
@ -17,9 +17,11 @@ use crate::s3::Client;
|
||||
use crate::s3::client::{MAX_MULTIPART_COUNT, MAX_PART_SIZE};
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::a_response_traits::HasEtagFromBody;
|
||||
use crate::s3::response::{
|
||||
AbortMultipartUploadResponse, ComposeObjectResponse, CopyObjectInternalResponse,
|
||||
CopyObjectResponse, CreateMultipartUploadResponse, StatObjectResponse, UploadPartCopyResponse,
|
||||
AbortMultipartUploadResponse, CompleteMultipartUploadResponse, ComposeObjectResponse,
|
||||
CopyObjectInternalResponse, CopyObjectResponse, CreateMultipartUploadResponse,
|
||||
StatObjectResponse, UploadPartCopyResponse,
|
||||
};
|
||||
use crate::s3::sse::{Sse, SseCustomerKey};
|
||||
use crate::s3::types::{Directive, PartInfo, Retention, S3Api, S3Request, ToS3Request};
|
||||
@ -445,7 +447,7 @@ impl CopyObject {
|
||||
|
||||
if self.source.offset.is_some()
|
||||
|| self.source.length.is_some()
|
||||
|| stat_resp.size > MAX_PART_SIZE
|
||||
|| stat_resp.size()? > MAX_PART_SIZE
|
||||
{
|
||||
if let Some(v) = &self.metadata_directive {
|
||||
match v {
|
||||
@ -499,14 +501,8 @@ impl CopyObject {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(CopyObjectResponse {
|
||||
headers: resp.headers,
|
||||
bucket: resp.bucket,
|
||||
object: resp.object,
|
||||
region: resp.region,
|
||||
etag: resp.etag,
|
||||
version_id: resp.version_id,
|
||||
})
|
||||
let resp: CopyObjectResponse = resp; // retype to CopyObjectResponse
|
||||
Ok(resp)
|
||||
} else {
|
||||
let resp: CopyObjectInternalResponse = self
|
||||
.client
|
||||
@ -526,14 +522,8 @@ impl CopyObject {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(CopyObjectResponse {
|
||||
headers: resp.headers,
|
||||
bucket: resp.bucket,
|
||||
object: resp.object,
|
||||
region: resp.region,
|
||||
etag: resp.etag,
|
||||
version_id: resp.version_id,
|
||||
})
|
||||
let resp: CopyObjectResponse = resp; // retype to CopyObjectResponse
|
||||
Ok(resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -652,17 +642,9 @@ impl ComposeObjectInternal {
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
|
||||
(
|
||||
Ok(ComposeObjectResponse {
|
||||
headers: resp.headers,
|
||||
bucket: resp.bucket,
|
||||
object: resp.object,
|
||||
region: resp.region,
|
||||
etag: resp.etag,
|
||||
version_id: resp.version_id,
|
||||
}),
|
||||
upload_id,
|
||||
)
|
||||
let resp: ComposeObjectResponse = resp; // retype to ComposeObjectResponse
|
||||
|
||||
(Ok(resp), upload_id)
|
||||
} else {
|
||||
let headers: Multimap = into_headers_copy_object(
|
||||
self.extra_headers,
|
||||
@ -687,7 +669,11 @@ impl ComposeObjectInternal {
|
||||
};
|
||||
|
||||
// the multipart upload was successful: update the upload_id
|
||||
upload_id.push_str(&cmu.upload_id);
|
||||
let upload_id_cmu: String = match cmu.upload_id().await {
|
||||
Ok(v) => v,
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
upload_id.push_str(&upload_id_cmu);
|
||||
|
||||
let mut part_number = 0_u16;
|
||||
let ssec_headers: Multimap = match self.sse {
|
||||
@ -739,9 +725,14 @@ impl ComposeObjectInternal {
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
|
||||
let etag = match resp.etag() {
|
||||
Ok(v) => v,
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
|
||||
parts.push(PartInfo {
|
||||
number: part_number,
|
||||
etag: resp.etag,
|
||||
etag,
|
||||
size,
|
||||
});
|
||||
} else {
|
||||
@ -773,9 +764,14 @@ impl ComposeObjectInternal {
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
|
||||
let etag = match resp.etag() {
|
||||
Ok(v) => v,
|
||||
Err(e) => return (Err(e), upload_id),
|
||||
};
|
||||
|
||||
parts.push(PartInfo {
|
||||
number: part_number,
|
||||
etag: resp.etag,
|
||||
etag,
|
||||
size,
|
||||
});
|
||||
|
||||
@ -785,7 +781,7 @@ impl ComposeObjectInternal {
|
||||
}
|
||||
}
|
||||
|
||||
let resp = self
|
||||
let resp: Result<CompleteMultipartUploadResponse, Error> = self
|
||||
.client
|
||||
.complete_multipart_upload(&self.bucket, &self.object, &upload_id, parts)
|
||||
.region(self.region)
|
||||
@ -793,17 +789,14 @@ impl ComposeObjectInternal {
|
||||
.await;
|
||||
|
||||
match resp {
|
||||
Ok(v) => (
|
||||
Ok(ComposeObjectResponse {
|
||||
Ok(v) => {
|
||||
let resp = ComposeObjectResponse {
|
||||
request: v.request,
|
||||
headers: v.headers,
|
||||
bucket: v.bucket,
|
||||
object: v.object,
|
||||
region: v.region,
|
||||
etag: v.etag,
|
||||
version_id: v.version_id,
|
||||
}),
|
||||
upload_id,
|
||||
),
|
||||
body: v.body,
|
||||
};
|
||||
(Ok(resp), upload_id)
|
||||
}
|
||||
Err(e) => (Err(e), upload_id),
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,7 +241,7 @@ impl ToS3Request for DeleteObjects {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut data = String::from("<Delete>");
|
||||
let mut data: String = String::from("<Delete>");
|
||||
if !self.verbose_mode {
|
||||
data.push_str("<Quiet>true</Quiet>");
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
use super::ObjectContent;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::a_response_traits::HasEtagFromHeaders;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::utils::{check_object_name, insert};
|
||||
use crate::s3::{
|
||||
@ -665,7 +666,7 @@ impl PutObjectContent {
|
||||
{
|
||||
let size = seg_bytes.len() as u64;
|
||||
|
||||
let res: PutObjectResponse = PutObject(UploadPart {
|
||||
let resp: PutObjectResponse = PutObject(UploadPart {
|
||||
client: self.client.clone(),
|
||||
extra_headers: self.extra_headers.clone(),
|
||||
extra_query_params: self.extra_query_params.clone(),
|
||||
@ -685,15 +686,7 @@ impl PutObjectContent {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(PutObjectContentResponse {
|
||||
headers: res.headers,
|
||||
bucket: res.bucket,
|
||||
object: res.object,
|
||||
region: res.region,
|
||||
object_size: size,
|
||||
etag: res.etag,
|
||||
version_id: res.version_id,
|
||||
})
|
||||
Ok(PutObjectContentResponse::new(resp, size))
|
||||
} else if object_size.is_known() && (seg_bytes.len() as u64) < part_size {
|
||||
// Not enough data!
|
||||
let expected: u64 = object_size.as_u64().unwrap();
|
||||
@ -722,19 +715,15 @@ impl PutObjectContent {
|
||||
.await?;
|
||||
|
||||
let client = self.client.clone();
|
||||
let upload_id: String = create_mpu_resp.upload_id().await?;
|
||||
|
||||
let mpu_res = self
|
||||
.send_mpu(
|
||||
part_size,
|
||||
create_mpu_resp.upload_id.clone(),
|
||||
object_size,
|
||||
seg_bytes,
|
||||
)
|
||||
.send_mpu(part_size, upload_id.clone(), object_size, seg_bytes)
|
||||
.await;
|
||||
|
||||
if mpu_res.is_err() {
|
||||
// If we failed to complete the multipart upload, we should abort it.
|
||||
let _ =
|
||||
AbortMultipartUpload::new(client, bucket, object, create_mpu_resp.upload_id)
|
||||
let _ = AbortMultipartUpload::new(client, bucket, object, upload_id)
|
||||
.send()
|
||||
.await;
|
||||
}
|
||||
@ -815,7 +804,7 @@ impl PutObjectContent {
|
||||
|
||||
parts.push(PartInfo {
|
||||
number: part_number,
|
||||
etag: resp.etag,
|
||||
etag: resp.etag()?,
|
||||
size: buffer_size,
|
||||
});
|
||||
|
||||
@ -835,7 +824,7 @@ impl PutObjectContent {
|
||||
}
|
||||
}
|
||||
|
||||
let res: CompleteMultipartUploadResponse = CompleteMultipartUpload {
|
||||
let resp: CompleteMultipartUploadResponse = CompleteMultipartUpload {
|
||||
client: self.client,
|
||||
extra_headers: self.extra_headers,
|
||||
extra_query_params: self.extra_query_params,
|
||||
@ -848,15 +837,7 @@ impl PutObjectContent {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(PutObjectContentResponse {
|
||||
headers: res.headers,
|
||||
bucket: res.bucket,
|
||||
object: res.object,
|
||||
region: res.region,
|
||||
object_size: size,
|
||||
etag: res.etag,
|
||||
version_id: res.version_id,
|
||||
})
|
||||
Ok(PutObjectContentResponse::new(resp, size))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,16 +21,17 @@ use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use crate::s3::builders::{BucketExists, ComposeSource};
|
||||
use crate::s3::creds::Provider;
|
||||
use crate::s3::error::{Error, ErrorCode, ErrorResponse};
|
||||
use crate::s3::http::BaseUrl;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::a_response_traits::{HasEtagFromHeaders, HasS3Fields};
|
||||
use crate::s3::response::*;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::signer::sign_v4_s3;
|
||||
use crate::s3::utils::{EMPTY_SHA256, sha256_hash_sb, to_amz_date, utc_now};
|
||||
|
||||
use crate::s3::builders::{BucketExists, ComposeSource};
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use bytes::Bytes;
|
||||
use dashmap::DashMap;
|
||||
use http::HeaderMap;
|
||||
@ -280,7 +281,7 @@ impl Client {
|
||||
|
||||
let express = match BucketExists::new(self.clone(), bucket_name).send().await {
|
||||
Ok(v) => {
|
||||
if let Some(server) = v.headers.get("server") {
|
||||
if let Some(server) = v.headers().get("server") {
|
||||
if let Ok(s) = server.to_str() {
|
||||
s.eq_ignore_ascii_case("MinIO Enterprise/S3Express")
|
||||
} else {
|
||||
@ -300,7 +301,6 @@ impl Client {
|
||||
express
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a bucket-region pair to the region cache if it does not exist.
|
||||
pub(crate) fn add_bucket_region(&mut self, bucket: &str, region: impl Into<String>) {
|
||||
self.shared
|
||||
@ -360,9 +360,9 @@ impl Client {
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
source.build_headers(stat_resp.size, stat_resp.etag)?;
|
||||
let mut size = stat_resp.size()?;
|
||||
source.build_headers(size, stat_resp.etag()?)?;
|
||||
|
||||
let mut size = stat_resp.size;
|
||||
if let Some(l) = source.length {
|
||||
size = l;
|
||||
} else if let Some(o) = source.offset {
|
||||
@ -493,15 +493,12 @@ impl Client {
|
||||
// Sort headers alphabetically by name
|
||||
header_strings.sort();
|
||||
|
||||
let body_str: String =
|
||||
String::from_utf8(body.unwrap_or(&SegmentedBytes::new()).to_bytes().to_vec())?;
|
||||
|
||||
println!(
|
||||
"S3 request: {} url={:?}; headers={:?}; body={}\n",
|
||||
method,
|
||||
url.path,
|
||||
header_strings.join("; "),
|
||||
body_str
|
||||
body.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ impl Client {
|
||||
/// use minio::s3::response::{AppendObjectResponse, PutObjectResponse};
|
||||
/// use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasObjectSize;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -49,7 +50,7 @@ impl Client {
|
||||
/// let resp: AppendObjectResponse = client
|
||||
/// .append_object("bucket-name", "object-name", data2, offset_bytes)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("size of the final object is {} bytes", resp.object_size);
|
||||
/// println!("size of the final object is {} bytes", resp.object_size());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn append_object<S1: Into<String>, S2: Into<String>>(
|
||||
@ -85,6 +86,7 @@ impl Client {
|
||||
/// use minio::s3::builders::ObjectContent;
|
||||
/// use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasObjectSize;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -97,7 +99,7 @@ impl Client {
|
||||
/// let resp: AppendObjectResponse = client
|
||||
/// .append_object_content("bucket-name", "object-name", content2)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("size of the final object is {} bytes", resp.object_size);
|
||||
/// println!("size of the final object is {} bytes", resp.object_size());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn append_object_content<S1: Into<String>, S2: Into<String>, C: Into<ObjectContent>>(
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::BucketExistsResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: BucketExistsResponse = client
|
||||
/// .bucket_exists("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("bucket '{}' exists: {}", resp.bucket, resp.exists);
|
||||
/// println!("bucket '{}' exists: {}", resp.bucket(), resp.exists());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn bucket_exists<S: Into<String>>(&self, bucket: S) -> BucketExists {
|
||||
|
||||
@ -33,6 +33,7 @@ impl Client {
|
||||
/// use minio::s3::response::{UploadPartCopyResponse};
|
||||
/// use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -42,7 +43,7 @@ impl Client {
|
||||
/// let resp: UploadPartCopyResponse = client
|
||||
/// .upload_part_copy("bucket-name", "object-name", "TODO")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("uploaded {}", resp.object);
|
||||
/// println!("uploaded {}", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn upload_part_copy<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::CreateBucketResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasRegion};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: CreateBucketResponse = client
|
||||
/// .create_bucket("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("Made bucket '{}' in region '{}'", resp.bucket, resp.region);
|
||||
/// println!("Made bucket '{}' in region '{}'", resp.bucket(), resp.region());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create_bucket<S: Into<String>>(&self, bucket: S) -> CreateBucket {
|
||||
|
||||
@ -21,6 +21,7 @@ use crate::s3::response::{
|
||||
DeleteBucketResponse, DeleteObjectResponse, DeleteObjectsResponse, PutObjectLegalHoldResponse,
|
||||
};
|
||||
use crate::s3::types::{S3Api, ToStream};
|
||||
use bytes::Bytes;
|
||||
use futures::StreamExt;
|
||||
|
||||
impl Client {
|
||||
@ -35,13 +36,14 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasRegion};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketResponse =
|
||||
/// client.delete_bucket("bucket-name").send().await.unwrap();
|
||||
/// println!("bucket '{}' in region '{}' is removed", resp.bucket, resp.region);
|
||||
/// println!("bucket '{}' in region '{}' is removed", resp.bucket(), resp.region());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket<S: Into<String>>(&self, bucket: S) -> DeleteBucket {
|
||||
@ -90,7 +92,7 @@ impl Client {
|
||||
|
||||
while let Some(item) = resp.next().await {
|
||||
let resp: DeleteObjectsResponse = item?;
|
||||
for obj in resp.result.into_iter() {
|
||||
for obj in resp.result()?.into_iter() {
|
||||
match obj {
|
||||
DeleteResult::Deleted(_) => {}
|
||||
DeleteResult::Error(v) => {
|
||||
@ -115,14 +117,15 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.delete_bucket(bucket).send().await {
|
||||
let request: DeleteBucket = self.delete_bucket(bucket);
|
||||
match request.send().await {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(Error::S3Error(e)) => {
|
||||
if e.code == ErrorCode::NoSuchBucket {
|
||||
Ok(DeleteBucketResponse {
|
||||
request: Default::default(), //TODO consider how to handle this
|
||||
body: Bytes::new(),
|
||||
headers: e.headers,
|
||||
bucket: e.bucket_name,
|
||||
region: String::new(),
|
||||
})
|
||||
} else {
|
||||
Err(Error::S3Error(e))
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketEncryptionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketEncryptionResponse = client
|
||||
/// .delete_bucket_encryption("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_encryption<S: Into<String>>(&self, bucket: S) -> DeleteBucketEncryption {
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketLifecycleResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketLifecycleResponse = client
|
||||
/// .delete_bucket_lifecycle("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("lifecycle of bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("lifecycle of bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_lifecycle<S: Into<String>>(&self, bucket: S) -> DeleteBucketLifecycle {
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketNotificationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketNotificationResponse = client
|
||||
/// .delete_bucket_notification("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("notification of bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("notification of bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_notification<S: Into<String>>(
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketPolicyResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketPolicyResponse = client
|
||||
/// .delete_bucket_policy("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("policy of bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("policy of bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_policy<S: Into<String>>(&self, bucket: S) -> DeleteBucketPolicy {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketReplicationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketReplicationResponse = client
|
||||
/// .delete_bucket_replication("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("replication of bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("replication of bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_replication<S: Into<String>>(&self, bucket: S) -> DeleteBucketReplication {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: DeleteBucketTaggingResponse = client
|
||||
/// .delete_bucket_tagging("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("tags of bucket '{}' are deleted", resp.bucket);
|
||||
/// println!("tags of bucket '{}' are deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_tagging<S: Into<String>>(&self, bucket: S) -> DeleteBucketTagging {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::{DeleteObjectLockConfigResponse, CreateBucketResponse, PutObjectLockConfigResponse};
|
||||
/// use minio::s3::types::{S3Api, ObjectLockConfig, RetentionMode};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -38,18 +39,19 @@ impl Client {
|
||||
///
|
||||
/// let resp: CreateBucketResponse =
|
||||
/// client.create_bucket(bucket_name).object_lock(true).send().await.unwrap();
|
||||
/// println!("created bucket '{}' with object locking enabled", resp.bucket);
|
||||
/// println!("created bucket '{}' with object locking enabled", resp.bucket());
|
||||
///
|
||||
///
|
||||
/// const DURATION_DAYS: i32 = 7;
|
||||
/// let config = ObjectLockConfig::new(RetentionMode::GOVERNANCE, Some(DURATION_DAYS), None).unwrap();
|
||||
///
|
||||
/// let resp: PutObjectLockConfigResponse =
|
||||
/// client.put_object_lock_config(bucket_name).config(config).send().await.unwrap();
|
||||
/// println!("configured object locking for bucket '{}'", resp.bucket);
|
||||
/// println!("configured object locking for bucket '{}'", resp.bucket());
|
||||
///
|
||||
/// let resp: DeleteObjectLockConfigResponse =
|
||||
/// client.delete_object_lock_config(bucket_name).send().await.unwrap();
|
||||
/// println!("object locking of bucket '{}' is deleted", resp.bucket);
|
||||
/// println!("object locking of bucket '{}' is deleted", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_object_lock_config<S: Into<String>>(&self, bucket: S) -> DeleteObjectLockConfig {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteObjectTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasObject};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: DeleteObjectTaggingResponse = client
|
||||
/// .delete_object_tagging("bucket-name", "object_name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is deleted", resp.object, resp.bucket);
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is deleted", resp.object(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_object_tagging<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -33,6 +33,7 @@ impl Client {
|
||||
/// use minio::s3::response::DeleteObjectResponse;
|
||||
/// use minio::s3::builders::ObjectToDelete;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasVersion;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -40,7 +41,7 @@ impl Client {
|
||||
/// let resp: DeleteObjectResponse = client
|
||||
/// .delete_object("bucket-name", ObjectToDelete::from("object-name"))
|
||||
/// .send().await.unwrap();
|
||||
/// println!("the object is deleted. The delete marker has version '{:?}'", resp.version_id);
|
||||
/// println!("the object is deleted. The delete marker has version '{:?}'", resp.version_id());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_object<S: Into<String>, D: Into<ObjectToDelete>>(
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketEncryptionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: GetBucketEncryptionResponse = client
|
||||
/// .get_bucket_encryption("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved SseConfig '{:?}' from bucket '{}'", resp.config, resp.bucket);
|
||||
/// println!("retrieved SseConfig '{:?}' from bucket '{}'", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_encryption<S: Into<String>>(&self, bucket: S) -> GetBucketEncryption {
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketLifecycleResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: GetBucketLifecycleResponse = client
|
||||
/// .get_bucket_lifecycle("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved bucket lifecycle config '{:?}' from bucket '{}'", resp.config, resp.bucket);
|
||||
/// println!("retrieved bucket lifecycle config '{:?}' from bucket '{}'", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_lifecycle<S: Into<String>>(&self, bucket: S) -> GetBucketLifecycle {
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketNotificationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: GetBucketNotificationResponse = client
|
||||
/// .get_bucket_notification("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved bucket notification config '{:?}' from bucket '{}'", resp.config, resp.bucket);
|
||||
/// println!("retrieved bucket notification config '{:?}' from bucket '{}'", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_notification<S: Into<String>>(&self, bucket: S) -> GetBucketNotification {
|
||||
|
||||
@ -28,6 +28,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketPolicyResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -35,7 +36,7 @@ impl Client {
|
||||
/// let resp: GetBucketPolicyResponse = client
|
||||
/// .get_bucket_policy("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved bucket policy config '{:?}' from bucket '{}'", resp.config, resp.bucket);
|
||||
/// println!("retrieved bucket policy config '{:?}' from bucket '{}'", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_policy<S: Into<String>>(&self, bucket: S) -> GetBucketPolicy {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketReplicationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetBucketReplicationResponse = client
|
||||
/// .get_bucket_replication("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved bucket replication config '{:?}' from bucket '{}'", resp.config, resp.bucket);
|
||||
/// println!("retrieved bucket replication config '{:?}' from bucket '{}'", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_replication<S: Into<String>>(&self, bucket: S) -> GetBucketReplication {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasTagging};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetBucketTaggingResponse = client
|
||||
/// .get_bucket_tagging("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved bucket tags '{:?}' from bucket '{}'", resp.tags, resp.bucket);
|
||||
/// println!("retrieved bucket tags '{:?}' from bucket '{}'", resp.tags(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_tagging<S: Into<String>>(&self, bucket: S) -> GetBucketTagging {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketVersioningResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetBucketVersioningResponse = client
|
||||
/// .get_bucket_versioning("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved versioning status '{:?}' from bucket '{}'", resp.status, resp.bucket);
|
||||
/// println!("retrieved versioning status '{:?}' from bucket '{}'", resp.status(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_versioning<S: Into<String>>(&self, bucket: S) -> GetBucketVersioning {
|
||||
|
||||
@ -38,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetObjectResponse = client
|
||||
/// .get_object("bucket-name", "object-name")
|
||||
/// .send().await.unwrap();
|
||||
/// let content_bytes = resp.content.to_segmented_bytes().await.unwrap().to_bytes();
|
||||
/// let content_bytes = resp.content().unwrap().to_segmented_bytes().await.unwrap().to_bytes();
|
||||
/// let content_str = String::from_utf8(content_bytes.to_vec()).unwrap();
|
||||
/// println!("retrieved content '{content_str}'");
|
||||
/// }
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetObjectLegalHoldResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasObject};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetObjectLegalHoldResponse = client
|
||||
/// .get_object_legal_hold("bucket-name", "object-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is enabled: {}", resp.object, resp.bucket, resp.enabled);
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is enabled: {:?}", resp.object(), resp.bucket(), resp.enabled());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object_legal_hold<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetObjectLockConfigResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetObjectLockConfigResponse = client
|
||||
/// .get_object_lock_config("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved object lock config '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// println!("retrieved object lock config '{:?}' from bucket '{}' is enabled", resp.config(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object_lock_config<S: Into<String>>(&self, bucket: S) -> GetObjectLockConfig {
|
||||
|
||||
@ -36,7 +36,7 @@ impl Client {
|
||||
/// let resp: GetObjectPromptResponse = client
|
||||
/// .get_object_prompt("bucket-name", "object-name", "What is it about?")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("the prompt response is: '{}'", resp.prompt_response);
|
||||
/// println!("the prompt response is: '{:?}'", resp.prompt_response());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object_prompt<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetObjectRetentionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetObjectRetentionResponse = client
|
||||
/// .get_object_retention("bucket-name", "object-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved retention mode '{:?}' until '{:?}' from bucket '{}' is enabled", resp.retention_mode, resp.retain_until_date, resp.bucket);
|
||||
/// println!("retrieved retention mode '{:?}' until '{:?}' from bucket '{}' is enabled", resp.retention_mode(), resp.retain_until_date(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object_retention<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetObjectTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasBucket, HasObject, HasTagging};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetObjectTaggingResponse = client
|
||||
/// .get_object_tagging("bucket-name", "object-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved object tags '{:?}' from object '{}' in bucket '{}' is enabled", resp.tags, resp.object, resp.bucket);
|
||||
/// println!("retrieved object tags '{:?}' from object '{}' in bucket '{}' is enabled", resp.tags(), resp.object(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object_tagging<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
use super::{Client, DEFAULT_REGION};
|
||||
use crate::s3::builders::GetRegion;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetRegionResponse;
|
||||
use crate::s3::types::S3Api;
|
||||
|
||||
impl Client {
|
||||
@ -31,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetRegionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -38,7 +38,7 @@ impl Client {
|
||||
/// let resp: GetRegionResponse = client
|
||||
/// .get_region("bucket-name")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved region '{:?}' for bucket '{}'", resp.region_response, resp.bucket);
|
||||
/// println!("retrieved region '{:?}' for bucket '{}'", resp.region_response(), resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_region<S: Into<String>>(&self, bucket: S) -> GetRegion {
|
||||
@ -82,18 +82,20 @@ impl Client {
|
||||
return Ok(v.value().clone());
|
||||
}
|
||||
|
||||
// Otherwise, fetch the region and cache it
|
||||
let resp: GetRegionResponse = self.get_region(&bucket).send().await?;
|
||||
|
||||
let resolved_region: String = if resp.region_response.is_empty() {
|
||||
DEFAULT_REGION.to_owned()
|
||||
// Otherwise, fetch the region from the server and cache it
|
||||
let resolved_region: String = {
|
||||
let region = self.get_region(&bucket).send().await?.region_response()?;
|
||||
if !region.is_empty() {
|
||||
region
|
||||
} else {
|
||||
resp.region_response
|
||||
DEFAULT_REGION.to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
self.shared
|
||||
.region_map
|
||||
.insert(bucket, resolved_region.clone());
|
||||
|
||||
Ok(resolved_region)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ impl Client {
|
||||
/// let resp: ListBucketsResponse = client
|
||||
/// .list_buckets()
|
||||
/// .send().await.unwrap();
|
||||
/// println!("retrieved buckets '{:?}'", resp.buckets);
|
||||
/// println!("retrieved buckets '{:?}'", resp.buckets());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn list_buckets(&self) -> ListBuckets {
|
||||
|
||||
@ -31,6 +31,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::PutBucketEncryptionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -40,7 +41,7 @@ impl Client {
|
||||
/// .put_bucket_encryption("bucket-name")
|
||||
/// .sse_config(config)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set encryption on bucket '{}'", resp.bucket);
|
||||
/// println!("set encryption on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_encryption<S: Into<String>>(&self, bucket: S) -> PutBucketEncryption {
|
||||
|
||||
@ -31,11 +31,11 @@ impl Client {
|
||||
/// use minio::s3::response::PutBucketLifecycleResponse;
|
||||
/// use minio::s3::types::{Filter, S3Api};
|
||||
/// use minio::s3::lifecycle_config::{LifecycleRule, LifecycleConfig};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
///
|
||||
/// let rules: Vec<LifecycleRule> = vec![LifecycleRule {
|
||||
/// id: String::from("rule1"),
|
||||
/// filter: Filter {and_operator: None, prefix: Some(String::from("logs/")), tag: None},
|
||||
@ -48,7 +48,7 @@ impl Client {
|
||||
/// .put_bucket_lifecycle("bucket-name")
|
||||
/// .life_cycle_config(LifecycleConfig { rules })
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set bucket replication policy on bucket '{}'", resp.bucket);
|
||||
/// println!("set bucket replication policy on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_lifecycle<S: Into<String>>(&self, bucket: S) -> PutBucketLifecycle {
|
||||
|
||||
@ -28,11 +28,11 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::types::{NotificationConfig, PrefixFilterRule, QueueConfig, S3Api, SuffixFilterRule};
|
||||
/// use minio::s3::response::PutBucketNotificationResponse;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
///
|
||||
/// let config = NotificationConfig {
|
||||
/// cloud_func_config_list: None,
|
||||
/// queue_config_list: Some(vec![QueueConfig {
|
||||
@ -56,7 +56,7 @@ impl Client {
|
||||
/// .put_bucket_notification("bucket-name")
|
||||
/// .notification_config(config)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set bucket notification for bucket '{:?}'", resp.bucket);
|
||||
/// println!("set bucket notification for bucket '{:?}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_notification<S: Into<String>>(&self, bucket: S) -> PutBucketNotification {
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::builders::VersioningStatus;
|
||||
/// use minio::s3::response::PutBucketPolicyResponse;
|
||||
/// use minio::s3::types::{S3Api, AndOperator, Destination, Filter, ReplicationConfig, ReplicationRule};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -63,7 +64,7 @@ impl Client {
|
||||
/// .put_bucket_policy("bucket-name")
|
||||
/// .config(config.to_owned())
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set bucket replication policy on bucket '{}'", resp.bucket);
|
||||
/// println!("set bucket replication policy on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_policy<S: Into<String>>(&self, bucket: S) -> PutBucketPolicy {
|
||||
|
||||
@ -31,6 +31,7 @@ impl Client {
|
||||
/// use minio::s3::builders::VersioningStatus;
|
||||
/// use minio::s3::response::PutBucketReplicationResponse;
|
||||
/// use minio::s3::types::{S3Api, AndOperator, Destination, Filter, ReplicationConfig, ReplicationRule};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
@ -75,7 +76,7 @@ impl Client {
|
||||
/// .put_bucket_replication("bucket-name")
|
||||
/// .replication_config(ReplicationConfig {role: None, rules})
|
||||
/// .send().await.unwrap();
|
||||
/// println!("enabled versioning on bucket '{}'", resp.bucket);
|
||||
/// println!("enabled versioning on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_replication<S: Into<String>>(&self, bucket: S) -> PutBucketReplication {
|
||||
|
||||
@ -31,6 +31,7 @@ impl Client {
|
||||
/// use minio::s3::builders::VersioningStatus;
|
||||
/// use minio::s3::response::PutBucketTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
@ -46,7 +47,7 @@ impl Client {
|
||||
/// .put_bucket_tagging("bucket-name")
|
||||
/// .tags(tags)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set tags on bucket '{}'", resp.bucket);
|
||||
/// println!("set tags on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_tagging<S: Into<String>>(&self, bucket: S) -> PutBucketTagging {
|
||||
|
||||
@ -31,6 +31,7 @@ impl Client {
|
||||
/// use minio::s3::builders::VersioningStatus;
|
||||
/// use minio::s3::response::PutBucketVersioningResponse;
|
||||
/// use minio::s3::types::{S3Api, ObjectLockConfig, RetentionMode};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -40,7 +41,7 @@ impl Client {
|
||||
/// .put_bucket_versioning("bucket-name")
|
||||
/// .versioning_status(VersioningStatus::Enabled)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("enabled versioning on bucket '{}'", resp.bucket);
|
||||
/// println!("enabled versioning on bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_bucket_versioning<S: Into<String>>(&self, bucket: S) -> PutBucketVersioning {
|
||||
|
||||
@ -29,7 +29,11 @@ impl Client {
|
||||
///
|
||||
/// For handling large files requiring multipart upload, see [`create_multipart_upload`](#method.create_multipart_upload).
|
||||
///
|
||||
/// To execute the request, call [`PutObject::send()`](crate::s3::types::S3Api::send),
|
||||
/// For handling large files requiring multipart upload, see [`create_multipart_upload`](#method.create_multipart_upload).
|
||||
///
|
||||
/// For handling large files requiring multipart upload, see [`create_multipart_upload`](#method.create_multipart_upload).
|
||||
///
|
||||
/// To execute the request, call [`PutObjects::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`PutObjectResponse`](crate::s3::response::PutObjectResponse).
|
||||
///
|
||||
/// For more information, refer to the [AWS S3 PutObject API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html).
|
||||
@ -41,6 +45,7 @@ impl Client {
|
||||
/// use minio::s3::response::PutObjectResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -48,7 +53,7 @@ impl Client {
|
||||
/// let data = SegmentedBytes::from("Hello world".to_string());
|
||||
/// let resp: PutObjectResponse =
|
||||
/// client.put_object("bucket-name", "object-name", data).send().await.unwrap();
|
||||
/// println!("successfully put object '{}'", resp.object);
|
||||
/// println!("successfully put object '{}'", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object<S1: Into<String>, S2: Into<String>>(
|
||||
@ -80,7 +85,7 @@ impl Client {
|
||||
/// let resp: CreateMultipartUploadResponse = client
|
||||
/// .create_multipart_upload("bucket-name", "large-object")
|
||||
/// .send().await.unwrap();
|
||||
/// println!("Initiated multipart upload with UploadId '{}'", resp.upload_id);
|
||||
/// println!("Initiated multipart upload with UploadId '{:?}'", resp.upload_id().await);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create_multipart_upload<S1: Into<String>, S2: Into<String>>(
|
||||
@ -135,8 +140,8 @@ impl Client {
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::CompleteMultipartUploadResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::types::PartInfo;
|
||||
/// use minio::s3::types::{S3Api, PartInfo};
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -145,7 +150,7 @@ impl Client {
|
||||
/// let resp: CompleteMultipartUploadResponse = client
|
||||
/// .complete_multipart_upload("bucket-name", "object-name", "upload-id-123", parts)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("Completed multipart upload for '{}'", resp.object);
|
||||
/// println!("Completed multipart upload for '{}'", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn complete_multipart_upload<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
@ -178,6 +183,7 @@ impl Client {
|
||||
/// use minio::s3::response::UploadPartResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -186,7 +192,7 @@ impl Client {
|
||||
/// let resp: UploadPartResponse = client
|
||||
/// .upload_part("bucket-name", "object-name", "upload-id", 1, data)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("Uploaded object: {}", resp.object);
|
||||
/// println!("Uploaded object: {}", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn upload_part<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
@ -220,6 +226,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::PutObjectContentResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::{HasObject, HasEtagFromHeaders};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -228,7 +235,7 @@ impl Client {
|
||||
/// let resp: PutObjectContentResponse = client
|
||||
/// .put_object_content("bucket", "object", content)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("Uploaded object '{}' with ETag '{}'", resp.object, resp.etag);
|
||||
/// println!("Uploaded object '{}' with ETag '{:?}'", resp.object(), resp.etag());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object_content<S1: Into<String>, S2: Into<String>, C: Into<ObjectContent>>(
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::PutObjectLegalHoldResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -37,7 +38,7 @@ impl Client {
|
||||
/// let resp: PutObjectLegalHoldResponse = client
|
||||
/// .put_object_legal_hold("bucket-name", "object-name", true)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("legal hold of bucket '{}' is enabled", resp.bucket);
|
||||
/// println!("legal hold of bucket '{}' is enabled", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object_legal_hold<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -30,6 +30,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::{CreateBucketResponse, PutObjectLockConfigResponse};
|
||||
/// use minio::s3::types::{S3Api, ObjectLockConfig, RetentionMode};
|
||||
/// use minio::s3::response::a_response_traits::HasBucket;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -38,14 +39,14 @@ impl Client {
|
||||
///
|
||||
/// let resp: CreateBucketResponse =
|
||||
/// client.create_bucket(bucket_name).object_lock(true).send().await.unwrap();
|
||||
/// println!("created bucket '{}' with object locking enabled", resp.bucket);
|
||||
/// println!("created bucket '{}' with object locking enabled", resp.bucket());
|
||||
///
|
||||
/// const DURATION_DAYS: i32 = 7;
|
||||
/// let config = ObjectLockConfig::new(RetentionMode::GOVERNANCE, Some(DURATION_DAYS), None).unwrap();
|
||||
///
|
||||
/// let resp: PutObjectLockConfigResponse =
|
||||
/// client.put_object_lock_config(bucket_name).config(config).send().await.unwrap();
|
||||
/// println!("configured object locking for bucket '{}'", resp.bucket);
|
||||
/// println!("configured object locking for bucket '{}'", resp.bucket());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object_lock_config<S: Into<String>>(&self, bucket: S) -> PutObjectLockConfig {
|
||||
|
||||
@ -32,6 +32,7 @@ impl Client {
|
||||
/// use minio::s3::builders::ObjectToDelete;
|
||||
/// use minio::s3::types::{S3Api, RetentionMode};
|
||||
/// use minio::s3::utils::utc_now;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -42,7 +43,7 @@ impl Client {
|
||||
/// .retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||
/// .retain_until_date(Some(retain_until_date))
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set the object retention for object '{}'", resp.object);
|
||||
/// println!("set the object retention for object '{}'", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object_retention<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -31,6 +31,7 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::PutObjectTaggingResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
@ -43,7 +44,7 @@ impl Client {
|
||||
/// .put_object_tagging("bucket-name", "object-name")
|
||||
/// .tags(tags)
|
||||
/// .send().await.unwrap();
|
||||
/// println!("set the object tags for object '{}'", resp.object);
|
||||
/// println!("set the object tags for object '{}'", resp.object());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn put_object_tagging<S: Into<String>>(&self, bucket: S, object: S) -> PutObjectTagging {
|
||||
|
||||
@ -28,13 +28,14 @@ impl Client {
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::StatObjectResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
/// use minio::s3::response::a_response_traits::HasObject;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: StatObjectResponse =
|
||||
/// client.stat_object("bucket-name", "object-name").send().await.unwrap();
|
||||
/// println!("stat of object '{}' are {:#?}", resp.object, resp);
|
||||
/// println!("stat of object '{}' are {:#?}", resp.object(), resp);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn stat_object<S1: Into<String>, S2: Into<String>>(
|
||||
|
||||
@ -129,12 +129,7 @@ pub enum Error {
|
||||
StrError(reqwest::header::ToStrError),
|
||||
IntError(std::num::ParseIntError),
|
||||
BoolError(std::str::ParseBoolError),
|
||||
|
||||
Utf8Error(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
/// Occurs when converting Vec<u8> to String (e.g. String::from_utf8)
|
||||
//FromUtf8Error(alloc::string::FromUtf8Error),
|
||||
/// Occurs when converting &[u8] to &str (e.g. std::str::from_utf8)
|
||||
//Utf8Error(std::str::Utf8Error),
|
||||
JsonError(serde_json::Error),
|
||||
XmlError(String),
|
||||
InvalidBaseUrl(String),
|
||||
@ -203,7 +198,6 @@ impl fmt::Display for Error {
|
||||
Error::IntError(e) => write!(f, "{e}"),
|
||||
Error::BoolError(e) => write!(f, "{e}"),
|
||||
Error::Utf8Error(e) => write!(f, "{e}"),
|
||||
//Error::FromUtf8Error(e) => write!(f, "{e}"),
|
||||
Error::JsonError(e) => write!(f, "{e}"),
|
||||
Error::XmlError(m) => write!(f, "{m}"),
|
||||
Error::InvalidBucketName(m) => write!(f, "{m}"),
|
||||
|
||||
@ -60,6 +60,9 @@ mod put_object_tagging;
|
||||
mod select_object_content;
|
||||
mod stat_object;
|
||||
|
||||
#[macro_use]
|
||||
pub mod a_response_traits;
|
||||
|
||||
pub use append_object::AppendObjectResponse;
|
||||
pub use bucket_exists::BucketExistsResponse;
|
||||
pub use copy_object::*;
|
||||
|
||||
219
src/s3/response/a_response_traits.rs
Normal file
219
src/s3/response/a_response_traits.rs
Normal file
@ -0,0 +1,219 @@
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::types::S3Request;
|
||||
use crate::s3::utils::{get_text, trim_quotes};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::collections::HashMap;
|
||||
use xmltree::Element;
|
||||
|
||||
#[macro_export]
|
||||
/// Implements the `FromS3Response` trait for the specified types.
|
||||
macro_rules! impl_from_s3response {
|
||||
($($ty:ty),* $(,)?) => {
|
||||
$(
|
||||
#[async_trait::async_trait]
|
||||
impl FromS3Response for $ty {
|
||||
async fn from_s3response(
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp: reqwest::Response = response?;
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?
|
||||
})
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implements the `FromS3Response` trait for the specified types with an additional `object_size` field.
|
||||
macro_rules! impl_from_s3response_with_size {
|
||||
($($ty:ty),* $(,)?) => {
|
||||
$(
|
||||
#[async_trait::async_trait]
|
||||
impl FromS3Response for $ty {
|
||||
async fn from_s3response(
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp: reqwest::Response = response?;
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
object_size: 0, // Default value, can be set later
|
||||
})
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Implements the `HasS3Fields` trait for the specified types.
|
||||
macro_rules! impl_has_s3fields {
|
||||
($($ty:ty),* $(,)?) => {
|
||||
$(
|
||||
impl HasS3Fields for $ty {
|
||||
/// The request that was sent to the S3 API.
|
||||
fn request(&self) -> &S3Request {
|
||||
&self.request
|
||||
}
|
||||
|
||||
/// The response of the S3 API.
|
||||
fn headers(&self) -> &HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
/// The response of the S3 API.
|
||||
fn body(&self) -> &Bytes {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
pub trait HasS3Fields {
|
||||
/// The request that was sent to the S3 API.
|
||||
fn request(&self) -> &S3Request;
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
fn headers(&self) -> &HeaderMap;
|
||||
/// The response body returned by the server, which may contain the object data or other information.
|
||||
fn body(&self) -> &Bytes;
|
||||
}
|
||||
/// Returns the name of the S3 bucket.
|
||||
pub trait HasBucket: HasS3Fields {
|
||||
/// Returns the name of the S3 bucket.
|
||||
#[inline]
|
||||
fn bucket(&self) -> &str {
|
||||
self.request().bucket.as_deref().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
/// Returns the object key (name) of the S3 object.
|
||||
pub trait HasObject: HasS3Fields {
|
||||
/// Returns the object key (name) of the S3 object.
|
||||
#[inline]
|
||||
fn object(&self) -> &str {
|
||||
self.request().object.as_deref().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
/// Returns the region of the S3 bucket.
|
||||
pub trait HasRegion: HasS3Fields {
|
||||
/// Returns the region of the S3 bucket.
|
||||
#[inline]
|
||||
fn region(&self) -> &str {
|
||||
&self.request().inner_region
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the version ID of the object (`x-amz-version-id`), if versioning is enabled for the bucket.
|
||||
pub trait HasVersion: HasS3Fields {
|
||||
/// Returns the version ID of the object (`x-amz-version-id`), if versioning is enabled for the bucket.
|
||||
#[inline]
|
||||
fn version_id(&self) -> Option<&str> {
|
||||
self.headers()
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value of the `ETag` header from response headers (for operations that return ETag in headers).
|
||||
/// The ETag is typically a hash of the object content, but it may vary based on the storage backend.
|
||||
pub trait HasEtagFromHeaders: HasS3Fields {
|
||||
/// Returns the value of the `ETag` header from response headers (for operations that return ETag in headers).
|
||||
/// The ETag is typically a hash of the object content, but it may vary based on the storage backend.
|
||||
#[inline]
|
||||
fn etag(&self) -> Result<String, Error> {
|
||||
// Retrieve the ETag from the response headers.
|
||||
let etag = self
|
||||
.headers()
|
||||
.get("etag")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.trim_matches('"'))
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
Ok(etag)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value of the `ETag` from the response body, which is a unique identifier for
|
||||
/// the object version. The ETag is typically a hash of the object content, but it may vary
|
||||
/// based on the storage backend.
|
||||
pub trait HasEtagFromBody: HasS3Fields {
|
||||
/// Returns the value of the `ETag` from the response body, which is a unique identifier for
|
||||
/// the object version. The ETag is typically a hash of the object content, but it may vary
|
||||
/// based on the storage backend.
|
||||
fn etag(&self) -> Result<String, Error> {
|
||||
// Retrieve the ETag from the response body.
|
||||
let root = xmltree::Element::parse(self.body().clone().reader())?;
|
||||
let etag: String = get_text(&root, "ETag")?;
|
||||
Ok(trim_quotes(etag))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the size of the object in bytes, as specified by the `x-amz-object-size` header.
|
||||
pub trait HasObjectSize: HasS3Fields {
|
||||
/// Returns the size of the object in bytes, as specified by the `x-amz-object-size` header.
|
||||
#[inline]
|
||||
fn object_size(&self) -> u64 {
|
||||
self.headers()
|
||||
.get("x-amz-object-size")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|s| s.parse::<u64>().ok())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Value of the `x-amz-delete-marker` header.
|
||||
/// Indicates whether the specified object version that was permanently deleted was (true) or
|
||||
/// was not (false) a delete marker before deletion. In a simple DELETE, this header indicates
|
||||
/// whether (true) or not (false) the current version of the object is a delete marker.
|
||||
pub trait HasIsDeleteMarker: HasS3Fields {
|
||||
/// Returns `true` if the object is a delete marker, `false` otherwise.
|
||||
///
|
||||
/// Value of the `x-amz-delete-marker` header.
|
||||
/// Indicates whether the specified object version that was permanently deleted was (true) or
|
||||
/// was not (false) a delete marker before deletion. In a simple DELETE, this header indicates
|
||||
/// whether (true) or not (false) the current version of the object is a delete marker.
|
||||
#[inline]
|
||||
fn is_delete_marker(&self) -> Result<Option<bool>, Error> {
|
||||
Ok(Some(
|
||||
self.headers()
|
||||
.get("x-amz-delete-marker")
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false),
|
||||
))
|
||||
|
||||
//Ok(match self.headers().get("x-amz-delete-marker") {
|
||||
// Some(v) => Some(v.to_str()?.parse::<bool>()?),
|
||||
// None => None,
|
||||
//})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasTagging: HasS3Fields {
|
||||
/// Returns the tags associated with the bucket.
|
||||
///
|
||||
/// If the bucket has no tags, this will return an empty `HashMap`.
|
||||
#[inline]
|
||||
fn tags(&self) -> Result<HashMap<String, String>, Error> {
|
||||
let mut tags = HashMap::new();
|
||||
if self.body().is_empty() {
|
||||
// Note: body is empty when server responses with NoSuchTagSet
|
||||
return Ok(tags);
|
||||
}
|
||||
let mut root = Element::parse(self.body().clone().reader())?;
|
||||
let element = root
|
||||
.get_mut_child("TagSet")
|
||||
.ok_or(Error::XmlError("<TagSet> tag not found".to_string()))?;
|
||||
while let Some(v) = element.take_child("Tag") {
|
||||
tags.insert(get_text(&v, "Key")?, get_text(&v, "Value")?);
|
||||
}
|
||||
Ok(tags)
|
||||
}
|
||||
}
|
||||
@ -14,78 +14,30 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasEtagFromHeaders, HasObject, HasObjectSize, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the `append_object` API call.
|
||||
/// This struct contains metadata and information about the object being appended.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket containing the object.
|
||||
/// * `object` - Key (path) identifying the object within the bucket.
|
||||
/// * `etag` - Entity tag representing a specific version of the object.
|
||||
/// * `version_id` - Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
/// * `object_size` - Value of the `x-amz-object-size` header.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppendObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Entity tag representing a specific version of the object.
|
||||
pub etag: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
|
||||
/// Value of the `x-amz-object-size` header.
|
||||
pub object_size: u64,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for AppendObjectResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
impl_from_s3response!(AppendObjectResponse);
|
||||
impl_has_s3fields!(AppendObjectResponse);
|
||||
|
||||
let etag: String = match headers.get("etag") {
|
||||
Some(v) => v.to_str()?.to_string().trim_matches('"').to_string(),
|
||||
_ => String::new(),
|
||||
};
|
||||
let version_id: Option<String> = match headers.get("x-amz-version-id") {
|
||||
Some(v) => Some(v.to_str()?.to_string()),
|
||||
None => None,
|
||||
};
|
||||
let object_size: u64 = match headers.get("x-amz-object-size") {
|
||||
Some(v) => v.to_str()?.parse::<u64>()?,
|
||||
None => 0,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
region: req.inner_region,
|
||||
etag,
|
||||
version_id,
|
||||
object_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for AppendObjectResponse {}
|
||||
impl HasObject for AppendObjectResponse {}
|
||||
impl HasRegion for AppendObjectResponse {}
|
||||
impl HasVersion for AppendObjectResponse {}
|
||||
impl HasEtagFromHeaders for AppendObjectResponse {}
|
||||
impl HasObjectSize for AppendObjectResponse {}
|
||||
|
||||
@ -13,59 +13,57 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [bucket_exists()](crate::s3::client::Client::bucket_exists) API call.
|
||||
/// This struct contains metadata and information about the existence of a bucket.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides. If the bucket does not exist, this will be an empty string.
|
||||
/// * `bucket` - The name of the bucket being checked.
|
||||
/// * `exists` - A boolean indicating whether the bucket exists or not.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BucketExistsResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// The name of the bucket being checked.
|
||||
pub bucket: String,
|
||||
|
||||
/// Whether the bucket exists or not.
|
||||
pub exists: bool,
|
||||
pub(crate) exists: bool,
|
||||
}
|
||||
impl_has_s3fields!(BucketExistsResponse);
|
||||
|
||||
impl HasBucket for BucketExistsResponse {}
|
||||
impl HasRegion for BucketExistsResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for BucketExistsResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => Ok(Self {
|
||||
headers: mem::take(r.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
exists: true,
|
||||
}),
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchBucket => {
|
||||
Ok(Self {
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchBucket => Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: String::new(), // NOTE the bucket does not exist and the region is not provided
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
body: Bytes::new(),
|
||||
exists: false,
|
||||
})
|
||||
}
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BucketExistsResponse {
|
||||
/// Returns `true` if the bucket exists, `false` otherwise.
|
||||
pub fn exists(&self) -> bool {
|
||||
self.exists
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,186 +14,43 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasEtagFromBody, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_text, take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use bytes::{Buf, Bytes};
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
|
||||
/// Base response struct that contains common functionality for S3 operations
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct S3Response2 {
|
||||
pub(crate) request: S3Request,
|
||||
pub(crate) headers: HeaderMap,
|
||||
pub(crate) body: Bytes,
|
||||
}
|
||||
|
||||
impl_from_s3response!(S3Response2);
|
||||
impl_has_s3fields!(S3Response2);
|
||||
|
||||
impl HasBucket for S3Response2 {}
|
||||
impl HasObject for S3Response2 {}
|
||||
impl HasRegion for S3Response2 {}
|
||||
impl HasVersion for S3Response2 {}
|
||||
impl HasEtagFromBody for S3Response2 {}
|
||||
|
||||
/// Represents the response of the `upload_part_copy` API call.
|
||||
/// This struct contains metadata and information about the part being copied during a multipart upload.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket containing the object.
|
||||
/// * `object` - Key (path) identifying the object within the bucket.
|
||||
/// * `etag` - Entity tag representing a specific version of the object.
|
||||
/// * `version_id` - Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UploadPartCopyResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
pub type UploadPartCopyResponse = S3Response2;
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Entity tag representing a specific version of the object.
|
||||
pub etag: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for UploadPartCopyResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let etag: String = {
|
||||
let body: Bytes = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
get_text(&root, "ETag")?.trim_matches('"').to_string()
|
||||
};
|
||||
|
||||
let version_id: Option<String> = headers
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok().map(String::from));
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
etag,
|
||||
version_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CopyObjectInternalResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
pub region: String,
|
||||
pub bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub etag: String,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for CopyObjectInternalResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let bucket = req
|
||||
.bucket
|
||||
.ok_or_else(|| Error::InvalidBucketName("no bucket specified".into()))?;
|
||||
let object = req
|
||||
.object
|
||||
.ok_or_else(|| Error::InvalidObjectName("no object specified".into()))?;
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let etag: String = {
|
||||
let body: Bytes = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
get_text(&root, "ETag")?.trim_matches('"').to_string()
|
||||
};
|
||||
|
||||
let version_id: Option<String> = headers
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok().map(String::from));
|
||||
|
||||
Ok(CopyObjectInternalResponse {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket,
|
||||
object,
|
||||
etag,
|
||||
version_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
/// Internal response type for copy operations
|
||||
pub type CopyObjectInternalResponse = S3Response2;
|
||||
|
||||
/// Represents the response of the `copy_object` API call.
|
||||
/// This struct contains metadata and information about the object being copied.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket containing the object.
|
||||
/// * `object` - Key (path) identifying the object within the bucket.
|
||||
/// * `etag` - Entity tag representing a specific version of the object.
|
||||
/// * `version_id` - Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CopyObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Entity tag representing a specific version of the object.
|
||||
pub etag: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
pub type CopyObjectResponse = S3Response2;
|
||||
|
||||
/// Represents the response of the `[compose_object()](crate::s3::client::Client::compose_object) API call.
|
||||
/// This struct contains metadata and information about the composed object.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `bucket` - Name of the bucket containing the composed object.
|
||||
/// * `object` - Key (path) identifying the composed object within the bucket.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `etag` - Entity tag representing a specific version of the composed object.
|
||||
/// * `version_id` - Version ID of the composed object, if versioning is enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ComposeObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// Name of the bucket containing the composed object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the composed object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Entity tag representing a specific version of the composed object.
|
||||
pub etag: String,
|
||||
|
||||
/// Version ID of the composed object, if versioning is enabled.
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
pub type ComposeObjectResponse = S3Response2;
|
||||
|
||||
@ -13,10 +13,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -25,31 +27,36 @@ use std::mem;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CreateBucketResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(CreateBucketResponse);
|
||||
|
||||
impl HasBucket for CreateBucketResponse {}
|
||||
impl HasRegion for CreateBucketResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for CreateBucketResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut req = req;
|
||||
let bucket: String = take_bucket(req.bucket)?;
|
||||
req.client.add_bucket_region(&bucket, &req.inner_region);
|
||||
let mut resp = resp?;
|
||||
let mut resp: reqwest::Response = response?;
|
||||
|
||||
let mut request = request;
|
||||
let bucket: &str = request
|
||||
.bucket
|
||||
.as_deref()
|
||||
.ok_or_else(|| Error::InvalidBucketName("no bucket specified".into()))?;
|
||||
let region: &str = &request.inner_region;
|
||||
request.client.add_bucket_region(bucket, region);
|
||||
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket,
|
||||
body: resp.bytes().await?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,10 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -25,31 +26,34 @@ use std::mem;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
pub(crate) request: S3Request,
|
||||
pub(crate) headers: HeaderMap,
|
||||
pub(crate) body: Bytes,
|
||||
}
|
||||
impl_has_s3fields!(DeleteBucketResponse);
|
||||
|
||||
#[async_trait]
|
||||
impl HasBucket for DeleteBucketResponse {}
|
||||
impl HasRegion for DeleteBucketResponse {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl FromS3Response for DeleteBucketResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut req = req;
|
||||
let bucket: String = take_bucket(req.bucket)?;
|
||||
req.client.remove_bucket_region(&bucket);
|
||||
let mut resp = resp?;
|
||||
let mut resp: reqwest::Response = response?;
|
||||
|
||||
let mut request = request;
|
||||
let bucket: &str = request
|
||||
.bucket
|
||||
.as_deref()
|
||||
.ok_or_else(|| Error::InvalidBucketName("no bucket specified".into()))?;
|
||||
|
||||
request.client.remove_bucket_region(bucket);
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket,
|
||||
body: resp.bytes().await?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,44 +14,24 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [delete_bucket_encryption()](crate::s3::client::Client::delete_bucket_encryption) API call.
|
||||
/// This struct contains metadata and information about the bucket whose encryption configuration was removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the encryption configuration was removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketEncryptionResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Encryption configuration was removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketEncryptionResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteBucketEncryptionResponse);
|
||||
impl_has_s3fields!(DeleteBucketEncryptionResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteBucketEncryptionResponse {}
|
||||
impl HasRegion for DeleteBucketEncryptionResponse {}
|
||||
|
||||
@ -14,44 +14,24 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [delete_bucket_lifecycle()](crate::s3::client::Client::delete_bucket_lifecycle) API call.
|
||||
/// This struct contains metadata and information about the bucket whose lifecycle configuration was removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the Bucket Lifecycle configuration was removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketLifecycleResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Bucket Lifecycle configuration was removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketLifecycleResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteBucketLifecycleResponse);
|
||||
impl_has_s3fields!(DeleteBucketLifecycleResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteBucketLifecycleResponse {}
|
||||
impl HasRegion for DeleteBucketLifecycleResponse {}
|
||||
|
||||
@ -14,44 +14,24 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [delete_bucket_notification()](crate::s3::client::Client::delete_bucket_notification) API call.
|
||||
/// This struct contains metadata and information about the bucket whose notifications were removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the Bucket Notifications were removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketNotificationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Bucket Notifications were removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketNotificationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteBucketNotificationResponse);
|
||||
impl_has_s3fields!(DeleteBucketNotificationResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteBucketNotificationResponse {}
|
||||
impl HasRegion for DeleteBucketNotificationResponse {}
|
||||
|
||||
@ -13,49 +13,45 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [delete_bucket_policy()](crate::s3::client::Client::delete_bucket_policy) API call.
|
||||
/// This struct contains metadata and information about the bucket whose policy was removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the Bucket Policy was removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketPolicyResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Bucket Policy was removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(DeleteBucketPolicyResponse);
|
||||
|
||||
impl HasBucket for DeleteBucketPolicyResponse {}
|
||||
impl HasRegion for DeleteBucketPolicyResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketPolicyResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => Ok(Self {
|
||||
headers: mem::take(r.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchBucketPolicy => Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
body: Bytes::new(),
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
||||
@ -13,52 +13,48 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the `[delete_bucket_replication()](crate::s3::client::Client::delete_bucket_replication) API call.
|
||||
/// This struct contains metadata and information about the bucket whose replication configuration was removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the Replication configuration was removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketReplicationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Replication configuration was removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(DeleteBucketReplicationResponse);
|
||||
|
||||
impl HasBucket for DeleteBucketReplicationResponse {}
|
||||
impl HasRegion for DeleteBucketReplicationResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketReplicationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => Ok(Self {
|
||||
headers: mem::take(r.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e))
|
||||
if e.code == ErrorCode::ReplicationConfigurationNotFoundError =>
|
||||
{
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
body: Bytes::new(),
|
||||
})
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
|
||||
@ -14,44 +14,24 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Represents the response of the [delete_bucket_tagging()](crate::s3::client::Client::delete_bucket_tagging) API call.
|
||||
/// This struct contains metadata and information about the bucket whose tags were removed.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `headers` - HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
/// * `region` - The AWS region where the bucket resides.
|
||||
/// * `bucket` - Name of the bucket from which the tags were removed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteBucketTaggingResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the tags were removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteBucketTaggingResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteBucketTaggingResponse);
|
||||
impl_has_s3fields!(DeleteBucketTaggingResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteBucketTaggingResponse {}
|
||||
impl HasRegion for DeleteBucketTaggingResponse {}
|
||||
|
||||
@ -13,58 +13,32 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasIsDeleteMarker, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_default_text, get_option_text, get_text};
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
|
||||
use crate::s3::{
|
||||
error::Error,
|
||||
types::{FromS3Response, S3Request},
|
||||
utils::{get_default_text, get_option_text, get_text},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// Value of the `x-amz-delete-marker` header.
|
||||
/// Indicates whether the specified object version that was permanently deleted was (true) or
|
||||
/// was not (false) a delete marker before deletion. In a simple DELETE, this header indicates
|
||||
/// whether (true) or not (false) the current version of the object is a delete marker.
|
||||
pub is_delete_marker: bool,
|
||||
|
||||
/// Value of the `x-amz-version-id` header.
|
||||
/// If a delete marker was created, this field will contain the version_id of the delete marker.
|
||||
pub version_id: Option<String>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteObjectResponse {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let is_delete_marker = headers
|
||||
.get("x-amz-delete-marker")
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false);
|
||||
impl_from_s3response!(DeleteObjectResponse);
|
||||
impl_has_s3fields!(DeleteObjectResponse);
|
||||
|
||||
let version_id: Option<String> = headers
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok().map(String::from));
|
||||
|
||||
Ok(DeleteObjectResponse {
|
||||
headers,
|
||||
is_delete_marker,
|
||||
version_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteObjectResponse {}
|
||||
impl HasRegion for DeleteObjectResponse {}
|
||||
impl HasVersion for DeleteObjectResponse {}
|
||||
impl HasIsDeleteMarker for DeleteObjectResponse {}
|
||||
|
||||
/// Error info returned by the S3 API when an object could not be deleted.
|
||||
#[derive(Clone, Debug)]
|
||||
@ -84,18 +58,6 @@ pub struct DeletedObject {
|
||||
pub delete_marker_version_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Response of
|
||||
/// [delete_objects()](crate::s3::client::Client::delete_objects)
|
||||
/// S3 API. It is also returned by the
|
||||
/// [remove_objects()](crate::s3::client::Client::delete_objects_streaming) API in the
|
||||
/// form of a stream.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteObjectsResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
pub result: Vec<DeleteResult>,
|
||||
}
|
||||
|
||||
/// Result of deleting an object.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeleteResult {
|
||||
@ -116,24 +78,30 @@ impl DeleteResult {
|
||||
pub fn is_deleted(&self) -> bool {
|
||||
matches!(self, DeleteResult::Deleted(_))
|
||||
}
|
||||
|
||||
pub fn is_error(&self) -> bool {
|
||||
matches!(self, DeleteResult::Error(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteObjectsResponse {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let resp = resp?;
|
||||
let headers = resp.headers().clone();
|
||||
/// Response of
|
||||
/// [delete_objects()](crate::s3::client::Client::delete_objects)
|
||||
/// S3 API. It is also returned by the
|
||||
/// [remove_objects()](crate::s3::client::Client::delete_objects_streaming) API in the
|
||||
/// form of a stream.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteObjectsResponse {
|
||||
request: S3Request,
|
||||
pub(crate) headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
let body = resp.bytes().await?;
|
||||
impl_from_s3response!(DeleteObjectsResponse);
|
||||
impl_has_s3fields!(DeleteObjectsResponse);
|
||||
|
||||
let root = Element::parse(body.reader())?;
|
||||
impl DeleteObjectsResponse {
|
||||
/// Returns the bucket name for which the delete operation was performed.
|
||||
pub fn result(&self) -> Result<Vec<DeleteResult>, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
let result = root
|
||||
.children
|
||||
.iter()
|
||||
@ -158,7 +126,6 @@ impl FromS3Response for DeleteObjectsResponse {
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<DeleteResult>, Error>>()?;
|
||||
|
||||
Ok(Self { headers, result })
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,9 +14,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -29,28 +30,13 @@ use std::mem;
|
||||
/// For more information, refer to the [AWS S3 DeleteObjectLockConfiguration API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjectLockConfiguration.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteObjectLockConfigResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket from which the Object Lock configuration was removed.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteObjectLockConfigResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteObjectLockConfigResponse);
|
||||
impl_has_s3fields!(DeleteObjectLockConfigResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteObjectLockConfigResponse {}
|
||||
impl HasRegion for DeleteObjectLockConfigResponse {}
|
||||
|
||||
@ -14,10 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::MultimapExt;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -30,38 +32,15 @@ use std::mem;
|
||||
/// For more information, refer to the [AWS S3 DeleteObjectTagging API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjectTagging.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteObjectTaggingResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (name) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// The version ID of the object from which the tags were removed.
|
||||
///
|
||||
/// If versioning is not enabled on the bucket, this field may be `None`.
|
||||
pub version_id: Option<String>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for DeleteObjectTaggingResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(DeleteObjectTaggingResponse);
|
||||
impl_has_s3fields!(DeleteObjectTaggingResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for DeleteObjectTaggingResponse {}
|
||||
impl HasRegion for DeleteObjectTaggingResponse {}
|
||||
impl HasObject for DeleteObjectTaggingResponse {}
|
||||
impl HasVersion for DeleteObjectTaggingResponse {}
|
||||
|
||||
@ -13,11 +13,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request, SseConfig};
|
||||
use crate::s3::utils::{get_option_text, get_text, take_bucket};
|
||||
use crate::s3::utils::{get_option_text, get_text};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -32,35 +34,26 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketEncryption API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketEncryptionResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket whose encryption configuration is retrieved.
|
||||
pub bucket: String,
|
||||
|
||||
/// The default server-side encryption configuration of the bucket.
|
||||
///
|
||||
/// This includes the encryption algorithm and, if applicable, the AWS KMS key ID used for encrypting objects.
|
||||
///
|
||||
/// If the bucket has no default encryption configuration, the `get_bucket_encryption` API call may return an error
|
||||
/// with the code `ServerSideEncryptionConfigurationNotFoundError`. It's advisable to handle this case appropriately in your application.
|
||||
pub config: SseConfig,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketEncryptionResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => {
|
||||
let headers: HeaderMap = mem::take(r.headers_mut());
|
||||
let body = r.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
impl_has_s3fields!(GetBucketEncryptionResponse);
|
||||
|
||||
impl HasBucket for GetBucketEncryptionResponse {}
|
||||
impl HasRegion for GetBucketEncryptionResponse {}
|
||||
|
||||
impl GetBucketEncryptionResponse {
|
||||
/// Returns the default server-side encryption configuration of the bucket.
|
||||
///
|
||||
/// This includes the encryption algorithm and, if applicable, the AWS KMS key ID used for encrypting objects.
|
||||
/// If the bucket has no default encryption configuration, this method returns a default `SseConfig` with empty fields.
|
||||
pub fn config(&self) -> Result<SseConfig, Error> {
|
||||
if self.body.is_empty() {
|
||||
return Ok(SseConfig::default());
|
||||
}
|
||||
let mut root = Element::parse(self.body.clone().reader())?; // clone of Bytes is inexpensive
|
||||
|
||||
let rule = root
|
||||
.get_mut_child("Rule")
|
||||
@ -72,24 +65,32 @@ impl FromS3Response for GetBucketEncryptionResponse {
|
||||
"<ApplyServerSideEncryptionByDefault> tag not found".into(),
|
||||
))?;
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: SseConfig {
|
||||
Ok(SseConfig {
|
||||
sse_algorithm: get_text(sse_by_default, "SSEAlgorithm")?,
|
||||
kms_master_key_id: get_option_text(sse_by_default, "KMSMasterKeyID"),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketEncryptionResponse {
|
||||
async fn from_s3response(
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e))
|
||||
if e.code == ErrorCode::ServerSideEncryptionConfigurationNotFoundError =>
|
||||
{
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: Default::default(),
|
||||
body: Bytes::new(),
|
||||
})
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
|
||||
@ -15,10 +15,10 @@
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::lifecycle_config::LifecycleConfig;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{UtcTime, take_bucket};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
@ -33,56 +33,36 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketLifecycleConfiguration API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketLifecycleResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket whose lifecycle configuration is retrieved.
|
||||
pub bucket: String,
|
||||
|
||||
/// The lifecycle configuration of the bucket.
|
||||
///
|
||||
/// This includes a set of rules that define actions applied to objects, such as transitioning
|
||||
/// them to different storage classes, expiring them, or aborting incomplete multipart uploads.
|
||||
///
|
||||
/// If the bucket has no lifecycle configuration, this field may contain an empty configuration.
|
||||
pub config: LifecycleConfig,
|
||||
|
||||
/// Optional value of `X-Minio-LifecycleConfig-UpdatedAt` header, indicating the last update
|
||||
/// time of the lifecycle configuration.
|
||||
pub updated_at: Option<UtcTime>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketLifecycleResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let config: LifecycleConfig = {
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
LifecycleConfig::from_xml(&root)?
|
||||
};
|
||||
let updated_at: Option<DateTime<Utc>> = headers
|
||||
impl_from_s3response!(GetBucketLifecycleResponse);
|
||||
impl_has_s3fields!(GetBucketLifecycleResponse);
|
||||
|
||||
impl HasBucket for GetBucketLifecycleResponse {}
|
||||
impl HasRegion for GetBucketLifecycleResponse {}
|
||||
|
||||
impl GetBucketLifecycleResponse {
|
||||
/// Returns the lifecycle configuration of the bucket.
|
||||
///
|
||||
/// This configuration includes rules for managing the lifecycle of objects in the bucket,
|
||||
/// such as transitioning them to different storage classes or expiring them after a specified period.
|
||||
pub fn config(&self) -> Result<LifecycleConfig, Error> {
|
||||
LifecycleConfig::from_xml(&Element::parse(self.body.clone().reader())?)
|
||||
}
|
||||
|
||||
/// Returns the last update time of the lifecycle configuration
|
||||
/// (`X-Minio-LifecycleConfig-UpdatedAt`), if available.
|
||||
pub fn updated_at(&self) -> Option<DateTime<Utc>> {
|
||||
self.headers
|
||||
.get("x-minio-lifecycleconfig-updatedat")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| {
|
||||
NaiveDateTime::parse_from_str(v, "%Y%m%dT%H%M%SZ")
|
||||
.ok()
|
||||
.map(|naive| DateTime::from_naive_utc_and_offset(naive, Utc))
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config,
|
||||
updated_at,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, NotificationConfig, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -31,42 +31,23 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketNotificationConfiguration API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketNotificationConfiguration.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketNotificationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
impl_from_s3response!(GetBucketNotificationResponse);
|
||||
impl_has_s3fields!(GetBucketNotificationResponse);
|
||||
|
||||
/// Name of the bucket whose notification configuration is retrieved.
|
||||
pub bucket: String,
|
||||
impl HasBucket for GetBucketNotificationResponse {}
|
||||
impl HasRegion for GetBucketNotificationResponse {}
|
||||
|
||||
/// The notification configuration of the bucket.
|
||||
impl GetBucketNotificationResponse {
|
||||
/// Returns the notification configuration of the bucket.
|
||||
///
|
||||
/// This includes the event types and the destinations (e.g., SNS topics, SQS queues, Lambda functions)
|
||||
/// This configuration includes the event types and the destinations (e.g., SNS topics, SQS queues, Lambda functions)
|
||||
/// configured to receive notifications for those events.
|
||||
///
|
||||
/// If the bucket has no notification configuration, this field may contain an empty configuration.
|
||||
pub config: NotificationConfig,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketNotificationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
let config = NotificationConfig::from_xml(&mut root)?;
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config,
|
||||
})
|
||||
pub fn config(&self) -> Result<NotificationConfig, Error> {
|
||||
NotificationConfig::from_xml(&mut Element::parse(self.body.clone().reader())?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,10 +13,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -29,45 +31,44 @@ use std::mem;
|
||||
/// For more information, refer to the [AWS S3 GetBucketPolicy API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicy.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketPolicyResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
impl_has_s3fields!(GetBucketPolicyResponse);
|
||||
|
||||
/// Name of the bucket whose policy is retrieved.
|
||||
pub bucket: String,
|
||||
impl HasBucket for GetBucketPolicyResponse {}
|
||||
impl HasRegion for GetBucketPolicyResponse {}
|
||||
|
||||
/// The bucket policy as a JSON-formatted string.
|
||||
impl GetBucketPolicyResponse {
|
||||
/// Returns the bucket policy as a JSON-formatted string.
|
||||
///
|
||||
/// This policy defines access permissions for the bucket. It specifies who can access the bucket,
|
||||
/// what actions they can perform, and under what conditions.
|
||||
///
|
||||
/// For example, a policy might grant read-only access to anonymous users or restrict access to specific IP addresses.
|
||||
///
|
||||
/// Note: If the bucket has no policy, the `get_bucket_policy` API call may return an error
|
||||
/// with the code `NoSuchBucketPolicy`. It's advisable to handle this case appropriately in your application.
|
||||
pub config: String,
|
||||
/// This method retrieves the policy associated with the bucket, which defines permissions
|
||||
/// for accessing the bucket and its contents.
|
||||
pub fn config(&self) -> Result<&str, Error> {
|
||||
std::str::from_utf8(&self.body).map_err(|e| {
|
||||
Error::Utf8Error(format!("Failed to parse bucket policy as UTF-8: {}", e).into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketPolicyResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => Ok(Self {
|
||||
headers: mem::take(r.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: r.text().await?,
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchBucketPolicy => Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: String::from("{}"),
|
||||
body: Bytes::from_static("{}".as_ref()),
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, ReplicationConfig, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -30,42 +30,26 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketReplication API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketReplication.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketReplicationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
impl_from_s3response!(GetBucketReplicationResponse);
|
||||
impl_has_s3fields!(GetBucketReplicationResponse);
|
||||
|
||||
/// Name of the bucket whose replication configuration is retrieved.
|
||||
pub bucket: String,
|
||||
impl HasBucket for GetBucketReplicationResponse {}
|
||||
impl HasRegion for GetBucketReplicationResponse {}
|
||||
|
||||
/// The replication configuration of the bucket.
|
||||
impl GetBucketReplicationResponse {
|
||||
/// Returns the replication configuration of the bucket.
|
||||
///
|
||||
/// This includes the IAM role that Amazon S3 assumes to replicate objects on your behalf,
|
||||
/// and one or more replication rules that specify the conditions under which objects are replicated.
|
||||
///
|
||||
/// For more details on replication configuration elements, see the [AWS S3 Replication Configuration documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication-add-config.html).
|
||||
pub config: ReplicationConfig,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketReplicationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
let config = ReplicationConfig::from_xml(&root)?;
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config,
|
||||
})
|
||||
pub fn config(&self) -> Result<ReplicationConfig, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
ReplicationConfig::from_xml(&root)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,15 +13,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields, HasTagging};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_text, take_bucket};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
|
||||
/// Response from the [`get_bucket_tagging`](crate::s3::client::Client::get_bucket_tagging) API call,
|
||||
/// providing the set of tags associated with an S3 bucket.
|
||||
@ -32,57 +31,33 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketTagging API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketTagging.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketTaggingResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket whose tags are retrieved.
|
||||
pub bucket: String,
|
||||
|
||||
/// A collection of tags assigned to the bucket.
|
||||
///
|
||||
/// Each tag is a key-value pair represented as a `HashMap<String, String>`.
|
||||
/// If the bucket has no tags, this map will be empty.
|
||||
///
|
||||
/// Note: If the bucket has no tags, the `get_bucket_tags` API call may return an error
|
||||
/// with the code `NoSuchTagSet`. It's advisable to handle this case appropriately in your application.
|
||||
pub tags: HashMap<String, String>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(GetBucketTaggingResponse);
|
||||
|
||||
impl HasBucket for GetBucketTaggingResponse {}
|
||||
impl HasRegion for GetBucketTaggingResponse {}
|
||||
impl HasTagging for GetBucketTaggingResponse {}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketTaggingResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => {
|
||||
let headers: HeaderMap = mem::take(r.headers_mut());
|
||||
let body = r.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
|
||||
let element = root
|
||||
.get_mut_child("TagSet")
|
||||
.ok_or(Error::XmlError("<TagSet> tag not found".to_string()))?;
|
||||
let mut tags = HashMap::new();
|
||||
while let Some(v) = element.take_child("Tag") {
|
||||
tags.insert(get_text(&v, "Key")?, get_text(&v, "Value")?);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
tags,
|
||||
})
|
||||
}
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchTagSet => Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
tags: HashMap::new(),
|
||||
body: Bytes::new(),
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
||||
@ -15,10 +15,11 @@
|
||||
|
||||
use crate::s3::builders::VersioningStatus;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_option_text, take_bucket};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::s3::utils::get_option_text;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -32,59 +33,40 @@ use xmltree::Element;
|
||||
/// For more information, refer to the [AWS S3 GetBucketVersioning API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketVersioning.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetBucketVersioningResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket whose versioning configuration is retrieved.
|
||||
pub bucket: String,
|
||||
|
||||
/// The versioning status of the bucket.
|
||||
///
|
||||
/// - `Some(VersioningStatus::Enabled)`: Versioning is enabled.
|
||||
/// - `Some(VersioningStatus::Suspended)`: Versioning is suspended.
|
||||
/// - `None`: Versioning has never been configured for this bucket.
|
||||
pub status: Option<VersioningStatus>,
|
||||
|
||||
/// Indicates whether MFA delete is enabled for the bucket.
|
||||
///
|
||||
/// - `Some(true)`: MFA delete is enabled.
|
||||
/// - `Some(false)`: MFA delete is disabled.
|
||||
/// - `None`: MFA delete has never been configured for this bucket.
|
||||
///
|
||||
/// Note: MFA delete adds an extra layer of security by requiring additional authentication
|
||||
/// for certain operations. For more details, see the [AWS S3 MFA Delete documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiFactorAuthenticationDelete.html).
|
||||
pub mfa_delete: Option<bool>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetBucketVersioningResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(GetBucketVersioningResponse);
|
||||
impl_has_s3fields!(GetBucketVersioningResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
impl HasBucket for GetBucketVersioningResponse {}
|
||||
impl HasRegion for GetBucketVersioningResponse {}
|
||||
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
let status: Option<VersioningStatus> =
|
||||
get_option_text(&root, "Status").map(|v| match v.as_str() {
|
||||
impl GetBucketVersioningResponse {
|
||||
/// Returns the versioning status of the bucket.
|
||||
///
|
||||
/// This method retrieves the current versioning status, which can be:
|
||||
/// - `Some(VersioningStatus::Enabled)` if versioning is enabled.
|
||||
/// - `Some(VersioningStatus::Suspended)` if versioning is suspended.
|
||||
/// - `None` if versioning has never been configured for this bucket.
|
||||
pub fn status(&self) -> Result<Option<VersioningStatus>, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
Ok(get_option_text(&root, "Status").map(|v| match v.as_str() {
|
||||
"Enabled" => VersioningStatus::Enabled,
|
||||
_ => VersioningStatus::Suspended, // Default case
|
||||
});
|
||||
let mfa_delete: Option<bool> =
|
||||
get_option_text(&root, "MFADelete").map(|v| v.eq_ignore_ascii_case("Enabled"));
|
||||
}))
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
status,
|
||||
mfa_delete,
|
||||
})
|
||||
/// Returns whether MFA delete is enabled for the bucket.
|
||||
///
|
||||
/// This method retrieves the MFA delete setting, which can be:
|
||||
/// - `Some(true)` if MFA delete is enabled.
|
||||
/// - `Some(false)` if MFA delete is disabled.
|
||||
/// - `None` if MFA delete has never been configured for this bucket.
|
||||
pub fn mfa_delete(&self) -> Result<Option<bool>, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
Ok(get_option_text(&root, "MFADelete").map(|v| v.eq_ignore_ascii_case("Enabled")))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,75 +13,64 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasEtagFromHeaders, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::{
|
||||
builders::ObjectContent,
|
||||
error::Error,
|
||||
types::{FromS3Response, S3Request},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures_util::TryStreamExt;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
pub struct GetObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes, // Note: not used
|
||||
resp: reqwest::Response,
|
||||
}
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
impl_has_s3fields!(GetObjectResponse);
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
impl HasBucket for GetObjectResponse {}
|
||||
impl HasRegion for GetObjectResponse {}
|
||||
impl HasObject for GetObjectResponse {}
|
||||
impl HasVersion for GetObjectResponse {}
|
||||
impl HasEtagFromHeaders for GetObjectResponse {}
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
impl GetObjectResponse {
|
||||
/// Returns the content of the object as a (streaming) byte buffer. Note: consumes the response.
|
||||
pub fn content(self) -> Result<ObjectContent, Error> {
|
||||
let content_length: u64 = self.object_size()?;
|
||||
let body = self.resp.bytes_stream().map_err(std::io::Error::other);
|
||||
Ok(ObjectContent::new_from_stream(body, Some(content_length)))
|
||||
}
|
||||
|
||||
/// Entity tag representing a specific version of the object.
|
||||
pub etag: Option<String>,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
|
||||
/// The content of the object as a stream or byte buffer.
|
||||
pub content: ObjectContent,
|
||||
|
||||
/// Size of the object in bytes.
|
||||
pub object_size: u64,
|
||||
/// Returns the content size (in Bytes) of the object.
|
||||
pub fn object_size(&self) -> Result<u64, Error> {
|
||||
self.resp
|
||||
.content_length()
|
||||
.ok_or(Error::ContentLengthUnknown)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let etag: Option<String> = headers
|
||||
.get("etag")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.trim_matches('"').to_string());
|
||||
|
||||
let version_id: Option<String> = headers
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok().map(String::from));
|
||||
|
||||
let content_length: u64 = resp.content_length().ok_or(Error::ContentLengthUnknown)?;
|
||||
let body = resp.bytes_stream().map_err(std::io::Error::other);
|
||||
let content = ObjectContent::new_from_stream(body, Some(content_length));
|
||||
|
||||
let mut resp = response?;
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id,
|
||||
content,
|
||||
object_size: content_length,
|
||||
etag,
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: Bytes::new(),
|
||||
resp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::multimap::MultimapExt;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_default_text, take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::s3::utils::get_default_text;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -28,57 +30,28 @@ use xmltree::Element;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetObjectLegalHoldResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
|
||||
/// Indicates whether the object legal hold is enabled.
|
||||
pub enabled: bool,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectLegalHoldResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => {
|
||||
let headers: HeaderMap = mem::take(r.headers_mut());
|
||||
let body = r.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
impl_from_s3response!(GetObjectLegalHoldResponse);
|
||||
impl_has_s3fields!(GetObjectLegalHoldResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
enabled: get_default_text(&root, "Status") == "ON",
|
||||
})
|
||||
}
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchObjectLockConfiguration => {
|
||||
Ok(Self {
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
enabled: false,
|
||||
})
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
impl HasBucket for GetObjectLegalHoldResponse {}
|
||||
impl HasRegion for GetObjectLegalHoldResponse {}
|
||||
impl HasObject for GetObjectLegalHoldResponse {}
|
||||
impl HasVersion for GetObjectLegalHoldResponse {}
|
||||
|
||||
impl GetObjectLegalHoldResponse {
|
||||
/// Returns the legal hold status of the object.
|
||||
///
|
||||
/// This method retrieves whether the legal hold is enabled for the specified object.
|
||||
pub fn enabled(&self) -> Result<bool, Error> {
|
||||
if self.body.is_empty() {
|
||||
return Ok(false); // No legal hold configuration present due to NoSuchObjectLockConfiguration
|
||||
}
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
Ok(get_default_text(&root, "Status") == "ON")
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasObject, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, ObjectLockConfig, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -29,36 +29,24 @@ use xmltree::Element;
|
||||
/// helping to enforce write-once-read-many (WORM) protection.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetObjectLockConfigResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket for which the Object Lock configuration is retrieved.
|
||||
pub bucket: String,
|
||||
|
||||
/// The Object Lock configuration of the bucket, including retention settings and legal hold status.
|
||||
pub config: ObjectLockConfig,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectLockConfigResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(GetObjectLockConfigResponse);
|
||||
impl_has_s3fields!(GetObjectLockConfigResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
impl HasBucket for GetObjectLockConfigResponse {}
|
||||
impl HasRegion for GetObjectLockConfigResponse {}
|
||||
impl HasObject for GetObjectLockConfigResponse {}
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: ObjectLockConfig::from_xml(&root)?,
|
||||
})
|
||||
impl GetObjectLockConfigResponse {
|
||||
/// Returns the Object Lock configuration of the bucket.
|
||||
///
|
||||
/// This method retrieves the Object Lock settings, which include retention mode and period,
|
||||
/// as well as legal hold status for the bucket.
|
||||
pub fn config(&self) -> Result<ObjectLockConfig, Error> {
|
||||
ObjectLockConfig::from_xml(&Element::parse(self.body.clone().reader())?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,47 +14,33 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasObject, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
pub struct GetObjectPromptResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// The prompt response for the object.
|
||||
pub prompt_response: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectPromptResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(GetObjectPromptResponse);
|
||||
impl_has_s3fields!(GetObjectPromptResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let prompt_response: String = String::from_utf8(body.to_vec())?;
|
||||
impl HasBucket for GetObjectPromptResponse {}
|
||||
impl HasRegion for GetObjectPromptResponse {}
|
||||
impl HasObject for GetObjectPromptResponse {}
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
prompt_response,
|
||||
impl GetObjectPromptResponse {
|
||||
/// Returns the prompt response for the object.
|
||||
///
|
||||
/// This method retrieves the content of the object as a UTF-8 encoded string.
|
||||
pub fn prompt_response(&self) -> Result<&str, Error> {
|
||||
std::str::from_utf8(&self.body).map_err(|e| {
|
||||
Error::Utf8Error(format!("Failed to parse prompt_response as UTF-8: {}", e).into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::{Error, ErrorCode};
|
||||
use crate::s3::multimap::MultimapExt;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, RetentionMode, S3Request};
|
||||
use crate::s3::utils::{UtcTime, from_iso8601utc, get_option_text, take_bucket, take_object};
|
||||
use crate::s3::utils::{UtcTime, from_iso8601utc, get_option_text};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -26,67 +29,65 @@ use xmltree::Element;
|
||||
/// Response of [get_object_retention()](crate::s3::client::Client::get_object_retention) API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetObjectRetentionResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
impl_has_s3fields!(GetObjectRetentionResponse);
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
impl HasBucket for GetObjectRetentionResponse {}
|
||||
impl HasRegion for GetObjectRetentionResponse {}
|
||||
impl HasObject for GetObjectRetentionResponse {}
|
||||
impl HasVersion for GetObjectRetentionResponse {}
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
impl GetObjectRetentionResponse {
|
||||
/// Returns the retention mode of the object.
|
||||
///
|
||||
/// This method retrieves the retention mode, which can be either `Governance` or `Compliance`.
|
||||
pub fn retention_mode(&self) -> Result<Option<RetentionMode>, Error> {
|
||||
if self.body.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
Ok(match get_option_text(&root, "Mode") {
|
||||
Some(v) => Some(RetentionMode::parse(&v)?),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
|
||||
/// The retention mode of the object.
|
||||
pub retention_mode: Option<RetentionMode>,
|
||||
|
||||
/// The date until which the object is retained.
|
||||
pub retain_until_date: Option<UtcTime>,
|
||||
/// Returns the date until which the object is retained.
|
||||
///
|
||||
/// This method retrieves the retention date, which indicates when the object will no longer be retained.
|
||||
pub fn retain_until_date(&self) -> Result<Option<UtcTime>, Error> {
|
||||
if self.body.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
Ok(match get_option_text(&root, "RetainUntilDate") {
|
||||
Some(v) => Some(from_iso8601utc(&v)?),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectRetentionResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
match resp {
|
||||
Ok(mut r) => {
|
||||
let headers = mem::take(r.headers_mut());
|
||||
let body = r.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
let retention_mode = match get_option_text(&root, "Mode") {
|
||||
Some(v) => Some(RetentionMode::parse(&v)?),
|
||||
_ => None,
|
||||
};
|
||||
let retain_until_date = match get_option_text(&root, "RetainUntilDate") {
|
||||
Some(v) => Some(from_iso8601utc(&v)?),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
retention_mode,
|
||||
retain_until_date,
|
||||
})
|
||||
}
|
||||
match response {
|
||||
Ok(mut resp) => Ok(Self {
|
||||
request,
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
body: resp.bytes().await?,
|
||||
}),
|
||||
Err(Error::S3Error(e)) if e.code == ErrorCode::NoSuchObjectLockConfiguration => {
|
||||
Ok(Self {
|
||||
request,
|
||||
headers: e.headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
retention_mode: None,
|
||||
retain_until_date: None,
|
||||
body: Bytes::new(),
|
||||
})
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
|
||||
@ -14,67 +14,30 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::MultimapExt;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasObject, HasRegion, HasS3Fields, HasTagging, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{get_text, take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
|
||||
/// Response of
|
||||
/// [get_object_tags()](crate::s3::client::Client::get_object_tagging)
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetObjectTaggingResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
|
||||
/// Tags associated with the object.
|
||||
pub tags: HashMap<String, String>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetObjectTaggingResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(GetObjectTaggingResponse);
|
||||
impl_has_s3fields!(GetObjectTaggingResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let body = resp.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
let element = root
|
||||
.get_mut_child("TagSet")
|
||||
.ok_or(Error::XmlError("<TagSet> tag not found".to_string()))?;
|
||||
let mut tags = HashMap::new();
|
||||
while let Some(v) = element.take_child("Tag") {
|
||||
tags.insert(get_text(&v, "Key")?, get_text(&v, "Value")?);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
tags,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for GetObjectTaggingResponse {}
|
||||
impl HasRegion for GetObjectTaggingResponse {}
|
||||
impl HasObject for GetObjectTaggingResponse {}
|
||||
impl HasVersion for GetObjectTaggingResponse {}
|
||||
impl HasTagging for GetObjectTaggingResponse {}
|
||||
|
||||
@ -15,10 +15,10 @@
|
||||
|
||||
use crate::s3::client::DEFAULT_REGION;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -28,44 +28,28 @@ use xmltree::Element;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GetRegionResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// The region response for the bucket.
|
||||
pub region_response: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for GetRegionResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(GetRegionResponse);
|
||||
impl_has_s3fields!(GetRegionResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let region_response: String = {
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
impl HasBucket for GetRegionResponse {}
|
||||
impl HasRegion for GetRegionResponse {}
|
||||
|
||||
impl GetRegionResponse {
|
||||
/// Returns the region response for the bucket.
|
||||
///
|
||||
/// This method retrieves the region where the bucket is located.
|
||||
pub fn region_response(&self) -> Result<String, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
|
||||
let mut location = root.get_text().unwrap_or_default().to_string();
|
||||
if location.is_empty() {
|
||||
location = String::from(DEFAULT_REGION);
|
||||
}
|
||||
location
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
region_response,
|
||||
})
|
||||
Ok(location)
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::HasS3Fields;
|
||||
use crate::s3::types::{Bucket, FromS3Response, S3Request};
|
||||
use crate::s3::utils::{from_iso8601utc, get_text};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -25,24 +26,18 @@ use xmltree::Element;
|
||||
/// Response of [list_buckets()](crate::s3::client::Client::list_buckets) API
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ListBucketsResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// the list of buckets that are present in the account.
|
||||
pub buckets: Vec<Bucket>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for ListBucketsResponse {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
impl_from_s3response!(ListBucketsResponse);
|
||||
impl_has_s3fields!(ListBucketsResponse);
|
||||
|
||||
let body = resp.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
impl ListBucketsResponse {
|
||||
/// Returns the list of buckets in the account.
|
||||
pub fn buckets(&self) -> Result<Vec<Bucket>, Error> {
|
||||
let mut root = Element::parse(self.body().clone().reader())?;
|
||||
let buckets_xml = root
|
||||
.get_mut_child("Buckets")
|
||||
.ok_or(Error::XmlError("<Buckets> tag not found".into()))?;
|
||||
@ -55,7 +50,6 @@ impl FromS3Response for ListBucketsResponse {
|
||||
creation_date: from_iso8601utc(&get_text(&bucket, "CreationDate")?)?,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Self { headers, buckets })
|
||||
Ok(buckets)
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,12 +12,8 @@
|
||||
|
||||
//! Response types for ListObjects APIs
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use reqwest::header::HeaderMap;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::response::a_response_traits::HasS3Fields;
|
||||
use crate::s3::{
|
||||
error::Error,
|
||||
types::{FromS3Response, ListEntry, S3Request},
|
||||
@ -26,6 +22,11 @@ use crate::s3::{
|
||||
xml::{Element, MergeXmlElements},
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::{Buf, Bytes};
|
||||
use reqwest::header::HeaderMap;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
||||
fn url_decode(
|
||||
encoding_type: &Option<String>,
|
||||
@ -188,8 +189,10 @@ fn parse_list_objects_common_prefixes(
|
||||
/// Response of [list_objects_v1()](crate::s3::client::Client::list_objects_v1) S3 API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListObjectsV1Response {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
pub name: String,
|
||||
pub encoding_type: Option<String>,
|
||||
pub prefix: Option<String>,
|
||||
@ -201,18 +204,20 @@ pub struct ListObjectsV1Response {
|
||||
pub next_marker: Option<String>,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(ListObjectsV1Response);
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for ListObjectsV1Response {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let mut resp = response?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let xmltree_root = xmltree::Element::parse(body.reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
|
||||
let xmltree_root = xmltree::Element::parse(body.clone().reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
let (name, encoding_type, prefix, delimiter, is_truncated, max_keys) =
|
||||
parse_common_list_objects_response(&root)?;
|
||||
let marker = url_decode(&encoding_type, root.get_child_text("Marker"))?;
|
||||
@ -224,8 +229,11 @@ impl FromS3Response for ListObjectsV1Response {
|
||||
}
|
||||
parse_list_objects_common_prefixes(&mut contents, &root, &encoding_type)?;
|
||||
|
||||
Ok(ListObjectsV1Response {
|
||||
Ok(Self {
|
||||
request,
|
||||
headers,
|
||||
body,
|
||||
|
||||
name,
|
||||
encoding_type,
|
||||
prefix,
|
||||
@ -242,8 +250,10 @@ impl FromS3Response for ListObjectsV1Response {
|
||||
/// Response of [list_objects_v2()](crate::s3::client::Client::list_objects_v2) S3 API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListObjectsV2Response {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
pub name: String,
|
||||
pub encoding_type: Option<String>,
|
||||
pub prefix: Option<String>,
|
||||
@ -257,19 +267,20 @@ pub struct ListObjectsV2Response {
|
||||
pub next_continuation_token: Option<String>,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(ListObjectsV2Response);
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for ListObjectsV2Response {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let mut resp = response?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let body = resp.bytes().await?;
|
||||
let xmltree_root = xmltree::Element::parse(body.reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
|
||||
let xmltree_root = xmltree::Element::parse(body.clone().reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
let (name, encoding_type, prefix, delimiter, is_truncated, max_keys) =
|
||||
parse_common_list_objects_response(&root)?;
|
||||
let key_count = root
|
||||
@ -283,8 +294,11 @@ impl FromS3Response for ListObjectsV2Response {
|
||||
parse_list_objects_contents(&mut contents, &root, "Contents", &encoding_type, false)?;
|
||||
parse_list_objects_common_prefixes(&mut contents, &root, &encoding_type)?;
|
||||
|
||||
Ok(ListObjectsV2Response {
|
||||
Ok(Self {
|
||||
request,
|
||||
headers,
|
||||
body,
|
||||
|
||||
name,
|
||||
encoding_type,
|
||||
prefix,
|
||||
@ -303,8 +317,10 @@ impl FromS3Response for ListObjectsV2Response {
|
||||
/// Response of [list_object_versions()](crate::s3::client::Client::list_object_versions) S3 API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListObjectVersionsResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
pub name: String,
|
||||
pub encoding_type: Option<String>,
|
||||
pub prefix: Option<String>,
|
||||
@ -318,18 +334,20 @@ pub struct ListObjectVersionsResponse {
|
||||
pub next_version_id_marker: Option<String>,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(ListObjectVersionsResponse);
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for ListObjectVersionsResponse {
|
||||
async fn from_s3response(
|
||||
_req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let resp = resp?;
|
||||
let headers = resp.headers().clone();
|
||||
let mut resp = response?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let xmltree_root = xmltree::Element::parse(body.reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
|
||||
let xmltree_root = xmltree::Element::parse(body.clone().reader())?;
|
||||
let root = Element::from(&xmltree_root);
|
||||
let (name, encoding_type, prefix, delimiter, is_truncated, max_keys) =
|
||||
parse_common_list_objects_response(&root)?;
|
||||
let key_marker = url_decode(&encoding_type, root.get_child_text("KeyMarker"))?;
|
||||
@ -340,8 +358,11 @@ impl FromS3Response for ListObjectVersionsResponse {
|
||||
parse_list_objects_contents(&mut contents, &root, "Version", &encoding_type, true)?;
|
||||
parse_list_objects_common_prefixes(&mut contents, &root, &encoding_type)?;
|
||||
|
||||
Ok(ListObjectVersionsResponse {
|
||||
Ok(Self {
|
||||
request,
|
||||
headers,
|
||||
body,
|
||||
|
||||
name,
|
||||
encoding_type,
|
||||
prefix,
|
||||
@ -360,8 +381,10 @@ impl FromS3Response for ListObjectVersionsResponse {
|
||||
/// Response of [list_objects()](crate::s3::client::Client::list_objects) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ListObjectsResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
pub name: String,
|
||||
pub encoding_type: Option<String>,
|
||||
pub prefix: Option<String>,
|
||||
@ -387,10 +410,15 @@ pub struct ListObjectsResponse {
|
||||
pub next_version_id_marker: Option<String>,
|
||||
}
|
||||
|
||||
impl_has_s3fields!(ListObjectsResponse);
|
||||
|
||||
impl From<ListObjectVersionsResponse> for ListObjectsResponse {
|
||||
fn from(value: ListObjectVersionsResponse) -> Self {
|
||||
ListObjectsResponse {
|
||||
Self {
|
||||
request: value.request,
|
||||
headers: value.headers,
|
||||
body: value.body,
|
||||
|
||||
name: value.name,
|
||||
encoding_type: value.encoding_type,
|
||||
prefix: value.prefix,
|
||||
@ -409,8 +437,11 @@ impl From<ListObjectVersionsResponse> for ListObjectsResponse {
|
||||
|
||||
impl From<ListObjectsV2Response> for ListObjectsResponse {
|
||||
fn from(value: ListObjectsV2Response) -> Self {
|
||||
ListObjectsResponse {
|
||||
Self {
|
||||
request: value.request,
|
||||
headers: value.headers,
|
||||
body: value.body,
|
||||
|
||||
name: value.name,
|
||||
encoding_type: value.encoding_type,
|
||||
prefix: value.prefix,
|
||||
@ -430,7 +461,10 @@ impl From<ListObjectsV2Response> for ListObjectsResponse {
|
||||
impl From<ListObjectsV1Response> for ListObjectsResponse {
|
||||
fn from(value: ListObjectsV1Response) -> Self {
|
||||
Self {
|
||||
request: value.request,
|
||||
headers: value.headers,
|
||||
body: value.body,
|
||||
|
||||
name: value.name,
|
||||
encoding_type: value.encoding_type,
|
||||
prefix: value.prefix,
|
||||
|
||||
@ -13,28 +13,31 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_has_s3fields;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, NotificationRecords, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use futures_util::{Stream, StreamExt, TryStreamExt};
|
||||
use async_std::stream::Stream;
|
||||
use bytes::Bytes;
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of
|
||||
/// [listen_bucket_notification()](crate::s3::client::Client::listen_bucket_notification)
|
||||
/// API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListenBucketNotificationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes, // Note: not used
|
||||
}
|
||||
|
||||
impl_has_s3fields!(ListenBucketNotificationResponse);
|
||||
|
||||
impl HasBucket for ListenBucketNotificationResponse {}
|
||||
impl HasRegion for ListenBucketNotificationResponse {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl FromS3Response
|
||||
for (
|
||||
@ -43,14 +46,13 @@ impl FromS3Response
|
||||
)
|
||||
{
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
request: S3Request,
|
||||
response: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let mut resp = response?;
|
||||
|
||||
// A simple stateful decoder that buffers bytes and yields complete lines
|
||||
let byte_stream = resp.bytes_stream(); // This is a futures::Stream<Item = Result<Bytes, reqwest::Error>>
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let byte_stream = resp.bytes_stream();
|
||||
|
||||
let line_stream = Box::pin(async_stream::try_stream! {
|
||||
let mut buf = Vec::new();
|
||||
@ -94,9 +96,9 @@ impl FromS3Response
|
||||
|
||||
Ok((
|
||||
ListenBucketNotificationResponse {
|
||||
request,
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
body: Bytes::new(),
|
||||
},
|
||||
Box::new(line_stream),
|
||||
))
|
||||
|
||||
@ -14,10 +14,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request, SseConfig};
|
||||
use crate::s3::utils::{get_option_text, get_text, take_bucket};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::s3::utils::{get_option_text, get_text};
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
@ -27,30 +28,21 @@ use xmltree::Element;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketEncryptionResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Server-side encryption configuration.
|
||||
pub config: SseConfig,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketEncryptionResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketEncryptionResponse);
|
||||
impl_has_s3fields!(PutBucketEncryptionResponse);
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let mut root = Element::parse(body.reader())?;
|
||||
impl HasBucket for PutBucketEncryptionResponse {}
|
||||
impl HasRegion for PutBucketEncryptionResponse {}
|
||||
|
||||
impl PutBucketEncryptionResponse {
|
||||
/// Returns the server-side encryption configuration.
|
||||
pub fn config(&self) -> Result<SseConfig, Error> {
|
||||
let mut root = Element::parse(self.body().clone().reader())?;
|
||||
|
||||
let rule = root
|
||||
.get_mut_child("Rule")
|
||||
@ -62,14 +54,9 @@ impl FromS3Response for PutBucketEncryptionResponse {
|
||||
"<ApplyServerSideEncryptionByDefault> tag not found",
|
||||
)))?;
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
config: SseConfig {
|
||||
Ok(SseConfig {
|
||||
sse_algorithm: get_text(sse_by_default, "SSEAlgorithm")?,
|
||||
kms_master_key_id: get_option_text(sse_by_default, "KMSMasterKeyID"),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,37 +14,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of [put_bucket_lifecycle()](crate::s3::client::Client::put_bucket_lifecycle) API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketLifecycleResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketLifecycleResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketLifecycleResponse);
|
||||
impl_has_s3fields!(PutBucketLifecycleResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketLifecycleResponse {}
|
||||
impl HasRegion for PutBucketLifecycleResponse {}
|
||||
|
||||
@ -14,37 +14,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of [put_bucket_notification()](crate::s3::client::Client::put_bucket_notification) API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketNotificationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketNotificationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketNotificationResponse);
|
||||
impl_has_s3fields!(PutBucketNotificationResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketNotificationResponse {}
|
||||
impl HasRegion for PutBucketNotificationResponse {}
|
||||
|
||||
@ -14,37 +14,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of [put_bucket_policy()](crate::s3::client::Client::put_bucket_policy) API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketPolicyResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketPolicyResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketPolicyResponse);
|
||||
impl_has_s3fields!(PutBucketPolicyResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketPolicyResponse {}
|
||||
impl HasRegion for PutBucketPolicyResponse {}
|
||||
|
||||
@ -14,37 +14,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of [put_bucket_replication()](crate::s3::client::Client::put_bucket_replication) API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketReplicationResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketReplicationResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketReplicationResponse);
|
||||
impl_has_s3fields!(PutBucketReplicationResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketReplicationResponse {}
|
||||
impl HasRegion for PutBucketReplicationResponse {}
|
||||
|
||||
@ -14,9 +14,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -25,28 +26,13 @@ use std::mem;
|
||||
/// API
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketTaggingResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketTaggingResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketTaggingResponse);
|
||||
impl_has_s3fields!(PutBucketTaggingResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketTaggingResponse {}
|
||||
impl HasRegion for PutBucketTaggingResponse {}
|
||||
|
||||
@ -14,37 +14,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{HasBucket, HasRegion, HasS3Fields};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::take_bucket;
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
/// Response of [put_bucket_versioning()](crate::s3::client::Client::put_bucket_versioning) API
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutBucketVersioningResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutBucketVersioningResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutBucketVersioningResponse);
|
||||
impl_has_s3fields!(PutBucketVersioningResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutBucketVersioningResponse {}
|
||||
impl HasRegion for PutBucketVersioningResponse {}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2023 MinIO, Inc.
|
||||
// Copyright 2025 MinIO, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -13,126 +13,112 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Buf;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasEtagFromHeaders, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::get_text;
|
||||
use crate::{impl_from_s3response, impl_from_s3response_with_size, impl_has_s3fields};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
use xmltree::Element;
|
||||
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use crate::s3::{
|
||||
error::Error,
|
||||
types::{FromS3Response, S3Request},
|
||||
utils::get_text,
|
||||
};
|
||||
// region
|
||||
|
||||
/// Base response struct that contains common functionality for S3 operations
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct S3Response1 {
|
||||
pub(crate) request: S3Request,
|
||||
pub(crate) headers: HeaderMap,
|
||||
pub(crate) body: Bytes,
|
||||
}
|
||||
|
||||
impl_from_s3response!(S3Response1);
|
||||
impl_has_s3fields!(S3Response1);
|
||||
|
||||
impl HasBucket for S3Response1 {}
|
||||
impl HasObject for S3Response1 {}
|
||||
impl HasRegion for S3Response1 {}
|
||||
impl HasVersion for S3Response1 {}
|
||||
impl HasEtagFromHeaders for S3Response1 {}
|
||||
|
||||
/// Extended response struct for operations that need additional data like object size
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct S3Response1WithSize {
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
|
||||
/// Additional object size information
|
||||
pub(crate) object_size: u64,
|
||||
}
|
||||
|
||||
impl_from_s3response_with_size!(S3Response1WithSize);
|
||||
impl_has_s3fields!(S3Response1WithSize);
|
||||
|
||||
impl HasBucket for S3Response1WithSize {}
|
||||
impl HasObject for S3Response1WithSize {}
|
||||
impl HasRegion for S3Response1WithSize {}
|
||||
impl HasVersion for S3Response1WithSize {}
|
||||
impl HasEtagFromHeaders for S3Response1WithSize {}
|
||||
|
||||
impl S3Response1WithSize {
|
||||
pub fn new(response: S3Response1, object_size: u64) -> Self {
|
||||
Self {
|
||||
request: response.request,
|
||||
headers: response.headers,
|
||||
body: response.body,
|
||||
object_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the object size for the response
|
||||
pub fn object_size(&self) -> u64 {
|
||||
self.object_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended response struct for multipart operations that need upload_id
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct S3MultipartResponse {
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
impl_from_s3response!(S3MultipartResponse);
|
||||
impl_has_s3fields!(S3MultipartResponse);
|
||||
|
||||
impl HasBucket for S3MultipartResponse {}
|
||||
impl HasObject for S3MultipartResponse {}
|
||||
impl HasRegion for S3MultipartResponse {}
|
||||
impl HasVersion for S3MultipartResponse {}
|
||||
impl HasEtagFromHeaders for S3MultipartResponse {}
|
||||
|
||||
impl S3MultipartResponse {
|
||||
/// Returns the upload ID for the multipart upload, while consuming the response.
|
||||
pub async fn upload_id(&self) -> Result<String, Error> {
|
||||
let root = Element::parse(self.body.clone().reader())?;
|
||||
get_text(&root, "UploadId").map_err(|e| Error::InvalidUploadId(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Response of [put_object_api()](crate::s3::client::Client::put_object) API
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PutObjectResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
pub bucket: String,
|
||||
pub object: String,
|
||||
pub region: String,
|
||||
pub etag: String,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutObjectResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
|
||||
let etag: String = headers
|
||||
.get("etag")
|
||||
.and_then(|v| v.to_str().ok()) // Convert to &str safely
|
||||
.map(|s| s.trim_matches('"').to_string()) // Trim and convert to String
|
||||
.unwrap_or_default();
|
||||
|
||||
let version_id: Option<String> = headers
|
||||
.get("x-amz-version-id")
|
||||
.and_then(|v| v.to_str().ok().map(String::from));
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
region: req.inner_region,
|
||||
etag,
|
||||
version_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub type PutObjectResponse = S3Response1;
|
||||
|
||||
/// Response of [create_multipart_upload()](crate::s3::client::Client::create_multipart_upload) API
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CreateMultipartUploadResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
pub region: String,
|
||||
pub bucket: String,
|
||||
pub object: String,
|
||||
pub upload_id: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for CreateMultipartUploadResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
|
||||
let headers: HeaderMap = mem::take(resp.headers_mut());
|
||||
let body = resp.bytes().await?;
|
||||
let root = Element::parse(body.reader())?;
|
||||
let upload_id: String =
|
||||
get_text(&root, "UploadId").map_err(|e| Error::InvalidUploadId(e.to_string()))?;
|
||||
|
||||
Ok(Self {
|
||||
headers,
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
upload_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub type CreateMultipartUploadResponse = S3MultipartResponse;
|
||||
|
||||
/// Response of [abort_multipart_upload()](crate::s3::client::Client::abort_multipart_upload) API
|
||||
pub type AbortMultipartUploadResponse = CreateMultipartUploadResponse;
|
||||
pub type AbortMultipartUploadResponse = S3MultipartResponse;
|
||||
|
||||
/// Response of [complete_multipart_upload()](crate::s3::client::Client::complete_multipart_upload) API
|
||||
pub type CompleteMultipartUploadResponse = PutObjectResponse;
|
||||
pub type CompleteMultipartUploadResponse = S3Response1;
|
||||
|
||||
/// Response of [upload_part()](crate::s3::client::Client::upload_part) API
|
||||
pub type UploadPartResponse = PutObjectResponse;
|
||||
pub type UploadPartResponse = S3Response1;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PutObjectContentResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (path) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// Size of the object in bytes.
|
||||
pub object_size: u64,
|
||||
|
||||
/// Entity tag representing a specific version of the object.
|
||||
pub etag: String,
|
||||
|
||||
/// Version ID of the object, if versioning is enabled. Value of the `x-amz-version-id` header.
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
/// Response for put_object operations that include object size information
|
||||
pub type PutObjectContentResponse = S3Response1WithSize;
|
||||
|
||||
@ -14,10 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::MultimapExt;
|
||||
use crate::s3::response::a_response_traits::{
|
||||
HasBucket, HasObject, HasRegion, HasS3Fields, HasVersion,
|
||||
};
|
||||
use crate::s3::types::{FromS3Response, S3Request};
|
||||
use crate::s3::utils::{take_bucket, take_object};
|
||||
use async_trait::async_trait;
|
||||
use crate::{impl_from_s3response, impl_has_s3fields};
|
||||
use bytes::Bytes;
|
||||
use http::HeaderMap;
|
||||
use std::mem;
|
||||
|
||||
@ -30,38 +32,15 @@ use std::mem;
|
||||
/// For more information, refer to the [AWS S3 PutObjectLegalHold API documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectLegalHold.html).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PutObjectLegalHoldResponse {
|
||||
/// HTTP headers returned by the server, containing metadata such as `Content-Type`, `ETag`, etc.
|
||||
pub headers: HeaderMap,
|
||||
|
||||
/// The AWS region where the bucket resides.
|
||||
pub region: String,
|
||||
|
||||
/// Name of the bucket containing the object.
|
||||
pub bucket: String,
|
||||
|
||||
/// Key (name) identifying the object within the bucket.
|
||||
pub object: String,
|
||||
|
||||
/// The version ID of the object from which the legal hold was removed.
|
||||
///
|
||||
/// If versioning is not enabled on the bucket, this field may be `None`.
|
||||
pub version_id: Option<String>,
|
||||
request: S3Request,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromS3Response for PutObjectLegalHoldResponse {
|
||||
async fn from_s3response(
|
||||
req: S3Request,
|
||||
resp: Result<reqwest::Response, Error>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut resp = resp?;
|
||||
impl_from_s3response!(PutObjectLegalHoldResponse);
|
||||
impl_has_s3fields!(PutObjectLegalHoldResponse);
|
||||
|
||||
Ok(Self {
|
||||
headers: mem::take(resp.headers_mut()),
|
||||
region: req.inner_region,
|
||||
bucket: take_bucket(req.bucket)?,
|
||||
object: take_object(req.object)?,
|
||||
version_id: req.query_params.take_version(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasBucket for PutObjectLegalHoldResponse {}
|
||||
impl HasRegion for PutObjectLegalHoldResponse {}
|
||||
impl HasObject for PutObjectLegalHoldResponse {}
|
||||
impl HasVersion for PutObjectLegalHoldResponse {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user