mirror of
https://github.com/minio/minio-rs.git
synced 2025-12-06 15:26:51 +08:00
refactored all functions (#145)
* refactored stat_object refactored select_object_content refactor get_presigned_object_url refactor get_presigned_policy_form_data refactored upload-part-copy * fixed object.unwrap * update region * made client Arc * made client Arc * update client * update tests * update segmented_bytes * bench updated * cleanup version handling * cleanup of headers: multimap * added inner in Client * updated clients: added Into<String>in API * Separated http_client and shared client items in Client
This commit is contained in:
parent
f23572dce8
commit
58d9203153
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
cargo clippy --all-targets --all-features
|
||||
cargo build --bins --examples --tests --benches --verbose
|
||||
|
||||
- name: Run tests
|
||||
- name: Run tests S3
|
||||
run: |
|
||||
./tests/start-server.sh
|
||||
export SERVER_ENDPOINT=localhost:9000
|
||||
|
||||
@ -48,7 +48,7 @@ ring = { version = "0.17.14", optional = true, default-features = false, feature
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
sha2 = { version = "0.10.8", optional = true }
|
||||
tokio = { version = "1.44.1", features = ["full"] }
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-util = { version = "0.7.14", features = ["io"] }
|
||||
urlencoding = "2.1.3"
|
||||
@ -59,7 +59,7 @@ http = "1.3.1"
|
||||
[dev-dependencies]
|
||||
minio_common = { path = "./common" }
|
||||
async-std = { version = "1.13.1", features = ["attributes", "tokio1"] }
|
||||
clap = { version = "4.5.34", features = ["derive"] }
|
||||
clap = { version = "4.5.35", features = ["derive"] }
|
||||
quickcheck = "1.0.3"
|
||||
criterion = "0.5.1"
|
||||
|
||||
@ -76,6 +76,9 @@ name = "file_downloader"
|
||||
[[example]]
|
||||
name = "object_prompt"
|
||||
|
||||
[[example]]
|
||||
name = "append_object"
|
||||
|
||||
[[bench]]
|
||||
name = "s3-api"
|
||||
path = "benches/s3/api_benchmarks.rs"
|
||||
|
||||
@ -21,14 +21,15 @@ mod bench_bucket_replication;
|
||||
mod bench_bucket_tags;
|
||||
mod bench_bucket_versioning;
|
||||
mod bench_list_bucket;
|
||||
mod bench_object_append;
|
||||
mod bench_object_copy;
|
||||
mod bench_object_legal_hold;
|
||||
mod bench_object_lock_config;
|
||||
mod bench_object_put;
|
||||
mod bench_object_retention;
|
||||
mod bench_object_tags;
|
||||
mod common_benches;
|
||||
|
||||
mod bench_object_copy;
|
||||
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -43,9 +44,12 @@ use crate::bench_bucket_tags::*;
|
||||
use crate::bench_bucket_versioning::*;
|
||||
use crate::bench_list_bucket::*;
|
||||
#[allow(unused_imports)]
|
||||
use crate::bench_object_append::bench_object_append;
|
||||
#[allow(unused_imports)]
|
||||
use crate::bench_object_copy::*;
|
||||
use crate::bench_object_legal_hold::*;
|
||||
use crate::bench_object_lock_config::*;
|
||||
use crate::bench_object_put::bench_object_put;
|
||||
use crate::bench_object_retention::*;
|
||||
use crate::bench_object_tags::*;
|
||||
|
||||
@ -54,9 +58,9 @@ criterion_group!(
|
||||
config = Criterion::default()
|
||||
.configure_from_args()
|
||||
.warm_up_time(Duration::from_secs_f32(0.01))
|
||||
.sample_size(100)
|
||||
.sample_size(1000)
|
||||
.nresamples(1001)
|
||||
.measurement_time(Duration::from_secs_f32(0.5));
|
||||
.measurement_time(Duration::from_secs_f32(10.0));
|
||||
targets =
|
||||
bench_bucket_exists,
|
||||
bench_set_bucket_lifecycle,
|
||||
@ -83,7 +87,9 @@ criterion_group!(
|
||||
bench_get_bucket_versioning,
|
||||
//
|
||||
bench_list_buckets,
|
||||
//bench_object_copy, //TODO first refactor object_copy
|
||||
bench_object_copy_internal,
|
||||
bench_object_append,
|
||||
bench_object_put,
|
||||
//
|
||||
bench_enable_object_legal_hold,
|
||||
bench_disable_object_legal_hold,
|
||||
|
||||
@ -23,6 +23,6 @@ pub(crate) fn bench_bucket_exists(criterion: &mut Criterion) {
|
||||
"bucket_exists",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| BucketExists::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| BucketExists::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -27,8 +27,7 @@ pub(crate) fn bench_set_bucket_lifecycle(criterion: &mut Criterion) {
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
let config = create_bucket_lifecycle_config_examples();
|
||||
SetBucketLifecycle::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
SetBucketLifecycle::new(ctx.client.clone(), ctx.bucket.clone())
|
||||
.life_cycle_config(config)
|
||||
},
|
||||
)
|
||||
@ -40,15 +39,15 @@ pub(crate) fn bench_get_bucket_lifecycle(criterion: &mut Criterion) {
|
||||
|| async {
|
||||
let ctx = Ctx2::new().await;
|
||||
let config = create_bucket_lifecycle_config_examples();
|
||||
SetBucketLifecycle::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
ctx.client
|
||||
.set_bucket_lifecycle(&ctx.bucket)
|
||||
.life_cycle_config(config)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| GetBucketLifecycle::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketLifecycle::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_delete_bucket_lifecycle(criterion: &mut Criterion) {
|
||||
@ -56,6 +55,6 @@ pub(crate) fn bench_delete_bucket_lifecycle(criterion: &mut Criterion) {
|
||||
"delete_bucket_lifecycle",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| DeleteBucketLifecycle::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteBucketLifecycle::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -28,8 +28,7 @@ pub(crate) fn bench_set_bucket_notification(criterion: &mut Criterion) {
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
let config = create_bucket_notification_config_example();
|
||||
SetBucketNotification::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
SetBucketNotification::new(ctx.client.clone(), ctx.bucket.clone())
|
||||
.notification_config(config)
|
||||
},
|
||||
)
|
||||
@ -42,15 +41,15 @@ pub(crate) fn bench_get_bucket_notification(criterion: &mut Criterion) {
|
||||
|| async {
|
||||
let ctx = Ctx2::new().await;
|
||||
let config = create_bucket_notification_config_example();
|
||||
SetBucketNotification::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
ctx.client
|
||||
.set_bucket_notification(&ctx.bucket)
|
||||
.notification_config(config)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| GetBucketNotification::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketNotification::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
@ -59,6 +58,6 @@ pub(crate) fn bench_delete_bucket_notification(criterion: &mut Criterion) {
|
||||
"delete_bucket_notification",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| DeleteBucketNotification::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteBucketNotification::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -27,9 +27,7 @@ pub(crate) fn bench_set_bucket_policy(criterion: &mut Criterion) {
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
let config = create_bucket_policy_config_example(&ctx.bucket);
|
||||
SetBucketPolicy::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.config(config)
|
||||
SetBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone()).config(config)
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -40,15 +38,14 @@ pub(crate) fn bench_get_bucket_policy(criterion: &mut Criterion) {
|
||||
|| async {
|
||||
let ctx = Ctx2::new().await;
|
||||
let config = create_bucket_policy_config_example(&ctx.bucket);
|
||||
SetBucketPolicy::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
SetBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone())
|
||||
.config(config)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| GetBucketPolicy::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_delete_bucket_policy(criterion: &mut Criterion) {
|
||||
@ -56,6 +53,6 @@ pub(crate) fn bench_delete_bucket_policy(criterion: &mut Criterion) {
|
||||
"delete_bucket_policy",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| DeleteBucketPolicy::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -53,8 +53,7 @@ pub(crate) fn bench_set_bucket_replication(criterion: &mut Criterion) {
|
||||
|ctx| {
|
||||
let config =
|
||||
create_bucket_replication_config_example(ctx.aux_bucket.clone().unwrap().as_str());
|
||||
SetBucketReplication::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
SetBucketReplication::new(ctx.client.clone(), ctx.bucket.clone())
|
||||
.replication_config(config)
|
||||
},
|
||||
)
|
||||
@ -86,7 +85,7 @@ pub(crate) fn bench_get_bucket_replication(criterion: &mut Criterion) {
|
||||
|
||||
ctx
|
||||
},
|
||||
|ctx| GetBucketReplication::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketReplication::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
@ -116,6 +115,6 @@ pub(crate) fn bench_delete_bucket_replication(criterion: &mut Criterion) {
|
||||
|
||||
ctx
|
||||
},
|
||||
|ctx| DeleteBucketReplication::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteBucketReplication::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,34 +13,36 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{DeleteBucketTags, GetBucketTags, SetBucketTags};
|
||||
use minio::s3::response::SetBucketTagsResponse;
|
||||
use minio::s3::types::S3Api;
|
||||
use minio_common::example::create_tags_example;
|
||||
|
||||
pub(crate) fn bench_set_bucket_tags(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_set_bucket_tags") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"set_bucket_tags",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
SetBucketTags::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.tags(create_tags_example())
|
||||
SetBucketTags::new(ctx.client.clone(), ctx.bucket.clone()).tags(create_tags_example())
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_get_bucket_tags(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_get_bucket_tags") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"get_bucket_tags",
|
||||
criterion,
|
||||
|| async {
|
||||
let ctx = Ctx2::new().await;
|
||||
let _resp: SetBucketTagsResponse = ctx
|
||||
.client
|
||||
ctx.client
|
||||
.set_bucket_tags(&ctx.bucket)
|
||||
.tags(create_tags_example())
|
||||
.send()
|
||||
@ -48,14 +50,17 @@ pub(crate) fn bench_get_bucket_tags(criterion: &mut Criterion) {
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| GetBucketTags::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketTags::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_delete_bucket_tags(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_delete_bucket_tags") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"delete_bucket_tags",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| DeleteBucketTags::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteBucketTags::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,29 +13,33 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{GetBucketVersioning, SetBucketVersioning, VersioningStatus};
|
||||
|
||||
pub(crate) fn bench_get_bucket_versioning(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_get_bucket_versioning") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"get_bucket_versioning",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| GetBucketVersioning::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetBucketVersioning::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_set_bucket_versioning(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_set_bucket_versioning") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"set_bucket_versioning",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
let status = VersioningStatus::Enabled;
|
||||
SetBucketVersioning::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.versioning_status(status)
|
||||
SetBucketVersioning::new(ctx.client.clone(), ctx.bucket.clone())
|
||||
.versioning_status(VersioningStatus::Enabled)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -23,6 +23,6 @@ pub(crate) fn bench_list_buckets(criterion: &mut Criterion) {
|
||||
"list_buckets",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| ListBuckets::new().client(&ctx.client),
|
||||
|ctx| ListBuckets::new(ctx.client.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
56
benches/s3/bench_object_append.rs
Normal file
56
benches/s3/bench_object_append.rs
Normal file
@ -0,0 +1,56 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::AppendObject;
|
||||
use minio::s3::response::StatObjectResponse;
|
||||
use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
use minio::s3::types::S3Api;
|
||||
use minio_common::test_context::TestContext;
|
||||
use tokio::task;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bench_object_append(criterion: &mut Criterion) {
|
||||
if !TestContext::new_from_env().client.is_minio_express() {
|
||||
println!("Skipping benchmark because it is NOT running in MinIO Express mode");
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"object_append",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(false).await },
|
||||
|ctx| {
|
||||
let content1 = "Hello world 2";
|
||||
let data1: SegmentedBytes = SegmentedBytes::from(content1.to_string());
|
||||
|
||||
let resp: StatObjectResponse = task::block_in_place(|| {
|
||||
tokio::runtime::Runtime::new()?
|
||||
.block_on(ctx.client.stat_object(&ctx.bucket, &ctx.object).send())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let offset_bytes: u64 = resp.size;
|
||||
AppendObject::new(
|
||||
ctx.client.clone(),
|
||||
ctx.bucket.clone(),
|
||||
ctx.object.clone(),
|
||||
data1,
|
||||
offset_bytes,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -13,23 +13,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{CopyObjectInternal, CopySource};
|
||||
use minio_common::utils::rand_object_name;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bench_object_copy(_criterion: &mut Criterion) {
|
||||
/*
|
||||
pub(crate) fn bench_object_copy_internal(criterion: &mut Criterion) {
|
||||
benchmark_s3_api(
|
||||
"object_copy",
|
||||
"object_copy_internal",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(false).await },
|
||||
|ctx| {
|
||||
let _object_name_dst = rand_object_name();
|
||||
//TODO refactor copy object for this to be possible
|
||||
todo!()
|
||||
let object_name_src = &ctx.object;
|
||||
let object_name_dst = rand_object_name();
|
||||
CopyObjectInternal::new(ctx.client.clone(), ctx.bucket.clone(), object_name_dst)
|
||||
.source(CopySource::new(&ctx.bucket, object_name_src).unwrap())
|
||||
},
|
||||
);
|
||||
*/
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,46 +13,62 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{
|
||||
DisableObjectLegalHold, EnableObjectLegalHold, IsObjectLegalHoldEnabled,
|
||||
};
|
||||
use minio::s3::types::S3Api;
|
||||
|
||||
pub(crate) fn bench_enable_object_legal_hold(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_enable_object_legal_hold") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"enable_object_legal_hold",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| {
|
||||
EnableObjectLegalHold::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
EnableObjectLegalHold::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_disable_object_legal_hold(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_disable_object_legal_hold") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"disable_object_legal_hold",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| {
|
||||
DisableObjectLegalHold::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
DisableObjectLegalHold::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_is_object_legal_hold(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_is_object_legal_hold") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"is_object_legal_hold",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|| async {
|
||||
let ctx = Ctx2::new_with_object(true).await;
|
||||
ctx.client
|
||||
.enable_object_legal_hold(&ctx.bucket, &ctx.object)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| {
|
||||
IsObjectLegalHoldEnabled::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
IsObjectLegalHoldEnabled::new(
|
||||
ctx.client.clone(),
|
||||
ctx.bucket.clone(),
|
||||
ctx.object.clone(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,37 +13,44 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{DeleteObjectLockConfig, GetObjectLockConfig, SetObjectLockConfig};
|
||||
use minio_common::example::create_object_lock_config_example;
|
||||
|
||||
pub(crate) fn bench_set_object_lock_config(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_set_object_lock_config") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"set_object_lock_config",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| {
|
||||
let config = create_object_lock_config_example();
|
||||
SetObjectLockConfig::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.config(config)
|
||||
SetObjectLockConfig::new(ctx.client.clone(), ctx.bucket.clone()).config(config)
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_get_object_lock_config(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_get_object_lock_config") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"get_object_lock_config",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| GetObjectLockConfig::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| GetObjectLockConfig::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_delete_object_lock_config(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_delete_object_lock_config") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"delete_object_lock_config",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| DeleteObjectLockConfig::new(&ctx.bucket).client(&ctx.client),
|
||||
|ctx| DeleteObjectLockConfig::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
43
benches/s3/bench_object_put.rs
Normal file
43
benches/s3/bench_object_put.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{ObjectContent, PutObject};
|
||||
use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
use minio_common::rand_src::RandSrc;
|
||||
use minio_common::utils::rand_object_name;
|
||||
use tokio::task;
|
||||
|
||||
pub(crate) fn bench_object_put(criterion: &mut Criterion) {
|
||||
benchmark_s3_api(
|
||||
"object_put",
|
||||
criterion,
|
||||
|| async { Ctx2::new().await },
|
||||
|ctx| {
|
||||
let object_name: String = rand_object_name();
|
||||
let size = 1024 * 1024_u64; // 1MB
|
||||
let object_content = ObjectContent::new_from_stream(RandSrc::new(size), Some(size));
|
||||
|
||||
let data: SegmentedBytes = task::block_in_place(|| {
|
||||
tokio::runtime::Runtime::new()?.block_on(object_content.to_segmented_bytes())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
PutObject::new(ctx.client.clone(), ctx.bucket.clone(), object_name, data)
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -13,7 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{GetObjectRetention, SetObjectRetention};
|
||||
@ -22,39 +22,38 @@ use minio::s3::types::{RetentionMode, S3Api};
|
||||
use minio::s3::utils::utc_now;
|
||||
|
||||
pub(crate) fn bench_set_object_retention(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_set_object_retention") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"set_object_retention",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(true).await },
|
||||
|ctx| {
|
||||
SetObjectRetention::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
SetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_get_object_retention(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_get_object_retention") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"get_object_retention",
|
||||
criterion,
|
||||
|| async {
|
||||
let ctx = Ctx2::new_with_object(true).await;
|
||||
let _resp: SetObjectRetentionResponse = SetObjectRetention::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let _resp: SetObjectRetentionResponse =
|
||||
SetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| {
|
||||
GetObjectRetention::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
},
|
||||
|ctx| GetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||
use crate::common_benches::{Ctx2, benchmark_s3_api, skip_express_mode};
|
||||
|
||||
use criterion::Criterion;
|
||||
use minio::s3::builders::{GetObjectTags, SetObjectTags};
|
||||
@ -22,37 +22,38 @@ use minio::s3::types::S3Api;
|
||||
use minio_common::example::create_tags_example;
|
||||
|
||||
pub(crate) fn bench_set_object_tags(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_set_object_tags") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"set_object_tags",
|
||||
criterion,
|
||||
|| async { Ctx2::new_with_object(false).await },
|
||||
|ctx| {
|
||||
SetObjectTags::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
SetObjectTags::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||
.tags(create_tags_example())
|
||||
},
|
||||
)
|
||||
}
|
||||
pub(crate) fn bench_get_object_tags(criterion: &mut Criterion) {
|
||||
if skip_express_mode("bench_get_object_tags") {
|
||||
return;
|
||||
}
|
||||
benchmark_s3_api(
|
||||
"get_object_tags",
|
||||
criterion,
|
||||
|| async {
|
||||
let ctx = Ctx2::new_with_object(false).await;
|
||||
let _resp: SetObjectTagsResponse = SetObjectTags::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
|
||||
let _resp: SetObjectTagsResponse = ctx
|
||||
.client
|
||||
.set_object_tags(&ctx.bucket, &ctx.object)
|
||||
.tags(create_tags_example())
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
ctx
|
||||
},
|
||||
|ctx| {
|
||||
GetObjectTags::new(&ctx.bucket)
|
||||
.client(&ctx.client)
|
||||
.object(ctx.object.clone())
|
||||
},
|
||||
|ctx| GetObjectTags::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -17,13 +17,14 @@ use criterion::Criterion;
|
||||
use minio::s3::Client;
|
||||
use minio::s3::error::Error;
|
||||
use minio::s3::response::{MakeBucketResponse, PutObjectContentResponse};
|
||||
use minio::s3::types::{FromS3Response, S3Api};
|
||||
use minio::s3::types::{FromS3Response, S3Api, S3Request};
|
||||
use minio_common::cleanup_guard::CleanupGuard;
|
||||
use minio_common::test_context::TestContext;
|
||||
use minio_common::utils::{
|
||||
get_bytes_from_response, get_response_from_bytes, rand_bucket_name, rand_object_name,
|
||||
};
|
||||
use std::env;
|
||||
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub(crate) struct Ctx2 {
|
||||
@ -67,7 +68,7 @@ impl Ctx2 {
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let cleanup = CleanupGuard::new(&ctx.client, &bucket_name);
|
||||
let cleanup = CleanupGuard::new(ctx.client.clone(), &bucket_name);
|
||||
let object_name = rand_object_name();
|
||||
let data = bytes::Bytes::from("hello, world".to_string().into_bytes());
|
||||
let _resp: PutObjectContentResponse = ctx
|
||||
@ -90,7 +91,7 @@ impl Ctx2 {
|
||||
pub async fn new_aux(&mut self) -> String {
|
||||
let bucket_name: String = rand_bucket_name();
|
||||
self.aux_bucket = Some(bucket_name.clone());
|
||||
self._aux_cleanup = Some(CleanupGuard::new(&self.client, &bucket_name));
|
||||
self._aux_cleanup = Some(CleanupGuard::new(self.client.clone(), &bucket_name));
|
||||
let _resp: MakeBucketResponse = self
|
||||
.client
|
||||
.make_bucket(&bucket_name)
|
||||
@ -140,7 +141,7 @@ pub(crate) fn benchmark_s3_api<ApiType, GlobalSetupFuture>(
|
||||
|
||||
// Per-iteration setup for initial request
|
||||
let api = per_iter_setup(&ctx);
|
||||
let request = api.to_s3request().unwrap();
|
||||
let request: S3Request = api.to_s3request().unwrap();
|
||||
|
||||
// Execute the request to get a response, store the bytes for swift cloning
|
||||
let bytes: bytes::Bytes = rt.block_on(async {
|
||||
@ -165,3 +166,11 @@ pub(crate) fn benchmark_s3_api<ApiType, GlobalSetupFuture>(
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
pub(crate) fn skip_express_mode(bench_name: &str) -> bool {
|
||||
let skip = TestContext::new_from_env().client.is_minio_express();
|
||||
if skip {
|
||||
println!("Skipping benchmark '{}' (MinIO Express mode)", bench_name);
|
||||
}
|
||||
skip
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
use async_std::future::timeout;
|
||||
use minio::s3::Client;
|
||||
|
||||
use std::thread;
|
||||
|
||||
/// Cleanup guard that removes the bucket when it is dropped
|
||||
@ -25,10 +26,10 @@ pub struct CleanupGuard {
|
||||
|
||||
impl CleanupGuard {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(client: &Client, bucket_name: &str) -> Self {
|
||||
pub fn new<S: Into<String>>(client: Client, bucket_name: S) -> Self {
|
||||
Self {
|
||||
client: client.clone(),
|
||||
bucket_name: bucket_name.to_string(),
|
||||
client,
|
||||
bucket_name: bucket_name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,7 +58,9 @@ impl Drop for CleanupGuard {
|
||||
Ok(_) => {
|
||||
//println!("Bucket {} removed successfully", bucket_name),
|
||||
}
|
||||
Err(e) => println!("Error removing bucket {}: {:?}", bucket_name, e),
|
||||
Err(_e) => {
|
||||
//println!("Error removing bucket {}: {:?}", bucket_name, e)
|
||||
}
|
||||
},
|
||||
Err(_) => println!("Timeout after 60s while removing bucket {}", bucket_name),
|
||||
}
|
||||
|
||||
@ -13,11 +13,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use minio::s3::args::PostPolicy;
|
||||
use chrono::{DateTime, Utc};
|
||||
use minio::s3::builders::PostPolicy;
|
||||
use minio::s3::types::{
|
||||
AndOperator, Destination, Filter, LifecycleConfig, LifecycleRule, NotificationConfig,
|
||||
ObjectLockConfig, PrefixFilterRule, QueueConfig, ReplicationConfig, ReplicationRule,
|
||||
RetentionMode, SuffixFilterRule,
|
||||
AndOperator, CsvInputSerialization, CsvOutputSerialization, Destination, FileHeaderInfo,
|
||||
Filter, LifecycleConfig, LifecycleRule, NotificationConfig, ObjectLockConfig, PrefixFilterRule,
|
||||
QueueConfig, QuoteFields, ReplicationConfig, ReplicationRule, RetentionMode, SelectRequest,
|
||||
SuffixFilterRule,
|
||||
};
|
||||
use minio::s3::utils::utc_now;
|
||||
use std::collections::HashMap;
|
||||
@ -185,7 +187,7 @@ pub fn create_object_lock_config_example() -> ObjectLockConfig {
|
||||
ObjectLockConfig::new(RetentionMode::GOVERNANCE, Some(DURATION_DAYS), None).unwrap()
|
||||
}
|
||||
pub fn create_post_policy_example(bucket_name: &str, object_name: &str) -> PostPolicy {
|
||||
let expiration = utc_now() + chrono::Duration::days(5);
|
||||
let expiration: DateTime<Utc> = utc_now() + chrono::Duration::days(5);
|
||||
|
||||
let mut policy = PostPolicy::new(&bucket_name, expiration).unwrap();
|
||||
policy.add_equals_condition("key", &object_name).unwrap();
|
||||
@ -194,3 +196,38 @@ pub fn create_post_policy_example(bucket_name: &str, object_name: &str) -> PostP
|
||||
.unwrap();
|
||||
policy
|
||||
}
|
||||
/// return (body, data)
|
||||
pub fn create_select_content_data() -> (String, String) {
|
||||
let mut data = String::new();
|
||||
data.push_str("1997,Ford,E350,\"ac, abs, moon\",3000.00\n");
|
||||
data.push_str("1999,Chevy,\"Venture \"\"Extended Edition\"\"\",,4900.00\n");
|
||||
data.push_str("1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00\n");
|
||||
data.push_str("1996,Jeep,Grand Cherokee,\"MUST SELL!\n");
|
||||
data.push_str("air, moon roof, loaded\",4799.00\n");
|
||||
let body = String::from("Year,Make,Model,Description,Price\n") + &data;
|
||||
(body, data)
|
||||
}
|
||||
pub fn create_select_content_request() -> SelectRequest {
|
||||
let request = SelectRequest::new_csv_input_output(
|
||||
"select * from S3Object",
|
||||
CsvInputSerialization {
|
||||
compression_type: None,
|
||||
allow_quoted_record_delimiter: false,
|
||||
comments: None,
|
||||
field_delimiter: None,
|
||||
file_header_info: Some(FileHeaderInfo::USE),
|
||||
quote_character: None,
|
||||
quote_escape_character: None,
|
||||
record_delimiter: None,
|
||||
},
|
||||
CsvOutputSerialization {
|
||||
field_delimiter: None,
|
||||
quote_character: None,
|
||||
quote_escape_character: None,
|
||||
quote_fields: Some(QuoteFields::ASNEEDED),
|
||||
record_delimiter: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
request
|
||||
}
|
||||
|
||||
@ -23,12 +23,12 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestContext {
|
||||
pub client: Client,
|
||||
pub base_url: BaseUrl,
|
||||
pub access_key: String,
|
||||
pub secret_key: String,
|
||||
pub ignore_cert_check: Option<bool>,
|
||||
pub ssl_cert_file: Option<PathBuf>,
|
||||
pub client: Client,
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
@ -66,12 +66,12 @@ impl TestContext {
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
client,
|
||||
base_url,
|
||||
access_key,
|
||||
secret_key,
|
||||
ignore_cert_check: Some(ignore_cert_check),
|
||||
ssl_cert_file: ssl_cert_file.map(PathBuf::from),
|
||||
client,
|
||||
}
|
||||
} else {
|
||||
const DEFAULT_SERVER_ENDPOINT: &str = "https://play.min.io/";
|
||||
@ -123,12 +123,12 @@ impl TestContext {
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
client,
|
||||
base_url,
|
||||
access_key,
|
||||
secret_key,
|
||||
ignore_cert_check: Some(ignore_cert_check),
|
||||
ssl_cert_file: Some(ssl_cert_file),
|
||||
client,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +144,7 @@ impl TestContext {
|
||||
/// - `CleanupGuard` - A guard that automatically deletes the bucket when dropped.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```ignore
|
||||
/// let (bucket_name, guard) = client.create_bucket_helper().await;
|
||||
/// println!("Created temporary bucket: {}", bucket_name);
|
||||
/// // The bucket will be removed when `guard` is dropped.
|
||||
@ -152,7 +152,7 @@ impl TestContext {
|
||||
pub async fn create_bucket_helper(&self) -> (String, CleanupGuard) {
|
||||
let bucket_name = rand_bucket_name();
|
||||
let _resp = self.client.make_bucket(&bucket_name).send().await.unwrap();
|
||||
let guard = CleanupGuard::new(&self.client, &bucket_name);
|
||||
let guard = CleanupGuard::new(self.client.clone(), &bucket_name);
|
||||
(bucket_name, guard)
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ pub fn rand_bucket_name() -> String {
|
||||
}
|
||||
|
||||
pub fn rand_object_name() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 20)
|
||||
}
|
||||
|
||||
pub async fn get_bytes_from_response(v: Result<reqwest::Response, Error>) -> bytes::Bytes {
|
||||
|
||||
85
examples/append_object.rs
Normal file
85
examples/append_object.rs
Normal file
@ -0,0 +1,85 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod common;
|
||||
|
||||
use crate::common::{create_bucket_if_not_exists, create_client_on_localhost};
|
||||
use minio::s3::Client;
|
||||
use minio::s3::response::{AppendObjectResponse, StatObjectResponse};
|
||||
use minio::s3::segmented_bytes::SegmentedBytes;
|
||||
use minio::s3::types::S3Api;
|
||||
use rand::Rng;
|
||||
use rand::distributions::Alphanumeric;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
env_logger::init(); // Note: set environment variable RUST_LOG="INFO" to log info and higher
|
||||
let client: Client = create_client_on_localhost()?;
|
||||
|
||||
if !client.is_minio_express() {
|
||||
println!("Need (MinIO) Express mode to run this example");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let bucket_name: &str = "append-test-bucket";
|
||||
create_bucket_if_not_exists(bucket_name, &client).await?;
|
||||
|
||||
let object_name: &str = "append-test-object";
|
||||
|
||||
let n_segments = 1000;
|
||||
let segment_size = 1024 * 1024; // 1 KB
|
||||
let mut offset_bytes = 0;
|
||||
|
||||
for i in 0..n_segments {
|
||||
let rand_str: String = random_string(segment_size);
|
||||
|
||||
let data_size = rand_str.len() as u64;
|
||||
let data: SegmentedBytes = SegmentedBytes::from(rand_str);
|
||||
|
||||
let resp: AppendObjectResponse = client
|
||||
.append_object(bucket_name, object_name, data, offset_bytes)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
offset_bytes += data_size;
|
||||
if resp.object_size != offset_bytes {
|
||||
panic!(
|
||||
"from the append_object: size mismatch: expected {}, got {}",
|
||||
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 {
|
||||
panic!(
|
||||
"from the stat_Object: size mismatch: expected {}, got {}",
|
||||
resp.size, offset_bytes
|
||||
)
|
||||
}
|
||||
println!("{}/{}", i, n_segments);
|
||||
//println!("Stat response: {:#?}", resp);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn random_string(len: usize) -> String {
|
||||
rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(len)
|
||||
.map(char::from)
|
||||
.collect()
|
||||
}
|
||||
@ -21,6 +21,19 @@ pub fn create_client_on_play() -> Result<Client, Box<dyn std::error::Error + Sen
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn create_client_on_localhost() -> Result<Client, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let base_url = "http://localhost:9000/".parse::<BaseUrl>()?;
|
||||
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
|
||||
|
||||
let static_provider = StaticProvider::new("minioadmin", "minioadmin", None);
|
||||
|
||||
let client = ClientBuilder::new(base_url.clone())
|
||||
.provider(Some(Box::new(static_provider)))
|
||||
.build()?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn create_bucket_if_not_exists(
|
||||
bucket_name: &str,
|
||||
client: &Client,
|
||||
|
||||
@ -16,10 +16,11 @@
|
||||
mod common;
|
||||
|
||||
use crate::common::create_bucket_if_not_exists;
|
||||
use minio::s3::builders::{ObjectContent, ObjectPrompt};
|
||||
use minio::s3::builders::ObjectContent;
|
||||
use minio::s3::client::ClientBuilder;
|
||||
use minio::s3::creds::StaticProvider;
|
||||
use minio::s3::http::BaseUrl;
|
||||
use minio::s3::response::ObjectPromptResponse;
|
||||
use minio::s3::types::S3Api;
|
||||
use std::path::Path;
|
||||
|
||||
@ -65,12 +66,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
filename.display()
|
||||
);
|
||||
|
||||
let op = ObjectPrompt::new(bucket_name, object_name, "what is it about?")
|
||||
let resp: ObjectPromptResponse = client
|
||||
.object_prompt(bucket_name, object_name, "what is it about?")
|
||||
//.lambda_arn("arn:minio:s3-object-lambda::_:webhook") // this is the default value
|
||||
.client(&client);
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let res = op.send().await?;
|
||||
log::info!("Object prompt result: '{}'", res.prompt_response);
|
||||
log::info!("Object prompt result: '{}'", resp.prompt_response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ use clap::Parser;
|
||||
use log::info;
|
||||
use minio::s3::response::BucketExistsResponse;
|
||||
use minio::s3::types::S3Api;
|
||||
use minio::s3::{builders::ObjectContent, client::ClientBuilder, creds::StaticProvider};
|
||||
use minio::s3::{Client, builders::ObjectContent, client::ClientBuilder, creds::StaticProvider};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Upload a file to the given bucket and object path on the MinIO Play server.
|
||||
@ -41,7 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
None,
|
||||
);
|
||||
|
||||
let client = ClientBuilder::new("https://play.min.io".parse()?)
|
||||
let client: Client = ClientBuilder::new("https://play.min.io".parse()?)
|
||||
.provider(Some(Box::new(static_provider)))
|
||||
.build()?;
|
||||
|
||||
|
||||
1273
src/s3/args.rs
1273
src/s3/args.rs
File diff suppressed because it is too large
Load Diff
@ -15,8 +15,10 @@
|
||||
|
||||
//! Argument builders for [minio::s3::client::Client](crate::s3::client::Client) APIs
|
||||
|
||||
mod append_object;
|
||||
mod bucket_common;
|
||||
mod bucket_exists;
|
||||
mod copy_object;
|
||||
mod delete_bucket_encryption;
|
||||
mod delete_bucket_lifecycle;
|
||||
mod delete_bucket_notification;
|
||||
@ -38,16 +40,19 @@ mod get_object;
|
||||
mod get_object_lock_config;
|
||||
mod get_object_retention;
|
||||
mod get_object_tags;
|
||||
mod get_presigned_object_url;
|
||||
mod get_presigned_policy_form_data;
|
||||
mod get_region;
|
||||
mod is_object_legal_hold_enabled;
|
||||
mod list_buckets;
|
||||
mod list_objects;
|
||||
mod listen_bucket_notification;
|
||||
mod make_bucket;
|
||||
mod object_content;
|
||||
mod object_prompt;
|
||||
mod put_object;
|
||||
mod remove_bucket;
|
||||
mod remove_objects;
|
||||
mod select_object_content;
|
||||
mod set_bucket_encryption;
|
||||
mod set_bucket_lifecycle;
|
||||
mod set_bucket_notification;
|
||||
@ -58,9 +63,13 @@ mod set_bucket_versioning;
|
||||
mod set_object_lock_config;
|
||||
mod set_object_retention;
|
||||
mod set_object_tags;
|
||||
mod stat_object;
|
||||
|
||||
pub use crate::s3::object_content::*;
|
||||
pub use append_object::*;
|
||||
pub use bucket_common::*;
|
||||
pub use bucket_exists::*;
|
||||
pub use copy_object::*;
|
||||
pub use delete_bucket_encryption::*;
|
||||
pub use delete_bucket_lifecycle::*;
|
||||
pub use delete_bucket_notification::*;
|
||||
@ -82,16 +91,19 @@ pub use get_object::*;
|
||||
pub use get_object_lock_config::*;
|
||||
pub use get_object_retention::*;
|
||||
pub use get_object_tags::*;
|
||||
pub use get_presigned_object_url::*;
|
||||
pub use get_presigned_policy_form_data::*;
|
||||
pub use get_region::*;
|
||||
pub use is_object_legal_hold_enabled::*;
|
||||
pub use list_buckets::*;
|
||||
pub use list_objects::*;
|
||||
pub use listen_bucket_notification::*;
|
||||
pub use make_bucket::*;
|
||||
pub use object_content::*;
|
||||
pub use object_prompt::*;
|
||||
pub use put_object::*;
|
||||
pub use remove_bucket::*;
|
||||
pub use remove_objects::*;
|
||||
pub use select_object_content::*;
|
||||
pub use set_bucket_encryption::*;
|
||||
pub use set_bucket_lifecycle::*;
|
||||
pub use set_bucket_notification::*;
|
||||
@ -102,3 +114,4 @@ pub use set_bucket_versioning::*;
|
||||
pub use set_object_lock_config::*;
|
||||
pub use set_object_retention::*;
|
||||
pub use set_object_tags::*;
|
||||
pub use stat_object::*;
|
||||
|
||||
331
src/s3/builders/append_object.rs
Normal file
331
src/s3/builders/append_object.rs
Normal file
@ -0,0 +1,331 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::{
|
||||
ContentStream, MAX_MULTIPART_COUNT, ObjectContent, Size, calc_part_info,
|
||||
};
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::{AppendObjectResponse, StatObjectResponse};
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::sse::Sse;
|
||||
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
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AppendObject {
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
bucket: String,
|
||||
object: String,
|
||||
|
||||
region: Option<String>,
|
||||
sse: Option<Arc<dyn Sse>>,
|
||||
data: SegmentedBytes,
|
||||
|
||||
/// value of x-amz-write-offset-bytes
|
||||
offset_bytes: u64,
|
||||
}
|
||||
|
||||
impl AppendObject {
|
||||
pub fn new(
|
||||
client: Client,
|
||||
bucket: String,
|
||||
object: String,
|
||||
data: SegmentedBytes,
|
||||
offset_bytes: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
offset_bytes,
|
||||
data,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_query_params(mut self, extra_query_params: Option<Multimap>) -> Self {
|
||||
self.extra_query_params = extra_query_params;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for AppendObject {
|
||||
type S3Response = AppendObjectResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for AppendObject {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
if let Some(v) = &self.sse {
|
||||
if v.tls_required() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
headers.add("x-amz-write-offset-bytes", self.offset_bytes.to_string());
|
||||
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(self.extra_query_params.unwrap_or_default())
|
||||
.object(Some(self.object))
|
||||
.headers(headers)
|
||||
.body(Some(self.data)))
|
||||
}
|
||||
}
|
||||
// endregion: append-object
|
||||
|
||||
// region: append-object-content
|
||||
|
||||
/// AppendObjectContent takes a `ObjectContent` stream and appends it to MinIO/S3.
|
||||
///
|
||||
/// It is a higher level API and handles multipart appends transparently.
|
||||
pub struct AppendObjectContent {
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
object: String,
|
||||
sse: Option<Arc<dyn Sse>>,
|
||||
part_size: Size,
|
||||
|
||||
// source data
|
||||
input_content: ObjectContent,
|
||||
|
||||
// Computed.
|
||||
content_stream: ContentStream,
|
||||
part_count: Option<u16>,
|
||||
|
||||
/// Value of x-amz-write-offset-bytes
|
||||
offset_bytes: u64,
|
||||
}
|
||||
|
||||
impl AppendObjectContent {
|
||||
pub fn new(
|
||||
client: Client,
|
||||
bucket: String,
|
||||
object: String,
|
||||
content: impl Into<ObjectContent>,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
input_content: content.into(),
|
||||
extra_headers: None,
|
||||
extra_query_params: None,
|
||||
region: None,
|
||||
sse: None,
|
||||
part_size: Size::Unknown,
|
||||
content_stream: ContentStream::empty(),
|
||||
part_count: None,
|
||||
offset_bytes: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_query_params(mut self, extra_query_params: Option<Multimap>) -> Self {
|
||||
self.extra_query_params = extra_query_params;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn region(mut self, region: Option<String>) -> Self {
|
||||
self.region = region;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn part_size(mut self, part_size: impl Into<Size>) -> Self {
|
||||
self.part_size = part_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset_bytes(mut self, offset_bytes: u64) -> Self {
|
||||
self.offset_bytes = offset_bytes;
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn send(mut self) -> Result<AppendObjectResponse, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
if let Some(v) = &self.sse {
|
||||
if v.tls_required() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut headers: Multimap = match self.extra_headers {
|
||||
Some(ref headers) => headers.clone(),
|
||||
None => Multimap::new(),
|
||||
};
|
||||
headers.add("x-amz-write-offset-bytes", self.offset_bytes.to_string());
|
||||
self.extra_query_params = Some(headers);
|
||||
}
|
||||
|
||||
self.content_stream = std::mem::take(&mut self.input_content)
|
||||
.to_content_stream()
|
||||
.await
|
||||
.map_err(Error::IOError)?;
|
||||
|
||||
// object_size may be Size::Unknown.
|
||||
let object_size = self.content_stream.get_size();
|
||||
|
||||
let (part_size, n_expected_parts) = calc_part_info(object_size, self.part_size)?;
|
||||
// Set the chosen part size and part count.
|
||||
self.part_size = Size::Known(part_size);
|
||||
self.part_count = n_expected_parts;
|
||||
|
||||
// Read the first part.
|
||||
let seg_bytes = self.content_stream.read_upto(part_size as usize).await?;
|
||||
|
||||
// get the length (if any) of the current file
|
||||
let resp: StatObjectResponse = self
|
||||
.client
|
||||
.stat_object(&self.bucket, &self.object)
|
||||
.send()
|
||||
.await?;
|
||||
//println!("statObjectResponse={:#?}", resp);
|
||||
|
||||
let current_file_size = resp.size;
|
||||
|
||||
// In the first part read, if:
|
||||
//
|
||||
// - object_size is unknown AND we got less than the part size, OR
|
||||
// - we are expecting only one part to be uploaded,
|
||||
//
|
||||
// we upload it as a simple put object.
|
||||
if (object_size.is_unknown() && (seg_bytes.len() as u64) < part_size)
|
||||
|| n_expected_parts == Some(1)
|
||||
{
|
||||
let ao = AppendObject {
|
||||
client: self.client,
|
||||
extra_headers: self.extra_headers,
|
||||
extra_query_params: self.extra_query_params,
|
||||
bucket: self.bucket,
|
||||
object: self.object,
|
||||
region: self.region,
|
||||
offset_bytes: current_file_size,
|
||||
sse: self.sse,
|
||||
data: seg_bytes,
|
||||
};
|
||||
ao.send().await
|
||||
} else if object_size.is_known() && (seg_bytes.len() as u64) < part_size {
|
||||
// Not enough data!
|
||||
let expected = object_size.as_u64().unwrap();
|
||||
let got = seg_bytes.len() as u64;
|
||||
Err(Error::InsufficientData(expected, got))
|
||||
} else {
|
||||
// Otherwise, we start a multipart append.
|
||||
self.send_mpa(part_size, current_file_size, seg_bytes).await
|
||||
}
|
||||
}
|
||||
|
||||
/// multipart append
|
||||
async fn send_mpa(
|
||||
&mut self,
|
||||
part_size: u64,
|
||||
object_size: u64,
|
||||
first_part: SegmentedBytes,
|
||||
) -> Result<AppendObjectResponse, Error> {
|
||||
let mut done = false;
|
||||
let mut part_number = 0;
|
||||
|
||||
let mut last_resp: Option<AppendObjectResponse> = None;
|
||||
let mut next_offset_bytes: u64 = object_size;
|
||||
//println!("initial offset_bytes: {}", next_offset_bytes);
|
||||
|
||||
let mut first_part = Some(first_part);
|
||||
while !done {
|
||||
let part_content: SegmentedBytes = {
|
||||
if let Some(v) = first_part.take() {
|
||||
v
|
||||
} else {
|
||||
self.content_stream.read_upto(part_size as usize).await?
|
||||
}
|
||||
};
|
||||
part_number += 1;
|
||||
let buffer_size = part_content.len() as u64;
|
||||
|
||||
assert!(
|
||||
buffer_size <= part_size,
|
||||
"{:?} <= {:?}",
|
||||
buffer_size,
|
||||
part_size
|
||||
);
|
||||
|
||||
if buffer_size == 0 && part_number > 1 {
|
||||
// We are done as we appended at least 1 part and we have
|
||||
// reached the end of the stream.
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we have too many parts to upload.
|
||||
if self.part_count.is_none() && part_number > MAX_MULTIPART_COUNT {
|
||||
return Err(Error::TooManyParts);
|
||||
}
|
||||
|
||||
// Append the part now.
|
||||
let append_object = AppendObject {
|
||||
client: self.client.clone(),
|
||||
extra_headers: self.extra_headers.clone(),
|
||||
extra_query_params: self.extra_query_params.clone(),
|
||||
bucket: self.bucket.clone(),
|
||||
object: self.object.clone(),
|
||||
region: self.region.clone(),
|
||||
sse: self.sse.clone(),
|
||||
data: part_content,
|
||||
offset_bytes: next_offset_bytes,
|
||||
};
|
||||
let resp: AppendObjectResponse = append_object.send().await?;
|
||||
//println!("AppendObjectResponse: object_size={:?}", resp.object_size);
|
||||
|
||||
next_offset_bytes = resp.object_size;
|
||||
|
||||
// Finally check if we are done.
|
||||
if buffer_size < part_size {
|
||||
done = true;
|
||||
last_resp = Some(resp);
|
||||
}
|
||||
}
|
||||
Ok(last_resp.unwrap())
|
||||
}
|
||||
}
|
||||
// endregion: append-object-content
|
||||
@ -15,11 +15,12 @@
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::s3::{client::Client, utils::Multimap};
|
||||
use crate::s3::client::Client;
|
||||
use crate::s3::multimap::Multimap;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BucketCommon<A> {
|
||||
pub(crate) client: Option<Client>,
|
||||
pub(crate) client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
@ -30,18 +31,14 @@ pub struct BucketCommon<A> {
|
||||
}
|
||||
|
||||
impl<A: Default> BucketCommon<A> {
|
||||
pub fn new(bucket: &str) -> BucketCommon<A> {
|
||||
pub fn new(client: Client, bucket: String) -> BucketCommon<A> {
|
||||
BucketCommon {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::BucketExistsResponse;
|
||||
@ -32,30 +31,13 @@ impl S3Api for BucketExists {
|
||||
}
|
||||
|
||||
impl ToS3Request for BucketExists {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::HEAD)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::HEAD)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(self.extra_query_params.unwrap_or_default())
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
1198
src/s3/builders/copy_object.rs
Normal file
1198
src/s3/builders/copy_object.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketEncryptionResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_bucket_encryption()](Client::delete_bucket_encryption) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketEncryption {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketEncryption {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("encryption".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "encryption"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketLifecycleResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_bucket_lifecycle()](Client::delete_bucket_lifecycle) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketLifecycle {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketLifecycle {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("lifecycle".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::{BucketCommon, SegmentedBytes};
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketNotificationResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{NotificationConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
@ -33,24 +33,9 @@ impl S3Api for DeleteBucketNotification {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketNotification {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("notification"), String::new());
|
||||
|
||||
const CONFIG: NotificationConfig = NotificationConfig {
|
||||
cloud_func_config_list: None,
|
||||
queue_config_list: None,
|
||||
@ -60,15 +45,11 @@ impl ToS3Request for DeleteBucketNotification {
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
//TODO consider const body
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "notification"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketPolicyResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_bucket_policy()](Client::delete_bucket_policy) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketPolicy {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketPolicy {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("policy"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "policy"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketReplicationResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_bucket_replication()](Client::delete_bucket_replication) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketReplication {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketReplication {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("replication".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "replication"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteBucketTagsResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_bucket_tags()](Client::delete_bucket_tags) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteBucketTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("tagging".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "tagging"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::{BucketCommon, SegmentedBytes};
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::DeleteObjectLockConfigResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{ObjectLockConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
@ -33,24 +33,9 @@ impl S3Api for DeleteObjectLockConfig {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteObjectLockConfig {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("object-lock"), String::new());
|
||||
|
||||
let config = ObjectLockConfig {
|
||||
retention_mode: None,
|
||||
retention_duration_days: None,
|
||||
@ -60,15 +45,11 @@ impl ToS3Request for DeleteObjectLockConfig {
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
//TODO consider const body
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,36 +15,35 @@
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::DeleteObjectTagsResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [delete_object_tags()](Client::delete_object_tags) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct DeleteObjectTags {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl DeleteObjectTags {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -61,11 +60,6 @@ impl DeleteObjectTags {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -77,36 +71,18 @@ impl S3Api for DeleteObjectTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for DeleteObjectTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert("tagging".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.object(Some(&self.object))
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,41 +14,39 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::DisableObjectLegalHoldResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, md5sum_hash};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert, md5sum_hash};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [disable_object_legal_hold()](Client::disable_object_legal_hold) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct DisableObjectLegalHold {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) object: String,
|
||||
pub(crate) version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl DisableObjectLegalHold {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -59,11 +57,6 @@ impl DisableObjectLegalHold {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -75,42 +68,25 @@ impl S3Api for DisableObjectLegalHold {
|
||||
}
|
||||
|
||||
impl ToS3Request for DisableObjectLegalHold {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let mut headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(String::from("legal-hold"), String::new());
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
const PAYLOAD: &str = "<LegalHold><Status>OFF</Status></LegalHold>";
|
||||
headers.insert(String::from("Content-MD5"), md5sum_hash(PAYLOAD.as_ref()));
|
||||
headers.add("Content-MD5", md5sum_hash(PAYLOAD.as_ref()));
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(PAYLOAD)));
|
||||
//TODO consider const body
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.object(Some(&self.object))
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,41 +14,39 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::EnableObjectLegalHoldResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, md5sum_hash};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert, md5sum_hash};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [enable_object_legal_hold()](Client::enable_object_legal_hold) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct EnableObjectLegalHold {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) object: String,
|
||||
pub(crate) version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl EnableObjectLegalHold {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -59,11 +57,6 @@ impl EnableObjectLegalHold {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -75,42 +68,25 @@ impl S3Api for EnableObjectLegalHold {
|
||||
}
|
||||
|
||||
impl ToS3Request for EnableObjectLegalHold {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let mut headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(String::from("legal-hold"), String::new());
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
const PAYLOAD: &str = "<LegalHold><Status>ON</Status></LegalHold>";
|
||||
headers.insert(String::from("Content-MD5"), md5sum_hash(PAYLOAD.as_ref()));
|
||||
headers.add("Content-MD5", md5sum_hash(PAYLOAD.as_ref()));
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(PAYLOAD)));
|
||||
//TODO consider const body
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.object(Some(&self.object))
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketEncryptionResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, merge};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
|
||||
@ -31,27 +31,13 @@ impl S3Api for GetBucketEncryption {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketEncryption {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
query_params.insert(String::from("encryption"), String::new());
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "encryption"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketLifecycleResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_lifecycle()](Client::get_bucket_lifecycle) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketLifecycle {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketLifecycle {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("lifecycle".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketNotificationResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_notification()](Client::get_bucket_notification) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketNotification {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketNotification {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("notification".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "notification"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketPolicyResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_policy()](Client::get_bucket_policy) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketPolicy {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketPolicy {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("policy"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "policy"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketReplicationResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_replication()](Client::get_bucket_replication) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketReplication {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketReplication {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("replication"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "replication"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,38 +15,35 @@
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::GetBucketTagsResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Argument builder for [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GetBucketTags {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) tags: HashMap<String, String>,
|
||||
tags: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl GetBucketTags {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -73,32 +70,13 @@ impl S3Api for GetBucketTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("tagging"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "tagging"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetBucketVersioningResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, merge};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API
|
||||
@ -31,27 +31,13 @@ impl S3Api for GetBucketVersioning {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetBucketVersioning {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
query_params.insert(String::from("versioning"), String::new());
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "versioning"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,19 +15,21 @@
|
||||
|
||||
use http::Method;
|
||||
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::utils::check_object_name;
|
||||
use crate::s3::{
|
||||
client::Client,
|
||||
error::Error,
|
||||
response::GetObjectResponse,
|
||||
sse::{Sse, SseCustomerKey},
|
||||
types::{S3Api, S3Request, ToS3Request},
|
||||
utils::{Multimap, UtcTime, check_bucket_name, merge, to_http_header_value},
|
||||
utils::{UtcTime, check_bucket_name, to_http_header_value},
|
||||
};
|
||||
|
||||
/// Argument builder for [list_objects()](Client::get_object) API.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct GetObject {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -46,21 +48,16 @@ pub struct GetObject {
|
||||
unmodified_since: Option<UtcTime>,
|
||||
}
|
||||
|
||||
// builder interface
|
||||
impl GetObject {
|
||||
pub fn new(bucket: &str, object: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_string(),
|
||||
object: object.to_string(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -117,99 +114,69 @@ impl GetObject {
|
||||
}
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
impl GetObject {
|
||||
fn get_range_header_value(&self) -> Option<String> {
|
||||
let (offset, length) = match self.length {
|
||||
Some(_) => (Some(self.offset.unwrap_or(0_u64)), self.length),
|
||||
None => (self.offset, None),
|
||||
};
|
||||
|
||||
if let Some(o) = offset {
|
||||
let mut range = String::new();
|
||||
range.push_str("bytes=");
|
||||
range.push_str(&o.to_string());
|
||||
range.push('-');
|
||||
if let Some(l) = length {
|
||||
range.push_str(&(o + l - 1).to_string());
|
||||
}
|
||||
Some(range)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_headers(&self) -> Multimap {
|
||||
let mut headers = Multimap::new();
|
||||
|
||||
if let Some(val) = self.get_range_header_value() {
|
||||
headers.insert(String::from("Range"), val);
|
||||
}
|
||||
|
||||
if let Some(v) = &self.match_etag {
|
||||
headers.insert(String::from("if-match"), v.to_string());
|
||||
}
|
||||
|
||||
if let Some(v) = &self.not_match_etag {
|
||||
headers.insert(String::from("if-none-match"), v.to_string());
|
||||
}
|
||||
|
||||
if let Some(v) = self.modified_since {
|
||||
headers.insert(String::from("if-modified-since"), to_http_header_value(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.unmodified_since {
|
||||
headers.insert(String::from("if-unmodified-since"), to_http_header_value(v));
|
||||
}
|
||||
|
||||
if let Some(v) = &self.ssec {
|
||||
merge(&mut headers, &v.headers());
|
||||
}
|
||||
|
||||
headers
|
||||
}
|
||||
}
|
||||
|
||||
impl ToS3Request for GetObject {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
if self.object.is_empty() {
|
||||
return Err(Error::InvalidObjectName(String::from(
|
||||
"object name cannot be empty",
|
||||
)));
|
||||
}
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
if self.ssec.is_some() && !client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
merge(&mut headers, &self.get_headers());
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.object(Some(&self.object))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for GetObject {
|
||||
type S3Response = GetObjectResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for GetObject {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
if self.ssec.is_some() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
{
|
||||
{
|
||||
let (offset, length): (Option<u64>, Option<u64>) = match self.length {
|
||||
Some(_) => (Some(self.offset.unwrap_or(0_u64)), self.length),
|
||||
None => (self.offset, None),
|
||||
};
|
||||
|
||||
if let Some(o) = offset {
|
||||
let mut range: String = String::new();
|
||||
range.push_str("bytes=");
|
||||
range.push_str(&o.to_string());
|
||||
range.push('-');
|
||||
if let Some(l) = length {
|
||||
range.push_str(&(o + l - 1).to_string());
|
||||
}
|
||||
headers.add("Range", range);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(v) = self.match_etag {
|
||||
headers.add("if-match", v);
|
||||
}
|
||||
|
||||
if let Some(v) = self.not_match_etag {
|
||||
headers.add("if-none-match", v);
|
||||
}
|
||||
|
||||
if let Some(v) = self.modified_since {
|
||||
headers.add("if-modified-since", to_http_header_value(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.unmodified_since {
|
||||
headers.add("if-unmodified-since", to_http_header_value(v));
|
||||
}
|
||||
|
||||
if let Some(v) = &self.ssec {
|
||||
headers.add_multimap(v.headers());
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.object(Some(self.object))
|
||||
.query_params(query_params)
|
||||
.headers(headers))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::GetObjectLockConfigResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_object_lock_config()](Client::get_object_lock_config) API
|
||||
@ -32,32 +31,13 @@ impl S3Api for GetObjectLockConfig {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetObjectLockConfig {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("object-lock"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,36 +15,35 @@
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::GetObjectRetentionResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_object_retention()](Client::get_object_retention) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GetObjectRetention {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl GetObjectRetention {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -61,11 +60,6 @@ impl GetObjectRetention {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -77,36 +71,18 @@ impl S3Api for GetObjectRetention {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetObjectRetention {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "retention");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(String::from("retention"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.object(Some(&self.object));
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,36 +15,35 @@
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::GetObjectTagsResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_object_tags()](Client::get_object_tags) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GetObjectTags {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl GetObjectTags {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -61,11 +60,6 @@ impl GetObjectTags {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -77,36 +71,18 @@ impl S3Api for GetObjectTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for GetObjectTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert("tagging".into(), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.object(Some(&self.object))
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
106
src/s3/builders/get_presigned_object_url.rs
Normal file
106
src/s3/builders/get_presigned_object_url.rs
Normal file
@ -0,0 +1,106 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::client::DEFAULT_EXPIRY_SECONDS;
|
||||
use crate::s3::creds::Credentials;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::GetPresignedObjectUrlResponse;
|
||||
use crate::s3::signer::presign_v4;
|
||||
use crate::s3::utils::{UtcTime, check_bucket_name, check_object_name, utc_now};
|
||||
use http::Method;
|
||||
|
||||
/// Argument for [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GetPresignedObjectUrl {
|
||||
client: Client,
|
||||
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
method: Method,
|
||||
expiry_seconds: Option<u32>,
|
||||
request_time: Option<UtcTime>,
|
||||
}
|
||||
|
||||
impl GetPresignedObjectUrl {
|
||||
pub fn new(client: Client, bucket: String, object: String, method: Method) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
method,
|
||||
expiry_seconds: Some(DEFAULT_EXPIRY_SECONDS),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send(self) -> Result<GetPresignedObjectUrlResponse, Error> {
|
||||
// NOTE: this send function is async to be comparable with other functions...
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let region: String = self.client.get_region_cached(&self.bucket, &self.region)?;
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
query_params.add_version(self.version_id.clone());
|
||||
|
||||
let mut url = self.client.shared.base_url.build_url(
|
||||
&self.method,
|
||||
®ion,
|
||||
&query_params,
|
||||
Some(&self.bucket),
|
||||
Some(&self.object),
|
||||
)?;
|
||||
|
||||
if let Some(p) = &self.client.shared.provider {
|
||||
let creds: Credentials = p.fetch();
|
||||
if let Some(t) = creds.session_token {
|
||||
query_params.add("X-Amz-Security-Token", t);
|
||||
}
|
||||
|
||||
let date = match self.request_time {
|
||||
Some(v) => v,
|
||||
_ => utc_now(),
|
||||
};
|
||||
|
||||
presign_v4(
|
||||
&self.method,
|
||||
&url.host_header_value(),
|
||||
&url.path,
|
||||
®ion,
|
||||
&mut query_params,
|
||||
&creds.access_key,
|
||||
&creds.secret_key,
|
||||
date,
|
||||
self.expiry_seconds.unwrap_or(DEFAULT_EXPIRY_SECONDS),
|
||||
);
|
||||
|
||||
url.query = query_params;
|
||||
}
|
||||
|
||||
Ok(GetPresignedObjectUrlResponse {
|
||||
region,
|
||||
bucket: self.bucket,
|
||||
object: self.object,
|
||||
version_id: self.version_id,
|
||||
url: url.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
347
src/s3/builders/get_presigned_policy_form_data.rs
Normal file
347
src/s3/builders/get_presigned_policy_form_data.rs
Normal file
@ -0,0 +1,347 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::creds::Credentials;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::signer::post_presign_v4;
|
||||
use crate::s3::utils::{
|
||||
UtcTime, b64encode, check_bucket_name, to_amz_date, to_iso8601utc, to_signer_date, utc_now,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Argument for [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API
|
||||
pub struct GetPresignedPolicyFormData {
|
||||
client: Client,
|
||||
policy: PostPolicy,
|
||||
}
|
||||
|
||||
impl GetPresignedPolicyFormData {
|
||||
pub fn new(client: Client, policy: PostPolicy) -> Self {
|
||||
Self { client, policy }
|
||||
}
|
||||
|
||||
pub async fn send(self) -> Result<HashMap<String, String>, Error> {
|
||||
// NOTE: this send function is async to be comparable with other functions...
|
||||
let region: String = self
|
||||
.client
|
||||
.get_region_cached(&self.policy.bucket, &self.policy.region)?;
|
||||
|
||||
let creds: Credentials = self.client.shared.provider.as_ref().unwrap().fetch();
|
||||
self.policy.form_data(
|
||||
creds.access_key,
|
||||
creds.secret_key,
|
||||
creds.session_token,
|
||||
region,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Post policy information for presigned post policy form-data
|
||||
///
|
||||
/// Condition elements and respective condition for Post policy is available <a
|
||||
/// href="https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html#sigv4-PolicyConditions">here</a>.
|
||||
pub struct PostPolicy {
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
|
||||
expiration: UtcTime,
|
||||
eq_conditions: HashMap<String, String>,
|
||||
starts_with_conditions: HashMap<String, String>,
|
||||
lower_limit: Option<usize>,
|
||||
upper_limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl PostPolicy {
|
||||
const EQ: &'static str = "eq";
|
||||
const STARTS_WITH: &'static str = "starts-with";
|
||||
const ALGORITHM: &'static str = "AWS4-HMAC-SHA256";
|
||||
|
||||
/// Returns post policy with given bucket name and expiration
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let policy = PostPolicy::new("bucket-name", expiration).unwrap();
|
||||
/// ```
|
||||
pub fn new(bucket_name: &str, expiration: UtcTime) -> Result<PostPolicy, Error> {
|
||||
check_bucket_name(bucket_name, true)?;
|
||||
|
||||
Ok(PostPolicy {
|
||||
region: None,
|
||||
bucket: bucket_name.to_owned(),
|
||||
expiration,
|
||||
eq_conditions: HashMap::new(),
|
||||
starts_with_conditions: HashMap::new(),
|
||||
lower_limit: None,
|
||||
upper_limit: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn trim_dollar(value: &str) -> String {
|
||||
let mut s = value.to_string();
|
||||
if s.starts_with('$') {
|
||||
s.remove(0);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn is_reserved_element(element: &str) -> bool {
|
||||
element.eq_ignore_ascii_case("bucket")
|
||||
|| element.eq_ignore_ascii_case("x-amz-algorithm")
|
||||
|| element.eq_ignore_ascii_case("x-amz-credential")
|
||||
|| element.eq_ignore_ascii_case("x-amz-date")
|
||||
|| element.eq_ignore_ascii_case("policy")
|
||||
|| element.eq_ignore_ascii_case("x-amz-signature")
|
||||
}
|
||||
|
||||
fn get_credential_string(access_key: &String, date: &UtcTime, region: &String) -> String {
|
||||
format!(
|
||||
"{}/{}/{}/s3/aws4_request",
|
||||
access_key,
|
||||
to_signer_date(*date),
|
||||
region
|
||||
)
|
||||
}
|
||||
|
||||
/// Adds equals condition for given element and value
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let mut policy = PostPolicy::new("my-bucket", expiration).unwrap();
|
||||
///
|
||||
/// // Add condition that 'key' (object name) equals to 'bucket-name'
|
||||
/// policy.add_equals_condition("key", "bucket-name").unwrap();
|
||||
/// ```
|
||||
pub fn add_equals_condition(&mut self, element: &str, value: &str) -> Result<(), Error> {
|
||||
if element.is_empty() {
|
||||
return Err(Error::PostPolicyError(
|
||||
"condition element cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let v = PostPolicy::trim_dollar(element);
|
||||
if v.eq_ignore_ascii_case("success_action_redirect")
|
||||
|| v.eq_ignore_ascii_case("redirect")
|
||||
|| v.eq_ignore_ascii_case("content-length-range")
|
||||
{
|
||||
return Err(Error::PostPolicyError(format!(
|
||||
"{} is unsupported for equals condition",
|
||||
element
|
||||
)));
|
||||
}
|
||||
|
||||
if PostPolicy::is_reserved_element(v.as_str()) {
|
||||
return Err(Error::PostPolicyError(format!("{} cannot set", element)));
|
||||
}
|
||||
|
||||
self.eq_conditions.insert(v, value.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes equals condition for given element
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let mut policy = PostPolicy::new("bucket-name", expiration).unwrap();
|
||||
/// policy.add_equals_condition("key", "bucket-name");
|
||||
///
|
||||
/// policy.remove_equals_condition("key");
|
||||
/// ```
|
||||
pub fn remove_equals_condition(&mut self, element: &str) {
|
||||
self.eq_conditions.remove(element);
|
||||
}
|
||||
|
||||
/// Adds starts-with condition for given element and value
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let mut policy = PostPolicy::new("bucket-name", expiration).unwrap();
|
||||
///
|
||||
/// // Add condition that 'Content-Type' starts with 'image/'
|
||||
/// policy.add_starts_with_condition("Content-Type", "image/").unwrap();
|
||||
/// ```
|
||||
pub fn add_starts_with_condition(&mut self, element: &str, value: &str) -> Result<(), Error> {
|
||||
if element.is_empty() {
|
||||
return Err(Error::PostPolicyError(
|
||||
"condition element cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let v = PostPolicy::trim_dollar(element);
|
||||
if v.eq_ignore_ascii_case("success_action_status")
|
||||
|| v.eq_ignore_ascii_case("content-length-range")
|
||||
|| (v.starts_with("x-amz-") && v.starts_with("x-amz-meta-"))
|
||||
{
|
||||
return Err(Error::PostPolicyError(format!(
|
||||
"{} is unsupported for starts-with condition",
|
||||
element
|
||||
)));
|
||||
}
|
||||
|
||||
if PostPolicy::is_reserved_element(v.as_str()) {
|
||||
return Err(Error::PostPolicyError(format!("{} cannot set", element)));
|
||||
}
|
||||
|
||||
self.starts_with_conditions.insert(v, value.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes starts-with condition for given element
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let mut policy = PostPolicy::new("bucket-name", expiration).unwrap();
|
||||
/// policy.add_starts_with_condition("Content-Type", "image/").unwrap();
|
||||
///
|
||||
/// policy.remove_starts_with_condition("Content-Type");
|
||||
/// ```
|
||||
pub fn remove_starts_with_condition(&mut self, element: &str) {
|
||||
self.starts_with_conditions.remove(element);
|
||||
}
|
||||
|
||||
/// Adds content-length range condition with given lower and upper limits
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use minio::s3::utils::*;
|
||||
/// use chrono::Duration;
|
||||
/// use minio::s3::builders::PostPolicy;
|
||||
///
|
||||
/// let expiration = utc_now() + Duration::days(7);
|
||||
/// let mut policy = PostPolicy::new("my-bucket", expiration).unwrap();
|
||||
///
|
||||
/// // Add condition that 'content-length-range' is between 64kiB to 10MiB
|
||||
/// policy.add_content_length_range_condition(64 * 1024, 10 * 1024 * 1024).unwrap();
|
||||
/// ```
|
||||
pub fn add_content_length_range_condition(
|
||||
&mut self,
|
||||
lower_limit: usize,
|
||||
upper_limit: usize,
|
||||
) -> Result<(), Error> {
|
||||
if lower_limit > upper_limit {
|
||||
return Err(Error::PostPolicyError(
|
||||
"lower limit cannot be greater than upper limit".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.lower_limit = Some(lower_limit);
|
||||
self.upper_limit = Some(upper_limit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes content-length range condition
|
||||
pub fn remove_content_length_range_condition(&mut self) {
|
||||
self.lower_limit = None;
|
||||
self.upper_limit = None;
|
||||
}
|
||||
|
||||
/// Generates form data for given access/secret keys, optional session token and region.
|
||||
/// The returned map contains `x-amz-algorithm`, `x-amz-credential`, `x-amz-security-token`, `x-amz-date`, `policy` and `x-amz-signature` keys and values.
|
||||
pub fn form_data(
|
||||
&self,
|
||||
access_key: String,
|
||||
secret_key: String,
|
||||
session_token: Option<String>,
|
||||
region: String,
|
||||
) -> Result<HashMap<String, String>, Error> {
|
||||
if region.is_empty() {
|
||||
return Err(Error::PostPolicyError("region cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
if !self.eq_conditions.contains_key("key")
|
||||
&& !self.starts_with_conditions.contains_key("key")
|
||||
{
|
||||
return Err(Error::PostPolicyError(
|
||||
"key condition must be set".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut conditions: Vec<Value> = Vec::new();
|
||||
conditions.push(json!([PostPolicy::EQ, "$bucket", self.bucket]));
|
||||
for (key, value) in &self.eq_conditions {
|
||||
conditions.push(json!([PostPolicy::EQ, String::from("$") + key, value]));
|
||||
}
|
||||
for (key, value) in &self.starts_with_conditions {
|
||||
conditions.push(json!([
|
||||
PostPolicy::STARTS_WITH,
|
||||
String::from("$") + key,
|
||||
value
|
||||
]));
|
||||
}
|
||||
if self.lower_limit.is_some() && self.upper_limit.is_some() {
|
||||
conditions.push(json!([
|
||||
"content-length-range",
|
||||
self.lower_limit.unwrap(),
|
||||
self.upper_limit.unwrap()
|
||||
]));
|
||||
}
|
||||
|
||||
let date = utc_now();
|
||||
let credential = PostPolicy::get_credential_string(&access_key, &date, ®ion);
|
||||
let amz_date = to_amz_date(date);
|
||||
conditions.push(json!([
|
||||
PostPolicy::EQ,
|
||||
"$x-amz-algorithm",
|
||||
PostPolicy::ALGORITHM
|
||||
]));
|
||||
conditions.push(json!([PostPolicy::EQ, "$x-amz-credential", credential]));
|
||||
if let Some(v) = &session_token {
|
||||
conditions.push(json!([PostPolicy::EQ, "$x-amz-security-token", v]));
|
||||
}
|
||||
conditions.push(json!([PostPolicy::EQ, "$x-amz-date", amz_date]));
|
||||
|
||||
let policy = json!({
|
||||
"expiration": to_iso8601utc(self.expiration),
|
||||
"conditions": conditions,
|
||||
});
|
||||
|
||||
let encoded_policy = b64encode(policy.to_string());
|
||||
let signature = post_presign_v4(&encoded_policy, &secret_key, date, ®ion);
|
||||
|
||||
let mut data: HashMap<String, String> = HashMap::new();
|
||||
data.insert("x-amz-algorithm".into(), PostPolicy::ALGORITHM.to_string());
|
||||
data.insert("x-amz-credential".into(), credential);
|
||||
data.insert("x-amz-date".into(), amz_date);
|
||||
data.insert("policy".into(), encoded_policy);
|
||||
data.insert("x-amz-signature".into(), signature);
|
||||
if let Some(v) = session_token {
|
||||
data.insert("x-amz-security-token".into(), v);
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
72
src/s3/builders/get_region.rs
Normal file
72
src/s3/builders/get_region.rs
Normal file
@ -0,0 +1,72 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::client::DEFAULT_REGION;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::GetRegionResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [get_region()](Client::get_region) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GetRegion {
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
bucket: String,
|
||||
}
|
||||
|
||||
impl GetRegion {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_query_params(mut self, extra_query_params: Option<Multimap>) -> Self {
|
||||
self.extra_query_params = extra_query_params;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct GetRegionPhantomData;
|
||||
|
||||
impl S3Api for GetRegion {
|
||||
type S3Response = GetRegionResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for GetRegion {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(Some(DEFAULT_REGION.to_string()))
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "location"))
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
@ -15,38 +15,36 @@
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::IsObjectLegalHoldEnabledResponse;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert};
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [is_object_legal_hold_enabled()](Client::is_object_legal_hold_enabled) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct IsObjectLegalHoldEnabled {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) object: String,
|
||||
pub(crate) version_id: Option<String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
impl IsObjectLegalHoldEnabled {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -57,11 +55,6 @@ impl IsObjectLegalHoldEnabled {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -73,36 +66,18 @@ impl S3Api for IsObjectLegalHoldEnabled {
|
||||
}
|
||||
|
||||
impl ToS3Request for IsObjectLegalHoldEnabled {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(String::from("legal-hold"), String::new());
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.object(Some(&self.object));
|
||||
|
||||
Ok(req)
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.object(Some(self.object)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,32 +15,29 @@
|
||||
|
||||
use http::Method;
|
||||
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::ListBucketsResponse;
|
||||
use crate::s3::{
|
||||
Client,
|
||||
error::Error,
|
||||
types::{S3Api, S3Request, ToS3Request},
|
||||
utils::Multimap,
|
||||
};
|
||||
|
||||
/// Argument builder for [list_buckets()](Client::list_buckets) API.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ListBuckets {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
}
|
||||
|
||||
// builder interface
|
||||
impl ListBuckets {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
@ -54,27 +51,14 @@ impl ListBuckets {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToS3Request for ListBuckets {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
headers = v.clone();
|
||||
}
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
query_params = v.clone();
|
||||
}
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for ListBuckets {
|
||||
type S3Response = ListBucketsResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for ListBuckets {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.query_params(self.extra_query_params.unwrap_or_default())
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@ use async_trait::async_trait;
|
||||
use futures_util::{Stream, StreamExt, stream as futures_stream};
|
||||
use http::Method;
|
||||
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::utils::insert;
|
||||
use crate::s3::{
|
||||
client::Client,
|
||||
error::Error,
|
||||
@ -24,34 +26,41 @@ use crate::s3::{
|
||||
ListObjectVersionsResponse, ListObjectsV1Response, ListObjectsV2Response,
|
||||
},
|
||||
types::{S3Api, S3Request, ToS3Request, ToStream},
|
||||
utils::{Multimap, check_bucket_name, merge},
|
||||
utils::check_bucket_name,
|
||||
};
|
||||
|
||||
fn add_common_list_objects_query_params(
|
||||
query_params: &mut Multimap,
|
||||
delimiter: Option<&str>,
|
||||
delimiter: Option<String>,
|
||||
disable_url_encoding: bool,
|
||||
max_keys: Option<u16>,
|
||||
prefix: Option<&str>,
|
||||
prefix: Option<String>,
|
||||
) {
|
||||
query_params.insert(
|
||||
String::from("delimiter"),
|
||||
delimiter.unwrap_or("").to_string(),
|
||||
);
|
||||
query_params.insert(
|
||||
String::from("max-keys"),
|
||||
max_keys.unwrap_or(1000).to_string(),
|
||||
);
|
||||
query_params.insert(String::from("prefix"), prefix.unwrap_or("").to_string());
|
||||
query_params.add("delimiter", delimiter.unwrap_or("".into()));
|
||||
query_params.add("max-keys", max_keys.unwrap_or(1000).to_string());
|
||||
query_params.add("prefix", prefix.unwrap_or("".into()));
|
||||
if !disable_url_encoding {
|
||||
query_params.insert(String::from("encoding-type"), String::from("url"));
|
||||
query_params.add("encoding-type", "url");
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function delimiter based on recursive flag when delimiter is not provided.
|
||||
fn delim_helper(delim: Option<String>, recursive: bool) -> Option<String> {
|
||||
if delim.is_some() {
|
||||
return delim;
|
||||
}
|
||||
match recursive {
|
||||
true => None,
|
||||
false => Some(String::from("/")),
|
||||
}
|
||||
}
|
||||
|
||||
// region: list-objects-v1
|
||||
|
||||
/// Argument for ListObjectsV1 S3 API.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ListObjectsV1 {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -70,76 +79,61 @@ impl ToStream for ListObjectsV1 {
|
||||
|
||||
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||
Box::new(Box::pin(futures_stream::unfold(
|
||||
(self.clone(), false),
|
||||
move |(mut args, mut is_done)| async move {
|
||||
(self, false),
|
||||
move |(args, mut is_done)| async move {
|
||||
// Stop the stream if no more data is available
|
||||
if is_done {
|
||||
return None;
|
||||
}
|
||||
let resp = args.send().await;
|
||||
match resp {
|
||||
// Prepare a clone of `args` for the next iteration
|
||||
let mut args_for_next_request: ListObjectsV1 = args.clone();
|
||||
|
||||
// Handle the result of the API call
|
||||
match args.send().await {
|
||||
Ok(resp) => {
|
||||
args.marker.clone_from(&resp.next_marker);
|
||||
// Update the marker for the next request
|
||||
args_for_next_request.marker.clone_from(&resp.next_marker);
|
||||
|
||||
// Determine if there are more results to fetch
|
||||
is_done = !resp.is_truncated;
|
||||
Some((Ok(resp), (args, is_done)))
|
||||
|
||||
// Return the response and prepare for the next iteration
|
||||
Some((Ok(resp), (args_for_next_request, is_done)))
|
||||
}
|
||||
Err(e) => Some((Err(e), (args, true))),
|
||||
Err(e) => Some((Err(e), (args_for_next_request, true))),
|
||||
}
|
||||
},
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToS3Request for ListObjectsV1 {
|
||||
fn to_s3request(&self) -> Result<S3Request<'_>, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter.as_deref(),
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix.as_deref(),
|
||||
);
|
||||
if let Some(v) = &self.marker {
|
||||
query_params.insert(String::from("marker"), v.to_string());
|
||||
}
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for ListObjectsV1 {
|
||||
type S3Response = ListObjectsV1Response;
|
||||
}
|
||||
|
||||
// Helper function delimiter based on recursive flag when delimiter is not
|
||||
// provided.
|
||||
fn delim_helper(delim: Option<String>, recursive: bool) -> Option<String> {
|
||||
if delim.is_some() {
|
||||
return delim;
|
||||
}
|
||||
match recursive {
|
||||
true => None,
|
||||
false => Some(String::from("/")),
|
||||
impl ToS3Request for ListObjectsV1 {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
{
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter,
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix,
|
||||
);
|
||||
if let Some(v) = self.marker {
|
||||
query_params.add("marker", v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,11 +153,14 @@ impl From<ListObjects> for ListObjectsV1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion: list-objects-v1
|
||||
|
||||
// region: list-objects-v2
|
||||
|
||||
/// Argument for ListObjectsV2 S3 API.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ListObjectsV2 {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -177,6 +174,7 @@ struct ListObjectsV2 {
|
||||
continuation_token: Option<String>,
|
||||
fetch_owner: bool,
|
||||
include_user_metadata: bool,
|
||||
unsorted: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -185,20 +183,28 @@ impl ToStream for ListObjectsV2 {
|
||||
|
||||
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||
Box::new(Box::pin(futures_stream::unfold(
|
||||
(self.clone(), false),
|
||||
move |(mut args, mut is_done)| async move {
|
||||
(self, false),
|
||||
move |(args, mut is_done)| async move {
|
||||
// Stop the stream if no more data is available
|
||||
if is_done {
|
||||
return None;
|
||||
}
|
||||
let resp = args.send().await;
|
||||
match resp {
|
||||
// Prepare a clone of `args` for the next iteration
|
||||
let mut args_for_next_request = args.clone();
|
||||
match args.send().await {
|
||||
Ok(resp) => {
|
||||
args.continuation_token
|
||||
// Update the continuation_token for the next request
|
||||
args_for_next_request
|
||||
.continuation_token
|
||||
.clone_from(&resp.next_continuation_token);
|
||||
|
||||
// Determine if there are more results to fetch
|
||||
is_done = !resp.is_truncated;
|
||||
Some((Ok(resp), (args, is_done)))
|
||||
|
||||
// Return the response and prepare for the next iteration
|
||||
Some((Ok(resp), (args_for_next_request, is_done)))
|
||||
}
|
||||
Err(e) => Some((Err(e), (args, true))),
|
||||
Err(e) => Some((Err(e), (args_for_next_request, true))),
|
||||
}
|
||||
},
|
||||
)))
|
||||
@ -210,48 +216,41 @@ impl S3Api for ListObjectsV2 {
|
||||
}
|
||||
|
||||
impl ToS3Request for ListObjectsV2 {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
{
|
||||
query_params.add("list-type", "2");
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter,
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix,
|
||||
);
|
||||
if let Some(v) = self.continuation_token {
|
||||
query_params.add("continuation-token", v);
|
||||
}
|
||||
if self.fetch_owner {
|
||||
query_params.add("fetch-owner", "true");
|
||||
}
|
||||
if let Some(v) = self.start_after {
|
||||
query_params.add("start-after", v);
|
||||
}
|
||||
if self.include_user_metadata {
|
||||
query_params.add("metadata", "true");
|
||||
}
|
||||
if self.unsorted {
|
||||
query_params.add("unsorted", "true");
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
query_params.insert(String::from("list-type"), String::from("2"));
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter.as_deref(),
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix.as_deref(),
|
||||
);
|
||||
if let Some(v) = &self.continuation_token {
|
||||
query_params.insert(String::from("continuation-token"), v.to_string());
|
||||
}
|
||||
if self.fetch_owner {
|
||||
query_params.insert(String::from("fetch-owner"), String::from("true"));
|
||||
}
|
||||
if let Some(v) = &self.start_after {
|
||||
query_params.insert(String::from("start-after"), v.to_string());
|
||||
}
|
||||
if self.include_user_metadata {
|
||||
query_params.insert(String::from("metadata"), String::from("true"));
|
||||
}
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,14 +270,18 @@ impl From<ListObjects> for ListObjectsV2 {
|
||||
continuation_token: value.continuation_token,
|
||||
fetch_owner: value.fetch_owner,
|
||||
include_user_metadata: value.include_user_metadata,
|
||||
unsorted: value.unsorted,
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion: list-objects-v2
|
||||
|
||||
/// Argument for ListObjectVerions S3 API
|
||||
// region: list-object-versions
|
||||
|
||||
/// Argument for ListObjectVersions S3 API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ListObjectVersions {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -291,6 +294,7 @@ struct ListObjectVersions {
|
||||
key_marker: Option<String>,
|
||||
version_id_marker: Option<String>,
|
||||
include_user_metadata: bool,
|
||||
unsorted: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -299,22 +303,32 @@ impl ToStream for ListObjectVersions {
|
||||
|
||||
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||
Box::new(Box::pin(futures_stream::unfold(
|
||||
(self.clone(), false),
|
||||
move |(mut args, mut is_done)| async move {
|
||||
(self, false),
|
||||
move |(args, mut is_done)| async move {
|
||||
// Stop the stream if no more data is available
|
||||
if is_done {
|
||||
return None;
|
||||
}
|
||||
let resp = args.send().await;
|
||||
match resp {
|
||||
// Prepare a clone of `args` for the next iteration
|
||||
let mut args_for_next_request = args.clone();
|
||||
match args.send().await {
|
||||
Ok(resp) => {
|
||||
args.key_marker.clone_from(&resp.next_key_marker);
|
||||
args.version_id_marker
|
||||
// Update the key_marker for the next request
|
||||
args_for_next_request
|
||||
.key_marker
|
||||
.clone_from(&resp.next_key_marker);
|
||||
// Update the version_id_marker for the next request
|
||||
args_for_next_request
|
||||
.version_id_marker
|
||||
.clone_from(&resp.next_version_id_marker);
|
||||
|
||||
// Determine if there are more results to fetch
|
||||
is_done = !resp.is_truncated;
|
||||
Some((Ok(resp), (args, is_done)))
|
||||
|
||||
// Return the response and prepare for the next iteration
|
||||
Some((Ok(resp), (args_for_next_request, is_done)))
|
||||
}
|
||||
Err(e) => Some((Err(e), (args, true))),
|
||||
Err(e) => Some((Err(e), (args_for_next_request, true))),
|
||||
}
|
||||
},
|
||||
)))
|
||||
@ -326,51 +340,43 @@ impl S3Api for ListObjectVersions {
|
||||
}
|
||||
|
||||
impl ToS3Request for ListObjectVersions {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "versions");
|
||||
{
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter,
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix,
|
||||
);
|
||||
if let Some(v) = self.key_marker {
|
||||
query_params.add("key-marker", v);
|
||||
}
|
||||
if let Some(v) = self.version_id_marker {
|
||||
query_params.add("version-id-marker", v);
|
||||
}
|
||||
if self.include_user_metadata {
|
||||
query_params.add("metadata", "true");
|
||||
}
|
||||
if self.unsorted {
|
||||
query_params.add("unsorted", "true");
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
query_params.insert(String::from("versions"), String::new());
|
||||
add_common_list_objects_query_params(
|
||||
&mut query_params,
|
||||
self.delimiter.as_deref(),
|
||||
self.disable_url_encoding,
|
||||
self.max_keys,
|
||||
self.prefix.as_deref(),
|
||||
);
|
||||
if let Some(v) = &self.key_marker {
|
||||
query_params.insert(String::from("key-marker"), v.to_string());
|
||||
}
|
||||
if let Some(v) = &self.version_id_marker {
|
||||
query_params.insert(String::from("version-id-marker"), v.to_string());
|
||||
}
|
||||
if self.include_user_metadata {
|
||||
query_params.insert(String::from("metadata"), String::from("true"));
|
||||
}
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::GET,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListObjects> for ListObjectVersions {
|
||||
fn from(value: ListObjects) -> Self {
|
||||
ListObjectVersions {
|
||||
Self {
|
||||
client: value.client,
|
||||
extra_headers: value.extra_headers,
|
||||
extra_query_params: value.extra_query_params,
|
||||
@ -383,10 +389,15 @@ impl From<ListObjects> for ListObjectVersions {
|
||||
key_marker: value.key_marker,
|
||||
version_id_marker: value.version_id_marker,
|
||||
include_user_metadata: value.include_user_metadata,
|
||||
unsorted: value.unsorted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: list-object-versions
|
||||
|
||||
// region: list-objects
|
||||
|
||||
/// Argument builder for
|
||||
/// [list_objects()](crate::s3::client::Client::list_objects) API.
|
||||
///
|
||||
@ -395,7 +406,7 @@ impl From<ListObjects> for ListObjectVersions {
|
||||
/// a stream of results. Pagination is automatically performed.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ListObjects {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
// Parameters common to all ListObjects APIs.
|
||||
extra_headers: Option<Multimap>,
|
||||
@ -424,6 +435,7 @@ pub struct ListObjects {
|
||||
recursive: bool,
|
||||
use_api_v1: bool,
|
||||
include_versions: bool,
|
||||
unsorted: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -445,18 +457,13 @@ impl ToStream for ListObjects {
|
||||
}
|
||||
|
||||
impl ListObjects {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -556,4 +563,11 @@ impl ListObjects {
|
||||
self.include_versions = include_versions;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set this to allow unsorted versions. Defaults to false
|
||||
pub fn unsorted(mut self, unsorted: bool) -> Self {
|
||||
self.unsorted = unsorted;
|
||||
self
|
||||
}
|
||||
}
|
||||
// endregion: list-objects
|
||||
|
||||
@ -17,12 +17,13 @@ use async_trait::async_trait;
|
||||
use futures_util::Stream;
|
||||
use http::Method;
|
||||
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::{
|
||||
client::Client,
|
||||
error::Error,
|
||||
response::ListenBucketNotificationResponse,
|
||||
types::{NotificationRecords, S3Api, S3Request, ToS3Request},
|
||||
utils::{Multimap, check_bucket_name, merge},
|
||||
utils::check_bucket_name,
|
||||
};
|
||||
|
||||
/// Argument builder for
|
||||
@ -30,7 +31,7 @@ use crate::s3::{
|
||||
/// API.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ListenBucketNotification {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -41,72 +42,15 @@ pub struct ListenBucketNotification {
|
||||
events: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl S3Api for ListenBucketNotification {
|
||||
type S3Response = (
|
||||
ListenBucketNotificationResponse,
|
||||
Box<dyn Stream<Item = Result<NotificationRecords, Error>> + Unpin + Send>,
|
||||
);
|
||||
}
|
||||
|
||||
impl ToS3Request for ListenBucketNotification {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
let client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
if client.is_aws_host() {
|
||||
return Err(Error::UnsupportedApi(String::from(
|
||||
"ListenBucketNotification",
|
||||
)));
|
||||
}
|
||||
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
if let Some(v) = &self.prefix {
|
||||
query_params.insert(String::from("prefix"), v.to_string());
|
||||
}
|
||||
if let Some(v) = &self.suffix {
|
||||
query_params.insert(String::from("suffix"), v.to_string());
|
||||
}
|
||||
if let Some(v) = &self.events {
|
||||
for e in v.iter() {
|
||||
query_params.insert(String::from("events"), e.to_string());
|
||||
}
|
||||
} else {
|
||||
query_params.insert(String::from("events"), String::from("s3:ObjectCreated:*"));
|
||||
query_params.insert(String::from("events"), String::from("s3:ObjectRemoved:*"));
|
||||
query_params.insert(String::from("events"), String::from("s3:ObjectAccessed:*"));
|
||||
}
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl ListenBucketNotification {
|
||||
pub fn new(bucket_name: &str) -> ListenBucketNotification {
|
||||
ListenBucketNotification {
|
||||
bucket: bucket_name.to_owned(),
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -137,3 +81,47 @@ impl ListenBucketNotification {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl S3Api for ListenBucketNotification {
|
||||
type S3Response = (
|
||||
ListenBucketNotificationResponse,
|
||||
Box<dyn Stream<Item = Result<NotificationRecords, Error>> + Unpin + Send>,
|
||||
);
|
||||
}
|
||||
|
||||
impl ToS3Request for ListenBucketNotification {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
if self.client.is_aws_host() {
|
||||
return Err(Error::UnsupportedApi("ListenBucketNotification".into()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
{
|
||||
if let Some(v) = self.prefix {
|
||||
query_params.add("prefix", v);
|
||||
}
|
||||
if let Some(v) = self.suffix {
|
||||
query_params.add("suffix", v);
|
||||
}
|
||||
if let Some(v) = self.events {
|
||||
for e in v.into_iter() {
|
||||
query_params.add("events", e);
|
||||
}
|
||||
} else {
|
||||
query_params.add("events", "s3:ObjectCreated:*");
|
||||
query_params.add("events", "s3:ObjectRemoved:*");
|
||||
query_params.add("events", "s3:ObjectAccessed:*");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,41 +14,37 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::client::DEFAULT_REGION;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::http::BaseUrl;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::MakeBucketResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::check_bucket_name;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [make_bucket()](Client::make_bucket) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MakeBucket {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object_lock: bool,
|
||||
object_lock: bool,
|
||||
}
|
||||
|
||||
impl MakeBucket {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -70,64 +66,37 @@ impl MakeBucket {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct MakeBucketPhantomData;
|
||||
|
||||
impl S3Api for MakeBucket {
|
||||
type S3Response = MakeBucketResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for MakeBucket {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let base_url: &BaseUrl = match &self.client {
|
||||
None => return Err(Error::NoClientProvided),
|
||||
Some(c) => &c.base_url,
|
||||
};
|
||||
|
||||
let region1: Option<&str> = self.region.as_deref();
|
||||
let region2: Option<&str> = if base_url.region.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(base_url.region.as_str())
|
||||
};
|
||||
let region2: Option<&str> = self.client.get_region_from_url();
|
||||
|
||||
let region: &str = match (region1, region2) {
|
||||
(None, None) => DEFAULT_REGION,
|
||||
(Some(r), None) | (None, Some(r)) => r, // Take the non-None value
|
||||
(Some(r1), Some(r2)) if r1 == r2 => r1, // Both are Some and equal
|
||||
let region_str: String = match (region1, region2) {
|
||||
(None, None) => DEFAULT_REGION.to_string(),
|
||||
(Some(_), None) => self.region.unwrap(),
|
||||
(None, Some(v)) => v.to_string(),
|
||||
(Some(r1), Some(r2)) if r1 == r2 => self.region.unwrap(), // Both are Some and equal
|
||||
(Some(r1), Some(r2)) => {
|
||||
return Err(Error::RegionMismatch(r1.to_string(), r2.to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
let mut headers: Multimap = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
if self.object_lock {
|
||||
headers.insert(
|
||||
String::from("x-amz-bucket-object-lock-enabled"),
|
||||
String::from("true"),
|
||||
);
|
||||
headers.add("x-amz-bucket-object-lock-enabled", "true");
|
||||
}
|
||||
|
||||
let query_params: Multimap = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let data: String = match region {
|
||||
let data: String = match region_str.as_str() {
|
||||
DEFAULT_REGION => String::new(),
|
||||
_ => format!(
|
||||
"<CreateBucketConfiguration><LocationConstraint>{}</LocationConstraint></CreateBucketConfiguration>",
|
||||
region
|
||||
region_str
|
||||
),
|
||||
};
|
||||
|
||||
@ -136,15 +105,11 @@ impl ToS3Request for MakeBucket {
|
||||
false => Some(SegmentedBytes::from(data)),
|
||||
};
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(Some(region))
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(Some(region_str))
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(self.extra_query_params.unwrap_or_default())
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,9 +13,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::sse::{Sse, SseCustomerKey};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, merge};
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::sse::SseCustomerKey;
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name};
|
||||
use crate::s3::{
|
||||
client::Client,
|
||||
error::Error,
|
||||
@ -28,7 +29,7 @@ use serde_json::json;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjectPrompt {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
bucket: String,
|
||||
object: String,
|
||||
prompt: String,
|
||||
@ -43,21 +44,16 @@ pub struct ObjectPrompt {
|
||||
|
||||
// builder interface
|
||||
impl ObjectPrompt {
|
||||
pub fn new(bucket: &str, object: &str, prompt: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String, prompt: String) -> Self {
|
||||
ObjectPrompt {
|
||||
client: None,
|
||||
bucket: bucket.to_string(),
|
||||
object: object.to_string(),
|
||||
prompt: prompt.to_string(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
prompt,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn lambda_arn(mut self, lambda_arn: &str) -> Self {
|
||||
self.lambda_arn = Some(lambda_arn.to_string());
|
||||
self
|
||||
@ -89,47 +85,28 @@ impl ObjectPrompt {
|
||||
}
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
impl ObjectPrompt {
|
||||
fn get_headers(&self) -> Multimap {
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.ssec {
|
||||
merge(&mut headers, &v.headers());
|
||||
}
|
||||
headers
|
||||
}
|
||||
impl S3Api for ObjectPrompt {
|
||||
type S3Response = ObjectPromptResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for ObjectPrompt {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
if self.object.is_empty() {
|
||||
return Err(Error::InvalidObjectName(String::from(
|
||||
"object name cannot be empty",
|
||||
)));
|
||||
if self.client.is_aws_host() {
|
||||
return Err(Error::UnsupportedApi("ObjectPrompt".into()));
|
||||
}
|
||||
if self.ssec.is_some() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if self.ssec.is_some() && !client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
merge(&mut headers, &self.get_headers());
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(
|
||||
String::from("lambdaArn"),
|
||||
query_params.add(
|
||||
"lambdaArn",
|
||||
self.lambda_arn
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
@ -139,18 +116,12 @@ impl ToS3Request for ObjectPrompt {
|
||||
let prompt_body = json!({ "prompt": self.prompt });
|
||||
let body: SegmentedBytes = SegmentedBytes::from(Bytes::from(prompt_body.to_string()));
|
||||
|
||||
let req = S3Request::new(client, Method::POST)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.object(Some(&self.object))
|
||||
Ok(S3Request::new(self.client, Method::POST)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.object(Some(self.object))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(Some(body));
|
||||
|
||||
Ok(req)
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(Some(body)))
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for ObjectPrompt {
|
||||
type S3Response = ObjectPromptResponse;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::BucketCommon;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::response::RemoveBucketResponse;
|
||||
@ -32,30 +31,13 @@ impl S3Api for RemoveBucket {
|
||||
}
|
||||
|
||||
impl ToS3Request for RemoveBucket {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::DELETE)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(self.extra_query_params.unwrap_or_default())
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,26 +15,30 @@
|
||||
|
||||
//! Builders for RemoveObject APIs.
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures_util::{Stream, StreamExt, stream as futures_stream};
|
||||
use http::Method;
|
||||
use std::pin::Pin;
|
||||
|
||||
use tokio_stream::iter as stream_iter;
|
||||
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::DeleteError;
|
||||
use crate::s3::types::ListEntry;
|
||||
use crate::s3::utils::{check_object_name, insert};
|
||||
use crate::s3::{
|
||||
Client,
|
||||
client_core::ClientCore,
|
||||
error::Error,
|
||||
response::{RemoveObjectResponse, RemoveObjectsResponse},
|
||||
types::{S3Api, S3Request, ToS3Request, ToStream},
|
||||
utils::{Multimap, check_bucket_name, md5sum_hash, merge},
|
||||
utils::{check_bucket_name, md5sum_hash},
|
||||
};
|
||||
|
||||
// region: object-to-delete
|
||||
/// Specify an object to be deleted. The object can be specified by key or by
|
||||
/// key and version_id via the From trait.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjectToDelete {
|
||||
key: String,
|
||||
version_id: Option<String>,
|
||||
@ -43,8 +47,8 @@ pub struct ObjectToDelete {
|
||||
/// A key can be converted into a DeleteObject. The version_id is set to None.
|
||||
impl From<&str> for ObjectToDelete {
|
||||
fn from(key: &str) -> Self {
|
||||
ObjectToDelete {
|
||||
key: key.to_string(),
|
||||
Self {
|
||||
key: key.to_owned(),
|
||||
version_id: None,
|
||||
}
|
||||
}
|
||||
@ -53,7 +57,7 @@ impl From<&str> for ObjectToDelete {
|
||||
/// A tuple of key and version_id can be converted into a DeleteObject.
|
||||
impl From<(&str, &str)> for ObjectToDelete {
|
||||
fn from((key, version_id): (&str, &str)) -> Self {
|
||||
ObjectToDelete {
|
||||
Self {
|
||||
key: key.to_string(),
|
||||
version_id: Some(version_id.to_string()),
|
||||
}
|
||||
@ -63,48 +67,58 @@ impl From<(&str, &str)> for ObjectToDelete {
|
||||
/// A tuple of key and option version_id can be converted into a DeleteObject.
|
||||
impl From<(&str, Option<&str>)> for ObjectToDelete {
|
||||
fn from((key, version_id): (&str, Option<&str>)) -> Self {
|
||||
ObjectToDelete {
|
||||
Self {
|
||||
key: key.to_string(),
|
||||
version_id: version_id.map(|v| v.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
impl From<ListEntry> for ObjectToDelete {
|
||||
fn from(entry: ListEntry) -> Self {
|
||||
Self {
|
||||
key: entry.name,
|
||||
version_id: entry.version_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeleteError> for ObjectToDelete {
|
||||
fn from(entry: DeleteError) -> Self {
|
||||
Self {
|
||||
key: entry.object_name,
|
||||
version_id: entry.version_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: object-to-delete
|
||||
|
||||
// region: remove-object
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RemoveObject {
|
||||
client: Option<Client>,
|
||||
|
||||
bucket: String,
|
||||
object: ObjectToDelete,
|
||||
|
||||
bypass_governance_mode: bool,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
object: ObjectToDelete,
|
||||
bypass_governance_mode: bool,
|
||||
}
|
||||
|
||||
impl RemoveObject {
|
||||
pub fn new(bucket: &str, object: impl Into<ObjectToDelete>) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: impl Into<ObjectToDelete>) -> Self {
|
||||
Self {
|
||||
client: None,
|
||||
|
||||
bucket: bucket.to_string(),
|
||||
client,
|
||||
bucket,
|
||||
object: object.into(),
|
||||
|
||||
bypass_governance_mode: false,
|
||||
|
||||
extra_headers: None,
|
||||
extra_query_params: None,
|
||||
region: None,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||
self.bypass_governance_mode = bypass_governance_mode;
|
||||
self
|
||||
@ -126,43 +140,33 @@ impl RemoveObject {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToS3Request for RemoveObject {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
}
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
if let Some(v) = &self.object.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
|
||||
let req = S3Request::new(
|
||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
||||
Method::DELETE,
|
||||
)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.object(Some(&self.object.key))
|
||||
.query_params(query_params)
|
||||
.headers(headers);
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for RemoveObject {
|
||||
type S3Response = RemoveObjectResponse;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
impl ToS3Request for RemoveObject {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object.key)?;
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
query_params.add_version(self.object.version_id);
|
||||
|
||||
Ok(S3Request::new(self.client, Method::DELETE)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.object(Some(self.object.key))
|
||||
.query_params(query_params)
|
||||
.headers(self.extra_headers.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: remove-object
|
||||
|
||||
// region: remove-object-api
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RemoveObjectsApi {
|
||||
client: Option<ClientCore>,
|
||||
client: Client,
|
||||
|
||||
bucket: String,
|
||||
objects: Vec<ObjectToDelete>,
|
||||
@ -176,27 +180,16 @@ pub struct RemoveObjectsApi {
|
||||
}
|
||||
|
||||
impl RemoveObjectsApi {
|
||||
pub fn new(bucket: &str, objects: Vec<ObjectToDelete>) -> Self {
|
||||
#[inline]
|
||||
pub fn new(client: Client, bucket: String, objects: Vec<ObjectToDelete>) -> Self {
|
||||
RemoveObjectsApi {
|
||||
client: None,
|
||||
|
||||
bucket: bucket.to_string(),
|
||||
client,
|
||||
bucket,
|
||||
objects,
|
||||
|
||||
bypass_governance_mode: false,
|
||||
verbose_mode: false,
|
||||
|
||||
extra_headers: None,
|
||||
extra_query_params: None,
|
||||
region: None,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &ClientCore) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||
self.bypass_governance_mode = bypass_governance_mode;
|
||||
self
|
||||
@ -226,8 +219,12 @@ impl RemoveObjectsApi {
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for RemoveObjectsApi {
|
||||
type S3Response = RemoveObjectsResponse;
|
||||
}
|
||||
|
||||
impl ToS3Request for RemoveObjectsApi {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut data = String::from("<Delete>");
|
||||
@ -249,56 +246,40 @@ impl ToS3Request for RemoveObjectsApi {
|
||||
data.push_str("</Delete>");
|
||||
let data: Bytes = data.into();
|
||||
|
||||
let mut headers = Multimap::new();
|
||||
if let Some(v) = &self.extra_headers {
|
||||
merge(&mut headers, v);
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
{
|
||||
if self.bypass_governance_mode {
|
||||
headers.add("x-amz-bypass-governance-retention", "true");
|
||||
}
|
||||
headers.add("Content-Type", "application/xml");
|
||||
headers.add("Content-MD5", md5sum_hash(data.as_ref()));
|
||||
}
|
||||
if self.bypass_governance_mode {
|
||||
headers.insert(
|
||||
String::from("x-amz-bypass-governance-retention"),
|
||||
String::from("true"),
|
||||
);
|
||||
}
|
||||
headers.insert(
|
||||
String::from("Content-Type"),
|
||||
String::from("application/xml"),
|
||||
);
|
||||
headers.insert(String::from("Content-MD5"), md5sum_hash(data.as_ref()));
|
||||
|
||||
let mut query_params = Multimap::new();
|
||||
if let Some(v) = &self.extra_query_params {
|
||||
merge(&mut query_params, v);
|
||||
}
|
||||
query_params.insert(String::from("delete"), String::new());
|
||||
|
||||
let client = self.client.as_ref().ok_or(Error::NoClientProvided)?.inner();
|
||||
let req = S3Request::new(client, Method::POST)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
Ok(S3Request::new(self.client, Method::POST)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "delete"))
|
||||
.headers(headers)
|
||||
.body(Some(data.into()));
|
||||
Ok(req)
|
||||
.body(Some(data.into())))
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for RemoveObjectsApi {
|
||||
type S3Response = RemoveObjectsResponse;
|
||||
}
|
||||
// endregion: remove-object-api
|
||||
|
||||
// region: delete-object
|
||||
pub struct DeleteObjects {
|
||||
items: Pin<Box<dyn Stream<Item = ObjectToDelete> + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl DeleteObjects {
|
||||
pub fn from_stream(s: impl Stream<Item = ObjectToDelete> + Send + Sync + 'static) -> Self {
|
||||
DeleteObjects { items: Box::pin(s) }
|
||||
Self { items: Box::pin(s) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectToDelete> for DeleteObjects {
|
||||
fn from(delete_object: ObjectToDelete) -> Self {
|
||||
DeleteObjects::from_stream(stream_iter(std::iter::once(delete_object)))
|
||||
Self::from_stream(stream_iter(std::iter::once(delete_object)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,12 +288,16 @@ where
|
||||
I: Iterator<Item = ObjectToDelete> + Send + Sync + 'static,
|
||||
{
|
||||
fn from(keys: I) -> Self {
|
||||
DeleteObjects::from_stream(stream_iter(keys))
|
||||
Self::from_stream(stream_iter(keys))
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: delete-object
|
||||
|
||||
// region: remove-objects
|
||||
|
||||
pub struct RemoveObjects {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
bucket: String,
|
||||
objects: DeleteObjects,
|
||||
@ -326,11 +311,10 @@ pub struct RemoveObjects {
|
||||
}
|
||||
|
||||
impl RemoveObjects {
|
||||
pub fn new(bucket: &str, objects: impl Into<DeleteObjects>) -> Self {
|
||||
RemoveObjects {
|
||||
client: None,
|
||||
|
||||
bucket: bucket.to_string(),
|
||||
pub fn new(client: Client, bucket: String, objects: impl Into<DeleteObjects>) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
objects: objects.into(),
|
||||
|
||||
bypass_governance_mode: false,
|
||||
@ -342,11 +326,6 @@ impl RemoveObjects {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||
self.bypass_governance_mode = bypass_governance_mode;
|
||||
self
|
||||
@ -386,15 +365,15 @@ impl RemoveObjects {
|
||||
if objects.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let client_core = ClientCore::new(self.client.as_ref().ok_or(Error::NoClientProvided)?);
|
||||
let request = RemoveObjectsApi::new(&self.bucket, objects)
|
||||
.client(&client_core)
|
||||
.bypass_governance_mode(self.bypass_governance_mode)
|
||||
.verbose_mode(self.verbose_mode)
|
||||
.extra_headers(self.extra_headers.clone())
|
||||
.extra_query_params(self.extra_query_params.clone())
|
||||
.region(self.region.clone());
|
||||
Ok(Some(request))
|
||||
|
||||
Ok(Some(
|
||||
RemoveObjectsApi::new(self.client.clone(), self.bucket.clone(), objects)
|
||||
.bypass_governance_mode(self.bypass_governance_mode)
|
||||
.verbose_mode(self.verbose_mode)
|
||||
.extra_headers(self.extra_headers.clone())
|
||||
.extra_query_params(self.extra_query_params.clone())
|
||||
.region(self.region.clone()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,3 +399,5 @@ impl ToStream for RemoveObjects {
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: remove-objects
|
||||
|
||||
120
src/s3/builders/select_object_content.rs
Normal file
120
src/s3/builders/select_object_content.rs
Normal file
@ -0,0 +1,120 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::SelectObjectContentResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::sse::SseCustomerKey;
|
||||
use crate::s3::types::{S3Api, S3Request, SelectRequest, ToS3Request};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert, md5sum_hash};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [bucket_exists()](Client::bucket_exists) API
|
||||
#[derive(Default)]
|
||||
pub struct SelectObjectContent {
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
ssec: Option<SseCustomerKey>,
|
||||
request: SelectRequest,
|
||||
}
|
||||
|
||||
impl SelectObjectContent {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_query_params(mut self, extra_query_params: Option<Multimap>) -> Self {
|
||||
self.extra_query_params = extra_query_params;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn region(mut self, region: Option<String>) -> Self {
|
||||
self.region = region;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ssec(mut self, ssec: Option<SseCustomerKey>) -> Self {
|
||||
self.ssec = ssec;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn request(mut self, request: SelectRequest) -> Self {
|
||||
self.request = request;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for SelectObjectContent {
|
||||
type S3Response = SelectObjectContentResponse;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ToS3Request for SelectObjectContent {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
if self.ssec.is_some() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
let region: String = self.client.get_region_cached(&self.bucket, &self.region)?;
|
||||
let data = self.request.to_xml();
|
||||
let bytes: Bytes = data.into();
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
headers.add("Content-MD5", md5sum_hash(bytes.as_ref()));
|
||||
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "select");
|
||||
query_params.add("select-type", "2");
|
||||
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
Ok(S3Request::new(self.client, Method::POST)
|
||||
.region(Some(region))
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.object(Some(self.object))
|
||||
.headers(headers)
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
@ -14,18 +14,19 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketEncryptionResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, SseConfig, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_bucket_encryption()](Client::set_bucket_encryption) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketEncryption {
|
||||
client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
@ -36,18 +37,14 @@ pub struct SetBucketEncryption {
|
||||
}
|
||||
|
||||
impl SetBucketEncryption {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -74,35 +71,17 @@ impl S3Api for SetBucketEncryption {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketEncryption {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("encryption".into(), String::new());
|
||||
|
||||
let bytes: Bytes = self.config.to_xml().into();
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::GET)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "encryption"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,38 +14,36 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::SetBucketLifecycleResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{LifecycleConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name, md5sum_hash};
|
||||
use crate::s3::utils::{check_bucket_name, insert, md5sum_hash};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketLifecycle {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) config: LifecycleConfig,
|
||||
config: LifecycleConfig,
|
||||
}
|
||||
|
||||
impl SetBucketLifecycle {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -73,37 +71,20 @@ impl S3Api for SetBucketLifecycle {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketLifecycle {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let mut headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert("lifecycle".into(), String::new());
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
|
||||
let bytes: Bytes = self.config.to_xml().into();
|
||||
headers.insert(String::from("Content-MD5"), md5sum_hash(&bytes));
|
||||
headers.add("Content-MD5", md5sum_hash(&bytes));
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,38 +14,36 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketNotificationResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{NotificationConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketNotification {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) config: NotificationConfig,
|
||||
config: NotificationConfig,
|
||||
}
|
||||
|
||||
impl SetBucketNotification {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketNotification {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketNotification {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("notification"), String::new());
|
||||
|
||||
let bytes: Bytes = self.config.to_xml().into();
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "notification"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,38 +14,36 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketPolicyResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketPolicy {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) config: String, //TODO consider PolicyConfig struct
|
||||
config: String, //TODO consider PolicyConfig struct
|
||||
}
|
||||
|
||||
impl SetBucketPolicy {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketPolicy {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketPolicy {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("policy"), String::new());
|
||||
|
||||
let bytes: Bytes = self.config.to_string().into();
|
||||
let bytes: Bytes = self.config.into();
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "policy"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,38 +14,36 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketReplicationResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{ReplicationConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketReplication {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) config: ReplicationConfig,
|
||||
config: ReplicationConfig,
|
||||
}
|
||||
|
||||
impl SetBucketReplication {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketReplication {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketReplication {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("replication"), String::new());
|
||||
|
||||
let bytes: Bytes = self.config.to_xml().into();
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "replication"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketTagsResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
use std::collections::HashMap;
|
||||
@ -26,29 +27,25 @@ use std::collections::HashMap;
|
||||
/// Argument builder for [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketTags {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) tags: HashMap<String, String>,
|
||||
tags: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl SetBucketTags {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -75,51 +72,35 @@ impl S3Api for SetBucketTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("tagging"), String::new());
|
||||
|
||||
let mut data = String::from("<Tagging>");
|
||||
if !self.tags.is_empty() {
|
||||
data.push_str("<TagSet>");
|
||||
for (key, value) in self.tags.iter() {
|
||||
data.push_str("<Tag>");
|
||||
data.push_str("<Key>");
|
||||
data.push_str(key);
|
||||
data.push_str("</Key>");
|
||||
data.push_str("<Value>");
|
||||
data.push_str(value);
|
||||
data.push_str("</Value>");
|
||||
data.push_str("</Tag>");
|
||||
let data: String = {
|
||||
let mut data = String::from("<Tagging>");
|
||||
if !self.tags.is_empty() {
|
||||
data.push_str("<TagSet>");
|
||||
for (key, value) in self.tags.iter() {
|
||||
data.push_str("<Tag>");
|
||||
data.push_str("<Key>");
|
||||
data.push_str(key);
|
||||
data.push_str("</Key>");
|
||||
data.push_str("<Value>");
|
||||
data.push_str(value);
|
||||
data.push_str("</Value>");
|
||||
data.push_str("</Tag>");
|
||||
}
|
||||
data.push_str("</TagSet>");
|
||||
}
|
||||
data.push_str("</TagSet>");
|
||||
}
|
||||
data.push_str("</Tagging>");
|
||||
|
||||
data.push_str("</Tagging>");
|
||||
data
|
||||
};
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(data)));
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "tagging"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetBucketVersioningResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
use std::fmt;
|
||||
@ -43,30 +44,26 @@ impl fmt::Display for VersioningStatus {
|
||||
/// Argument builder for [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetBucketVersioning {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) status: Option<VersioningStatus>,
|
||||
pub(crate) mfa_delete: Option<bool>,
|
||||
status: Option<VersioningStatus>,
|
||||
mfa_delete: Option<bool>,
|
||||
}
|
||||
|
||||
impl SetBucketVersioning {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -98,54 +95,38 @@ impl S3Api for SetBucketVersioning {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetBucketVersioning {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let data: String = {
|
||||
let mut data = "<VersioningConfiguration>".to_string();
|
||||
|
||||
query_params.insert("versioning".into(), String::new());
|
||||
|
||||
let mut data = "<VersioningConfiguration>".to_string();
|
||||
|
||||
if let Some(v) = self.mfa_delete {
|
||||
data.push_str("<MFADelete>");
|
||||
data.push_str(if v { "Enabled" } else { "Disabled" });
|
||||
data.push_str("</MFADelete>");
|
||||
}
|
||||
|
||||
match self.status {
|
||||
Some(VersioningStatus::Enabled) => data.push_str("<Status>Enabled</Status>"),
|
||||
Some(VersioningStatus::Suspended) => data.push_str("<Status>Suspended</Status>"),
|
||||
None => {
|
||||
return Err(Error::InvalidVersioningStatus(
|
||||
"Missing VersioningStatus".into(),
|
||||
));
|
||||
if let Some(v) = self.mfa_delete {
|
||||
data.push_str("<MFADelete>");
|
||||
data.push_str(if v { "Enabled" } else { "Disabled" });
|
||||
data.push_str("</MFADelete>");
|
||||
}
|
||||
|
||||
match self.status {
|
||||
Some(VersioningStatus::Enabled) => data.push_str("<Status>Enabled</Status>"),
|
||||
Some(VersioningStatus::Suspended) => data.push_str("<Status>Suspended</Status>"),
|
||||
None => {
|
||||
return Err(Error::InvalidVersioningStatus(
|
||||
"Missing VersioningStatus".into(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
data.push_str("</VersioningConfiguration>");
|
||||
data
|
||||
};
|
||||
|
||||
data.push_str("</VersioningConfiguration>");
|
||||
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(data)));
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "versioning"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::Multimap;
|
||||
use crate::s3::response::SetObjectLockConfigResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{ObjectLockConfig, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
@ -26,29 +27,25 @@ use http::Method;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetObjectLockConfig {
|
||||
pub(crate) client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub(crate) extra_headers: Option<Multimap>,
|
||||
pub(crate) extra_query_params: Option<Multimap>,
|
||||
pub(crate) region: Option<String>,
|
||||
pub(crate) bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub(crate) config: ObjectLockConfig,
|
||||
config: ObjectLockConfig,
|
||||
}
|
||||
|
||||
impl SetObjectLockConfig {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -75,36 +72,17 @@ impl S3Api for SetObjectLockConfig {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetObjectLockConfig {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
query_params.insert(String::from("object-lock"), String::new());
|
||||
|
||||
let bytes: Bytes = self.config.to_xml().into();
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,45 +14,44 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::SetObjectRetentionResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{RetentionMode, S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, UtcTime, check_bucket_name, md5sum_hash, to_iso8601utc};
|
||||
use crate::s3::utils::{
|
||||
UtcTime, check_bucket_name, check_object_name, insert, md5sum_hash, to_iso8601utc,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
/// Argument builder for [set_object_retention()](Client::set_object_retention) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetObjectRetention {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub version_id: Option<String>,
|
||||
pub bypass_governance_mode: bool,
|
||||
pub retention_mode: Option<RetentionMode>,
|
||||
pub retain_until_date: Option<UtcTime>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
bypass_governance_mode: bool,
|
||||
retention_mode: Option<RetentionMode>,
|
||||
retain_until_date: Option<UtcTime>,
|
||||
}
|
||||
|
||||
impl SetObjectRetention {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
bypass_governance_mode: false,
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
@ -68,11 +67,6 @@ impl SetObjectRetention {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -99,74 +93,49 @@ impl S3Api for SetObjectRetention {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetObjectRetention {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
//TODO move the following checks to a validate fn
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
if self.object.is_empty() {
|
||||
return Err(Error::InvalidObjectName(String::from(
|
||||
"object name cannot be empty",
|
||||
)));
|
||||
if self.retention_mode.is_some() ^ self.retain_until_date.is_some() {
|
||||
return Err(Error::InvalidRetentionConfig(String::from(
|
||||
"both mode and retain_until_date must be set or unset",
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if self.retention_mode.is_some() ^ self.retain_until_date.is_some() {
|
||||
return Err(Error::InvalidRetentionConfig(String::from(
|
||||
"both mode and retain_until_date must be set or unset",
|
||||
)));
|
||||
}
|
||||
|
||||
let mut headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let data: String = {
|
||||
let mut data: String = "<Retention>".into();
|
||||
if let Some(v) = &self.retention_mode {
|
||||
data.push_str("<Mode>");
|
||||
data.push_str(&v.to_string());
|
||||
data.push_str("</Mode>");
|
||||
}
|
||||
if let Some(v) = &self.retain_until_date {
|
||||
data.push_str("<RetainUntilDate>");
|
||||
data.push_str(&to_iso8601utc(*v));
|
||||
data.push_str("</RetainUntilDate>");
|
||||
}
|
||||
data.push_str("</Retention>");
|
||||
data
|
||||
};
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
if self.bypass_governance_mode {
|
||||
headers.insert(
|
||||
String::from("x-amz-bypass-governance-retention"),
|
||||
String::from("true"),
|
||||
);
|
||||
headers.add("x-amz-bypass-governance-retention", "true");
|
||||
}
|
||||
headers.add("Content-MD5", md5sum_hash(data.as_ref()));
|
||||
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "retention");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert(String::from("retention"), String::new());
|
||||
|
||||
let mut data: String = String::from("<Retention>");
|
||||
if let Some(v) = &self.retention_mode {
|
||||
data.push_str("<Mode>");
|
||||
data.push_str(&v.to_string());
|
||||
data.push_str("</Mode>");
|
||||
}
|
||||
if let Some(v) = &self.retain_until_date {
|
||||
data.push_str("<RetainUntilDate>");
|
||||
data.push_str(&to_iso8601utc(*v));
|
||||
data.push_str("</RetainUntilDate>");
|
||||
}
|
||||
data.push_str("</Retention>");
|
||||
|
||||
headers.insert(String::from("Content-MD5"), md5sum_hash(data.as_ref()));
|
||||
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(data)));
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.headers(headers)
|
||||
.object(Some(&self.object))
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.body(Some(SegmentedBytes::from(Bytes::from(data)))))
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::s3::Client;
|
||||
use crate::s3::builders::SegmentedBytes;
|
||||
use crate::s3::error::Error;
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::SetObjectTagsResponse;
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
||||
use crate::s3::utils::{Multimap, check_bucket_name};
|
||||
use crate::s3::utils::{check_bucket_name, check_object_name, insert};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
use std::collections::HashMap;
|
||||
@ -26,29 +27,27 @@ use std::collections::HashMap;
|
||||
/// Argument builder for [set_object_tags()](Client::set_object_tags) API
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SetObjectTags {
|
||||
pub client: Option<Client>,
|
||||
client: Client,
|
||||
|
||||
pub extra_headers: Option<Multimap>,
|
||||
pub extra_query_params: Option<Multimap>,
|
||||
pub region: Option<String>,
|
||||
pub bucket: String,
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
region: Option<String>,
|
||||
bucket: String,
|
||||
|
||||
pub object: String,
|
||||
pub version_id: Option<String>,
|
||||
pub tags: HashMap<String, String>,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
tags: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl SetObjectTags {
|
||||
pub fn new(bucket: &str) -> Self {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
bucket: bucket.to_owned(),
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn client(mut self, client: &Client) -> Self {
|
||||
self.client = Some(client.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
@ -65,11 +64,6 @@ impl SetObjectTags {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn object(mut self, object: String) -> Self {
|
||||
self.object = object;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
@ -86,63 +80,40 @@ impl S3Api for SetObjectTags {
|
||||
}
|
||||
|
||||
impl ToS3Request for SetObjectTags {
|
||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
|
||||
// TODO add to all other function (that use object) the following test
|
||||
// TODO should it be moved to the object setter function? or use validate as in put_object
|
||||
if self.object.is_empty() {
|
||||
return Err(Error::InvalidObjectName(String::from(
|
||||
"object name cannot be empty",
|
||||
)));
|
||||
}
|
||||
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
let headers = self
|
||||
.extra_headers
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut query_params = self
|
||||
.extra_query_params
|
||||
.as_ref()
|
||||
.filter(|v| !v.is_empty())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(v) = &self.version_id {
|
||||
query_params.insert(String::from("versionId"), v.to_string());
|
||||
}
|
||||
query_params.insert("tagging".into(), String::new());
|
||||
|
||||
let mut data = String::from("<Tagging>");
|
||||
if !self.tags.is_empty() {
|
||||
data.push_str("<TagSet>");
|
||||
for (key, value) in self.tags.iter() {
|
||||
data.push_str("<Tag>");
|
||||
data.push_str("<Key>");
|
||||
data.push_str(key);
|
||||
data.push_str("</Key>");
|
||||
data.push_str("<Value>");
|
||||
data.push_str(value);
|
||||
data.push_str("</Value>");
|
||||
data.push_str("</Tag>");
|
||||
let data: String = {
|
||||
let mut data = String::from("<Tagging>");
|
||||
if !self.tags.is_empty() {
|
||||
data.push_str("<TagSet>");
|
||||
for (key, value) in self.tags.iter() {
|
||||
data.push_str("<Tag>");
|
||||
data.push_str("<Key>");
|
||||
data.push_str(key);
|
||||
data.push_str("</Key>");
|
||||
data.push_str("<Value>");
|
||||
data.push_str(value);
|
||||
data.push_str("</Value>");
|
||||
data.push_str("</Tag>");
|
||||
}
|
||||
data.push_str("</TagSet>");
|
||||
}
|
||||
data.push_str("</TagSet>");
|
||||
}
|
||||
data.push_str("</Tagging>");
|
||||
|
||||
data.push_str("</Tagging>");
|
||||
data
|
||||
};
|
||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(data)));
|
||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
||||
|
||||
let req = S3Request::new(client, Method::PUT)
|
||||
.region(self.region.as_deref())
|
||||
.bucket(Some(&self.bucket))
|
||||
Ok(S3Request::new(self.client, Method::PUT)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.query_params(query_params)
|
||||
.object(Some(&self.object))
|
||||
.headers(headers)
|
||||
.body(body);
|
||||
|
||||
Ok(req)
|
||||
.object(Some(self.object))
|
||||
.headers(self.extra_headers.unwrap_or_default())
|
||||
.body(body))
|
||||
}
|
||||
}
|
||||
|
||||
162
src/s3/builders/stat_object.rs
Normal file
162
src/s3/builders/stat_object.rs
Normal file
@ -0,0 +1,162 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2023 MinIO, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use http::Method;
|
||||
|
||||
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||
use crate::s3::response::StatObjectResponse;
|
||||
use crate::s3::utils::check_object_name;
|
||||
use crate::s3::{
|
||||
client::Client,
|
||||
error::Error,
|
||||
sse::{Sse, SseCustomerKey},
|
||||
types::{S3Api, S3Request, ToS3Request},
|
||||
utils::{UtcTime, check_bucket_name, to_http_header_value},
|
||||
};
|
||||
|
||||
/// Argument builder for [list_objects()](Client::get_object) API.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct StatObject {
|
||||
client: Client,
|
||||
|
||||
extra_headers: Option<Multimap>,
|
||||
extra_query_params: Option<Multimap>,
|
||||
bucket: String,
|
||||
object: String,
|
||||
version_id: Option<String>,
|
||||
offset: Option<u64>,
|
||||
length: Option<u64>,
|
||||
region: Option<String>,
|
||||
ssec: Option<SseCustomerKey>,
|
||||
|
||||
// Conditionals
|
||||
match_etag: Option<String>,
|
||||
not_match_etag: Option<String>,
|
||||
modified_since: Option<UtcTime>,
|
||||
unmodified_since: Option<UtcTime>,
|
||||
}
|
||||
|
||||
impl StatObject {
|
||||
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||
Self {
|
||||
client,
|
||||
bucket,
|
||||
object,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||
self.extra_headers = extra_headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra_query_params(mut self, extra_query_params: Option<Multimap>) -> Self {
|
||||
self.extra_query_params = extra_query_params;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||
self.version_id = version_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset(mut self, offset: Option<u64>) -> Self {
|
||||
self.offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn length(mut self, length: Option<u64>) -> Self {
|
||||
self.length = length;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn region(mut self, region: Option<String>) -> Self {
|
||||
self.region = region;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ssec(mut self, ssec: Option<SseCustomerKey>) -> Self {
|
||||
self.ssec = ssec;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn match_etag(mut self, etag: Option<String>) -> Self {
|
||||
self.match_etag = etag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn not_match_etag(mut self, etag: Option<String>) -> Self {
|
||||
self.not_match_etag = etag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn modified_since(mut self, time: Option<UtcTime>) -> Self {
|
||||
self.modified_since = time;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unmodified_since(mut self, time: Option<UtcTime>) -> Self {
|
||||
self.unmodified_since = time;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Api for StatObject {
|
||||
type S3Response = StatObjectResponse;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ToS3Request for StatObject {
|
||||
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||
{
|
||||
check_bucket_name(&self.bucket, true)?;
|
||||
check_object_name(&self.object)?;
|
||||
if self.ssec.is_some() && !self.client.is_secure() {
|
||||
return Err(Error::SseTlsRequired(None));
|
||||
}
|
||||
}
|
||||
|
||||
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||
{
|
||||
if let Some(v) = self.match_etag {
|
||||
headers.add("if-match", v);
|
||||
}
|
||||
if let Some(v) = self.not_match_etag {
|
||||
headers.add("if-none-match", v);
|
||||
}
|
||||
if let Some(v) = self.modified_since {
|
||||
headers.add("if-modified-since", to_http_header_value(v));
|
||||
}
|
||||
if let Some(v) = self.unmodified_since {
|
||||
headers.add("if-unmodified-since", to_http_header_value(v));
|
||||
}
|
||||
if let Some(v) = self.ssec {
|
||||
headers.add_multimap(v.headers());
|
||||
}
|
||||
}
|
||||
|
||||
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||
query_params.add_version(self.version_id);
|
||||
|
||||
Ok(S3Request::new(self.client, Method::GET)
|
||||
.region(self.region)
|
||||
.bucket(Some(self.bucket))
|
||||
.object(Some(self.object))
|
||||
.query_params(query_params)
|
||||
.headers(headers))
|
||||
}
|
||||
}
|
||||
1410
src/s3/client.rs
1410
src/s3/client.rs
File diff suppressed because it is too large
Load Diff
55
src/s3/client/append_object.rs
Normal file
55
src/s3/client/append_object.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// ! S3 APIs for appending objects.
|
||||
|
||||
use super::Client;
|
||||
use crate::s3::builders::ObjectContent;
|
||||
use crate::s3::builders::{AppendObject, AppendObjectContent};
|
||||
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||
|
||||
impl Client {
|
||||
/// Creates an AppendObject request builder to append data to the end of an (existing) object.
|
||||
/// This is a lower-level API that performs a non-multipart object upload.
|
||||
///
|
||||
/// 🛈 This operation is not supported for regular non-express buckets.
|
||||
pub fn append_object<S: Into<String>>(
|
||||
&self,
|
||||
bucket: S,
|
||||
object: S,
|
||||
data: SegmentedBytes,
|
||||
offset_bytes: u64,
|
||||
) -> AppendObject {
|
||||
AppendObject::new(
|
||||
self.clone(),
|
||||
bucket.into(),
|
||||
object.into(),
|
||||
data,
|
||||
offset_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates an AppendObjectContent request builder to append data to the end of an existing
|
||||
/// object. The content is streamed and appended to MinIO/S3. This is a higher-level API that
|
||||
/// handles multipart appends transparently.
|
||||
pub fn append_object_content<S: Into<String>, C: Into<ObjectContent>>(
|
||||
&self,
|
||||
bucket: S,
|
||||
object: S,
|
||||
content: C,
|
||||
) -> AppendObjectContent {
|
||||
AppendObjectContent::new(self.clone(), bucket.into(), object.into(), content)
|
||||
}
|
||||
}
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::BucketExists;
|
||||
|
||||
impl Client {
|
||||
/// Create a BucketExists request builder.
|
||||
pub fn bucket_exists(&self, bucket: &str) -> BucketExists {
|
||||
BucketExists::new(bucket).client(self)
|
||||
/// Creates a [`BucketExists`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`BucketExists::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`BucketExistsResponse`](crate::s3::response::BucketExistsResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::BucketExistsResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: BucketExistsResponse =
|
||||
/// client.bucket_exists("bucket-name").send().await.unwrap();
|
||||
/// println!("bucket '{}' exists: {}", resp.bucket, resp.exists);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn bucket_exists<S: Into<String>>(&self, bucket: S) -> BucketExists {
|
||||
BucketExists::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
69
src/s3/client/copy_object.rs
Normal file
69
src/s3/client/copy_object.rs
Normal file
@ -0,0 +1,69 @@
|
||||
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
||||
// 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! S3 APIs for bucket objects.
|
||||
|
||||
use super::Client;
|
||||
use crate::s3::builders::{
|
||||
ComposeObject, ComposeObjectInternal, ComposeSource, CopyObject, CopyObjectInternal,
|
||||
UploadPartCopy,
|
||||
};
|
||||
|
||||
impl Client {
|
||||
/// Executes [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) S3 API
|
||||
pub fn upload_part_copy<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
upload_id: S3,
|
||||
) -> UploadPartCopy {
|
||||
UploadPartCopy::new(self.clone(), bucket.into(), object.into(), upload_id.into())
|
||||
}
|
||||
|
||||
/// Create a CopyObject request builder. This is a lower-level API that
|
||||
/// performs a non-multipart object copy.
|
||||
pub fn copy_object_internal<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> CopyObjectInternal {
|
||||
CopyObjectInternal::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
|
||||
/// copy object is a high-order API that calls [`stat_object`] and based on the results calls
|
||||
/// either [`compose_object`] or [`copy_object_internal`] to copy the object.
|
||||
pub fn copy_object<S: Into<String>>(&self, bucket: S, object: S) -> CopyObject {
|
||||
CopyObject::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
|
||||
pub(crate) fn compose_object_internal<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> ComposeObjectInternal {
|
||||
ComposeObjectInternal::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
|
||||
/// compose object is high-order API that calls [`compose_object_internal`] and if that call fails,
|
||||
/// it calls ['abort_multipart_upload`].
|
||||
pub fn compose_object<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
sources: Vec<ComposeSource>,
|
||||
) -> ComposeObject {
|
||||
ComposeObject::new(self.clone(), bucket.into(), object.into()).sources(sources)
|
||||
}
|
||||
}
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteBucketEncryption;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketEncryption request builder.
|
||||
pub fn delete_bucket_encryption(&self, bucket: &str) -> DeleteBucketEncryption {
|
||||
DeleteBucketEncryption::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketEncryption`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketEncryption::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`BucketExistsResponse`](crate::s3::response::BucketExistsResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketEncryptionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketEncryptionResponse =
|
||||
/// client.delete_bucket_encryption("bucket-name").send().await.unwrap();
|
||||
/// println!("bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_encryption<S: Into<String>>(&self, bucket: S) -> DeleteBucketEncryption {
|
||||
DeleteBucketEncryption::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteBucketLifecycle;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketLifecycle request builder.
|
||||
pub fn delete_bucket_lifecycle(&self, bucket: &str) -> DeleteBucketLifecycle {
|
||||
DeleteBucketLifecycle::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketLifecycle`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketLifecycle::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteBucketLifecycleResponse`](crate::s3::response::DeleteBucketLifecycleResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketLifecycleResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketLifecycleResponse =
|
||||
/// client.delete_bucket_lifecycle("bucket-name").send().await.unwrap();
|
||||
/// println!("lifecycle of bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_lifecycle<S: Into<String>>(&self, bucket: S) -> DeleteBucketLifecycle {
|
||||
DeleteBucketLifecycle::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,31 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteBucketNotification;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketNotification request builder.
|
||||
pub fn delete_bucket_notification(&self, bucket: &str) -> DeleteBucketNotification {
|
||||
DeleteBucketNotification::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketNotification`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketNotification::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteBucketNotificationResponse`](crate::s3::response::DeleteBucketNotificationResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketNotificationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketNotificationResponse =
|
||||
/// client.delete_bucket_notification("bucket-name").send().await.unwrap();
|
||||
/// println!("notification of bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_notification<S: Into<String>>(
|
||||
&self,
|
||||
bucket: S,
|
||||
) -> DeleteBucketNotification {
|
||||
DeleteBucketNotification::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,28 @@ use crate::s3::builders::DeleteBucketPolicy;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketPolicy request builder.
|
||||
pub fn delete_bucket_policy(&self, bucket: &str) -> DeleteBucketPolicy {
|
||||
DeleteBucketPolicy::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketPolicy`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketPolicy::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteBucketPolicyResponse`](crate::s3::response::DeleteBucketPolicyResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketPolicyResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketPolicyResponse =
|
||||
/// client.delete_bucket_policy("bucket-name").send().await.unwrap();
|
||||
/// println!("policy of bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_policy<S: Into<String>>(&self, bucket: S) -> DeleteBucketPolicy {
|
||||
DeleteBucketPolicy::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,30 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteBucketReplication;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketReplication request builder.
|
||||
pub fn delete_bucket_replication(&self, bucket: &str) -> DeleteBucketReplication {
|
||||
DeleteBucketReplication::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketReplication`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketReplication::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteBucketReplicationResponse`](crate::s3::response::DeleteBucketReplicationResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketReplicationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketReplicationResponse =
|
||||
/// client.delete_bucket_replication("bucket-name").send().await.unwrap();
|
||||
/// println!("replication of bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_replication<S: Into<String>>(&self, bucket: S) -> DeleteBucketReplication {
|
||||
DeleteBucketReplication::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,30 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteBucketTags;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteBucketTags request builder.
|
||||
pub fn delete_bucket_tags(&self, bucket: &str) -> DeleteBucketTags {
|
||||
DeleteBucketTags::new(bucket).client(self)
|
||||
/// Creates a [`DeleteBucketTags`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteBucketTagsResponse::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteBucketTagsResponse`](crate::s3::response::DeleteBucketTagsResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteBucketTagsResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteBucketTagsResponse =
|
||||
/// client.delete_bucket_tags("bucket-name").send().await.unwrap();
|
||||
/// println!("tags of bucket '{}' are deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_bucket_tags<S: Into<String>>(&self, bucket: S) -> DeleteBucketTags {
|
||||
DeleteBucketTags::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,43 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteObjectLockConfig;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteObjectLockConfig request builder.
|
||||
pub fn delete_object_lock_config(&self, bucket: &str) -> DeleteObjectLockConfig {
|
||||
DeleteObjectLockConfig::new(bucket).client(self)
|
||||
/// Creates a [`DeleteObjectLockConfig`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteObjectLockConfig::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteObjectLockConfigResponse`](crate::s3::response::DeleteObjectLockConfigResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::{DeleteObjectLockConfigResponse, MakeBucketResponse, SetObjectLockConfigResponse};
|
||||
/// use minio::s3::types::{S3Api, ObjectLockConfig, RetentionMode};
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let bucket_name = "bucket-name";
|
||||
///
|
||||
/// let resp: MakeBucketResponse =
|
||||
/// client.make_bucket(bucket_name).object_lock(true).send().await.unwrap();
|
||||
/// 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: SetObjectLockConfigResponse =
|
||||
/// client.set_object_lock_config(bucket_name).config(config).send().await.unwrap();
|
||||
/// 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);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_object_lock_config<S: Into<String>>(&self, bucket: S) -> DeleteObjectLockConfig {
|
||||
DeleteObjectLockConfig::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,34 @@ use super::Client;
|
||||
use crate::s3::builders::DeleteObjectTags;
|
||||
|
||||
impl Client {
|
||||
/// Create a DeleteObjectTags request builder.
|
||||
pub fn delete_object_tags(&self, bucket: &str) -> DeleteObjectTags {
|
||||
DeleteObjectTags::new(bucket).client(self)
|
||||
/// Creates a [`DeleteObjectTags`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DeleteObjectTags::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DeleteObjectTagsResponse`](crate::s3::response::DeleteObjectTagsResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DeleteObjectTagsResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DeleteObjectTagsResponse =
|
||||
/// client.delete_object_tags("bucket-name", "object_name").send().await.unwrap();
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is deleted", resp.object, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn delete_object_tags<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> DeleteObjectTags {
|
||||
DeleteObjectTags::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,34 @@ use super::Client;
|
||||
use crate::s3::builders::DisableObjectLegalHold;
|
||||
|
||||
impl Client {
|
||||
/// Create a DisableObjectLegalHold request builder.
|
||||
pub fn disable_object_legal_hold(&self, bucket: &str) -> DisableObjectLegalHold {
|
||||
DisableObjectLegalHold::new(bucket).client(self)
|
||||
/// Creates a [`DisableObjectLegalHold`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`DisableObjectLegalHold::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`DisableObjectLegalHoldResponse`](crate::s3::response::DisableObjectLegalHoldResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::DisableObjectLegalHoldResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: DisableObjectLegalHoldResponse =
|
||||
/// client.disable_object_legal_hold("bucket-name", "object-name").send().await.unwrap();
|
||||
/// println!("legal hold of bucket '{}' is deleted", resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn disable_object_legal_hold<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> DisableObjectLegalHold {
|
||||
DisableObjectLegalHold::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,34 @@ use super::Client;
|
||||
use crate::s3::builders::EnableObjectLegalHold;
|
||||
|
||||
impl Client {
|
||||
/// Create a EnableObjectLegalHold request builder.
|
||||
pub fn enable_object_legal_hold(&self, bucket: &str) -> EnableObjectLegalHold {
|
||||
EnableObjectLegalHold::new(bucket).client(self)
|
||||
/// Creates a [`EnableObjectLegalHold`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`EnableObjectLegalHold::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`EnableObjectLegalHoldResponse`](crate::s3::response::EnableObjectLegalHoldResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::EnableObjectLegalHoldResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: EnableObjectLegalHoldResponse =
|
||||
/// client.enable_object_legal_hold("bucket-name", "object-name").send().await.unwrap();
|
||||
/// println!("legal hold of object '{}' in bucket '{}' is enabled", resp.object, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn enable_object_legal_hold<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> EnableObjectLegalHold {
|
||||
EnableObjectLegalHold::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketEncryption;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketEncryption request builder.
|
||||
pub fn get_bucket_encryption(&self, bucket: &str) -> GetBucketEncryption {
|
||||
GetBucketEncryption::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketEncryption`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketEncryption::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketEncryptionResponse`](crate::s3::response::GetBucketEncryptionResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketEncryptionResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketEncryptionResponse =
|
||||
/// client.get_bucket_encryption("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved SseConfig '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_encryption<S: Into<String>>(&self, bucket: S) -> GetBucketEncryption {
|
||||
GetBucketEncryption::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,29 @@ use crate::s3::builders::GetBucketLifecycle;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketLifecycle request builder.
|
||||
pub fn get_bucket_lifecycle(&self, bucket: &str) -> GetBucketLifecycle {
|
||||
GetBucketLifecycle::new(bucket).client(self)
|
||||
///
|
||||
/// Creates a [`GetBucketLifecycle`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketLifecycle::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketLifecycleResponse`](crate::s3::response::GetBucketLifecycleResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketLifecycleResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketLifecycleResponse =
|
||||
/// client.get_bucket_lifecycle("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved bucket lifecycle config '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_lifecycle<S: Into<String>>(&self, bucket: S) -> GetBucketLifecycle {
|
||||
GetBucketLifecycle::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketNotification;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketNotification request builder.
|
||||
pub fn get_bucket_notification(&self, bucket: &str) -> GetBucketNotification {
|
||||
GetBucketNotification::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketNotification`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketNotification::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketNotificationResponse`](crate::s3::response::GetBucketNotificationResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketNotificationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketNotificationResponse =
|
||||
/// client.get_bucket_notification("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved bucket notification config '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_notification<S: Into<String>>(&self, bucket: S) -> GetBucketNotification {
|
||||
GetBucketNotification::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,28 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketPolicy;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketPolicy request builder.
|
||||
pub fn get_bucket_policy(&self, bucket: &str) -> GetBucketPolicy {
|
||||
GetBucketPolicy::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketPolicy`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketPolicy::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketPolicyResponse`](crate::s3::response::GetBucketPolicyResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketPolicyResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketPolicyResponse =
|
||||
/// client.get_bucket_policy("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved bucket policy config '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_policy<S: Into<String>>(&self, bucket: S) -> GetBucketPolicy {
|
||||
GetBucketPolicy::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,30 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketReplication;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketReplication request builder.
|
||||
pub fn get_bucket_replication(&self, bucket: &str) -> GetBucketReplication {
|
||||
GetBucketReplication::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketReplication`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketReplication::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketReplicationResponse`](crate::s3::response::GetBucketReplicationResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketReplicationResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketReplicationResponse =
|
||||
/// client.get_bucket_replication("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved bucket replication config '{:?}' from bucket '{}' is enabled", resp.config, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_replication<S: Into<String>>(&self, bucket: S) -> GetBucketReplication {
|
||||
GetBucketReplication::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,30 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketTags;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketTags request builder.
|
||||
pub fn get_bucket_tags(&self, bucket: &str) -> GetBucketTags {
|
||||
GetBucketTags::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketTags`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketTags::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketTagsResponse`](crate::s3::response::GetBucketTagsResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketTagsResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketTagsResponse =
|
||||
/// client.get_bucket_tags("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved bucket tags '{:?}' from bucket '{}' is enabled", resp.tags, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_tags<S: Into<String>>(&self, bucket: S) -> GetBucketTags {
|
||||
GetBucketTags::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,30 @@ use super::Client;
|
||||
use crate::s3::builders::GetBucketVersioning;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetBucketVersioning request builder.
|
||||
pub fn get_bucket_versioning(&self, bucket: &str) -> GetBucketVersioning {
|
||||
GetBucketVersioning::new(bucket).client(self)
|
||||
/// Creates a [`GetBucketVersioning`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetBucketVersioning::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetBucketVersioningResponse`](crate::s3::response::GetBucketVersioningResponse).
|
||||
///
|
||||
/// 🛈 This operation is not supported for express buckets.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetBucketVersioningResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// let resp: GetBucketVersioningResponse =
|
||||
/// client.get_bucket_versioning("bucket-name").send().await.unwrap();
|
||||
/// println!("retrieved versioning status '{:?}' from bucket '{}' is enabled", resp.status, resp.bucket);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_bucket_versioning<S: Into<String>>(&self, bucket: S) -> GetBucketVersioning {
|
||||
GetBucketVersioning::new(self.clone(), bucket.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,13 +15,38 @@
|
||||
|
||||
//! S3 APIs for downloading objects.
|
||||
|
||||
use super::Client;
|
||||
use crate::s3::builders::GetObject;
|
||||
|
||||
use super::Client;
|
||||
|
||||
impl Client {
|
||||
/// Create a GetObject request builder.
|
||||
pub fn get_object(&self, bucket: &str, object: &str) -> GetObject {
|
||||
GetObject::new(bucket, object).client(self)
|
||||
/// Creates a [`GetObject`] request builder.
|
||||
///
|
||||
/// To execute the request, call [`GetObject::send()`](crate::s3::types::S3Api::send),
|
||||
/// which returns a [`Result`] containing a [`GetObjectResponse`](crate::s3::response::GetObjectResponse).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use minio::s3::Client;
|
||||
/// use minio::s3::response::GetObjectResponse;
|
||||
/// use minio::s3::types::S3Api;
|
||||
///
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client: Client = Default::default(); // configure your client here
|
||||
/// 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_str = String::from_utf8(content_bytes.to_vec()).unwrap();
|
||||
/// println!("retrieved content '{}'", content_str);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_object<S1: Into<String>, S2: Into<String>>(
|
||||
&self,
|
||||
bucket: S1,
|
||||
object: S2,
|
||||
) -> GetObject {
|
||||
GetObject::new(self.clone(), bucket.into(), object.into())
|
||||
}
|
||||
}
|
||||
|
||||
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