mirror of
https://github.com/minio/minio-rs.git
synced 2025-12-06 23:36:52 +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 clippy --all-targets --all-features
|
||||||
cargo build --bins --examples --tests --benches --verbose
|
cargo build --bins --examples --tests --benches --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests S3
|
||||||
run: |
|
run: |
|
||||||
./tests/start-server.sh
|
./tests/start-server.sh
|
||||||
export SERVER_ENDPOINT=localhost:9000
|
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 = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
sha2 = { version = "0.10.8", optional = true }
|
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-stream = "0.1.17"
|
||||||
tokio-util = { version = "0.7.14", features = ["io"] }
|
tokio-util = { version = "0.7.14", features = ["io"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
@ -59,7 +59,7 @@ http = "1.3.1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
minio_common = { path = "./common" }
|
minio_common = { path = "./common" }
|
||||||
async-std = { version = "1.13.1", features = ["attributes", "tokio1"] }
|
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"
|
quickcheck = "1.0.3"
|
||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
|
|
||||||
@ -76,6 +76,9 @@ name = "file_downloader"
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "object_prompt"
|
name = "object_prompt"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "append_object"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "s3-api"
|
name = "s3-api"
|
||||||
path = "benches/s3/api_benchmarks.rs"
|
path = "benches/s3/api_benchmarks.rs"
|
||||||
|
|||||||
@ -21,14 +21,15 @@ mod bench_bucket_replication;
|
|||||||
mod bench_bucket_tags;
|
mod bench_bucket_tags;
|
||||||
mod bench_bucket_versioning;
|
mod bench_bucket_versioning;
|
||||||
mod bench_list_bucket;
|
mod bench_list_bucket;
|
||||||
|
mod bench_object_append;
|
||||||
|
mod bench_object_copy;
|
||||||
mod bench_object_legal_hold;
|
mod bench_object_legal_hold;
|
||||||
mod bench_object_lock_config;
|
mod bench_object_lock_config;
|
||||||
|
mod bench_object_put;
|
||||||
mod bench_object_retention;
|
mod bench_object_retention;
|
||||||
mod bench_object_tags;
|
mod bench_object_tags;
|
||||||
mod common_benches;
|
mod common_benches;
|
||||||
|
|
||||||
mod bench_object_copy;
|
|
||||||
|
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{Criterion, criterion_group, criterion_main};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -43,9 +44,12 @@ use crate::bench_bucket_tags::*;
|
|||||||
use crate::bench_bucket_versioning::*;
|
use crate::bench_bucket_versioning::*;
|
||||||
use crate::bench_list_bucket::*;
|
use crate::bench_list_bucket::*;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
use crate::bench_object_append::bench_object_append;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::bench_object_copy::*;
|
use crate::bench_object_copy::*;
|
||||||
use crate::bench_object_legal_hold::*;
|
use crate::bench_object_legal_hold::*;
|
||||||
use crate::bench_object_lock_config::*;
|
use crate::bench_object_lock_config::*;
|
||||||
|
use crate::bench_object_put::bench_object_put;
|
||||||
use crate::bench_object_retention::*;
|
use crate::bench_object_retention::*;
|
||||||
use crate::bench_object_tags::*;
|
use crate::bench_object_tags::*;
|
||||||
|
|
||||||
@ -54,9 +58,9 @@ criterion_group!(
|
|||||||
config = Criterion::default()
|
config = Criterion::default()
|
||||||
.configure_from_args()
|
.configure_from_args()
|
||||||
.warm_up_time(Duration::from_secs_f32(0.01))
|
.warm_up_time(Duration::from_secs_f32(0.01))
|
||||||
.sample_size(100)
|
.sample_size(1000)
|
||||||
.nresamples(1001)
|
.nresamples(1001)
|
||||||
.measurement_time(Duration::from_secs_f32(0.5));
|
.measurement_time(Duration::from_secs_f32(10.0));
|
||||||
targets =
|
targets =
|
||||||
bench_bucket_exists,
|
bench_bucket_exists,
|
||||||
bench_set_bucket_lifecycle,
|
bench_set_bucket_lifecycle,
|
||||||
@ -83,7 +87,9 @@ criterion_group!(
|
|||||||
bench_get_bucket_versioning,
|
bench_get_bucket_versioning,
|
||||||
//
|
//
|
||||||
bench_list_buckets,
|
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_enable_object_legal_hold,
|
||||||
bench_disable_object_legal_hold,
|
bench_disable_object_legal_hold,
|
||||||
|
|||||||
@ -23,6 +23,6 @@ pub(crate) fn bench_bucket_exists(criterion: &mut Criterion) {
|
|||||||
"bucket_exists",
|
"bucket_exists",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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 },
|
|| async { Ctx2::new().await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let config = create_bucket_lifecycle_config_examples();
|
let config = create_bucket_lifecycle_config_examples();
|
||||||
SetBucketLifecycle::new(&ctx.bucket)
|
SetBucketLifecycle::new(ctx.client.clone(), ctx.bucket.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.life_cycle_config(config)
|
.life_cycle_config(config)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -40,15 +39,15 @@ pub(crate) fn bench_get_bucket_lifecycle(criterion: &mut Criterion) {
|
|||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new().await;
|
let ctx = Ctx2::new().await;
|
||||||
let config = create_bucket_lifecycle_config_examples();
|
let config = create_bucket_lifecycle_config_examples();
|
||||||
SetBucketLifecycle::new(&ctx.bucket)
|
ctx.client
|
||||||
.client(&ctx.client)
|
.set_bucket_lifecycle(&ctx.bucket)
|
||||||
.life_cycle_config(config)
|
.life_cycle_config(config)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
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) {
|
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",
|
"delete_bucket_lifecycle",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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 },
|
|| async { Ctx2::new().await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let config = create_bucket_notification_config_example();
|
let config = create_bucket_notification_config_example();
|
||||||
SetBucketNotification::new(&ctx.bucket)
|
SetBucketNotification::new(ctx.client.clone(), ctx.bucket.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.notification_config(config)
|
.notification_config(config)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -42,15 +41,15 @@ pub(crate) fn bench_get_bucket_notification(criterion: &mut Criterion) {
|
|||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new().await;
|
let ctx = Ctx2::new().await;
|
||||||
let config = create_bucket_notification_config_example();
|
let config = create_bucket_notification_config_example();
|
||||||
SetBucketNotification::new(&ctx.bucket)
|
ctx.client
|
||||||
.client(&ctx.client)
|
.set_bucket_notification(&ctx.bucket)
|
||||||
.notification_config(config)
|
.notification_config(config)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
ctx
|
||||||
},
|
},
|
||||||
|ctx| GetBucketNotification::new(&ctx.bucket).client(&ctx.client),
|
|ctx| GetBucketNotification::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -59,6 +58,6 @@ pub(crate) fn bench_delete_bucket_notification(criterion: &mut Criterion) {
|
|||||||
"delete_bucket_notification",
|
"delete_bucket_notification",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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 },
|
|| async { Ctx2::new().await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let config = create_bucket_policy_config_example(&ctx.bucket);
|
let config = create_bucket_policy_config_example(&ctx.bucket);
|
||||||
SetBucketPolicy::new(&ctx.bucket)
|
SetBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone()).config(config)
|
||||||
.client(&ctx.client)
|
|
||||||
.config(config)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -40,15 +38,14 @@ pub(crate) fn bench_get_bucket_policy(criterion: &mut Criterion) {
|
|||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new().await;
|
let ctx = Ctx2::new().await;
|
||||||
let config = create_bucket_policy_config_example(&ctx.bucket);
|
let config = create_bucket_policy_config_example(&ctx.bucket);
|
||||||
SetBucketPolicy::new(&ctx.bucket)
|
SetBucketPolicy::new(ctx.client.clone(), ctx.bucket.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.config(config)
|
.config(config)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
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) {
|
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",
|
"delete_bucket_policy",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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| {
|
|ctx| {
|
||||||
let config =
|
let config =
|
||||||
create_bucket_replication_config_example(ctx.aux_bucket.clone().unwrap().as_str());
|
create_bucket_replication_config_example(ctx.aux_bucket.clone().unwrap().as_str());
|
||||||
SetBucketReplication::new(&ctx.bucket)
|
SetBucketReplication::new(ctx.client.clone(), ctx.bucket.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.replication_config(config)
|
.replication_config(config)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -86,7 +85,7 @@ pub(crate) fn bench_get_bucket_replication(criterion: &mut Criterion) {
|
|||||||
|
|
||||||
ctx
|
ctx
|
||||||
},
|
},
|
||||||
|ctx| GetBucketReplication::new(&ctx.bucket).client(&ctx.client),
|
|ctx| GetBucketReplication::new(ctx.client.clone(), ctx.bucket.clone()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -116,6 +115,6 @@ pub(crate) fn bench_delete_bucket_replication(criterion: &mut Criterion) {
|
|||||||
|
|
||||||
ctx
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{DeleteBucketTags, GetBucketTags, SetBucketTags};
|
use minio::s3::builders::{DeleteBucketTags, GetBucketTags, SetBucketTags};
|
||||||
use minio::s3::response::SetBucketTagsResponse;
|
|
||||||
use minio::s3::types::S3Api;
|
use minio::s3::types::S3Api;
|
||||||
use minio_common::example::create_tags_example;
|
use minio_common::example::create_tags_example;
|
||||||
|
|
||||||
pub(crate) fn bench_set_bucket_tags(criterion: &mut Criterion) {
|
pub(crate) fn bench_set_bucket_tags(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_set_bucket_tags") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"set_bucket_tags",
|
"set_bucket_tags",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| async { Ctx2::new().await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
SetBucketTags::new(&ctx.bucket)
|
SetBucketTags::new(ctx.client.clone(), ctx.bucket.clone()).tags(create_tags_example())
|
||||||
.client(&ctx.client)
|
|
||||||
.tags(create_tags_example())
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_get_bucket_tags(criterion: &mut Criterion) {
|
pub(crate) fn bench_get_bucket_tags(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_get_bucket_tags") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"get_bucket_tags",
|
"get_bucket_tags",
|
||||||
criterion,
|
criterion,
|
||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new().await;
|
let ctx = Ctx2::new().await;
|
||||||
let _resp: SetBucketTagsResponse = ctx
|
ctx.client
|
||||||
.client
|
|
||||||
.set_bucket_tags(&ctx.bucket)
|
.set_bucket_tags(&ctx.bucket)
|
||||||
.tags(create_tags_example())
|
.tags(create_tags_example())
|
||||||
.send()
|
.send()
|
||||||
@ -48,14 +50,17 @@ pub(crate) fn bench_get_bucket_tags(criterion: &mut Criterion) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
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) {
|
pub(crate) fn bench_delete_bucket_tags(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_delete_bucket_tags") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"delete_bucket_tags",
|
"delete_bucket_tags",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{GetBucketVersioning, SetBucketVersioning, VersioningStatus};
|
use minio::s3::builders::{GetBucketVersioning, SetBucketVersioning, VersioningStatus};
|
||||||
|
|
||||||
pub(crate) fn bench_get_bucket_versioning(criterion: &mut Criterion) {
|
pub(crate) fn bench_get_bucket_versioning(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_get_bucket_versioning") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"get_bucket_versioning",
|
"get_bucket_versioning",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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) {
|
pub(crate) fn bench_set_bucket_versioning(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_set_bucket_versioning") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"set_bucket_versioning",
|
"set_bucket_versioning",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| async { Ctx2::new().await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let status = VersioningStatus::Enabled;
|
SetBucketVersioning::new(ctx.client.clone(), ctx.bucket.clone())
|
||||||
SetBucketVersioning::new(&ctx.bucket)
|
.versioning_status(VersioningStatus::Enabled)
|
||||||
.client(&ctx.client)
|
|
||||||
.versioning_status(status)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,6 @@ pub(crate) fn bench_list_buckets(criterion: &mut Criterion) {
|
|||||||
"list_buckets",
|
"list_buckets",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new().await },
|
|| 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
use crate::common_benches::{Ctx2, benchmark_s3_api};
|
||||||
|
|
||||||
use criterion::Criterion;
|
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_internal(criterion: &mut Criterion) {
|
||||||
pub(crate) fn bench_object_copy(_criterion: &mut Criterion) {
|
|
||||||
/*
|
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"object_copy",
|
"object_copy_internal",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(false).await },
|
|| async { Ctx2::new_with_object(false).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let _object_name_dst = rand_object_name();
|
let object_name_src = &ctx.object;
|
||||||
//TODO refactor copy object for this to be possible
|
let object_name_dst = rand_object_name();
|
||||||
todo!()
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{
|
use minio::s3::builders::{
|
||||||
DisableObjectLegalHold, EnableObjectLegalHold, IsObjectLegalHoldEnabled,
|
DisableObjectLegalHold, EnableObjectLegalHold, IsObjectLegalHoldEnabled,
|
||||||
};
|
};
|
||||||
|
use minio::s3::types::S3Api;
|
||||||
|
|
||||||
pub(crate) fn bench_enable_object_legal_hold(criterion: &mut Criterion) {
|
pub(crate) fn bench_enable_object_legal_hold(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_enable_object_legal_hold") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"enable_object_legal_hold",
|
"enable_object_legal_hold",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| async { Ctx2::new_with_object(true).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
EnableObjectLegalHold::new(&ctx.bucket)
|
EnableObjectLegalHold::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_disable_object_legal_hold(criterion: &mut Criterion) {
|
pub(crate) fn bench_disable_object_legal_hold(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_disable_object_legal_hold") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"disable_object_legal_hold",
|
"disable_object_legal_hold",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| async { Ctx2::new_with_object(true).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
DisableObjectLegalHold::new(&ctx.bucket)
|
DisableObjectLegalHold::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_is_object_legal_hold(criterion: &mut Criterion) {
|
pub(crate) fn bench_is_object_legal_hold(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_is_object_legal_hold") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"is_object_legal_hold",
|
"is_object_legal_hold",
|
||||||
criterion,
|
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| {
|
|ctx| {
|
||||||
IsObjectLegalHoldEnabled::new(&ctx.bucket)
|
IsObjectLegalHoldEnabled::new(
|
||||||
.client(&ctx.client)
|
ctx.client.clone(),
|
||||||
.object(ctx.object.clone())
|
ctx.bucket.clone(),
|
||||||
|
ctx.object.clone(),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,37 +13,44 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{DeleteObjectLockConfig, GetObjectLockConfig, SetObjectLockConfig};
|
use minio::s3::builders::{DeleteObjectLockConfig, GetObjectLockConfig, SetObjectLockConfig};
|
||||||
use minio_common::example::create_object_lock_config_example;
|
use minio_common::example::create_object_lock_config_example;
|
||||||
|
|
||||||
pub(crate) fn bench_set_object_lock_config(criterion: &mut Criterion) {
|
pub(crate) fn bench_set_object_lock_config(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_set_object_lock_config") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"set_object_lock_config",
|
"set_object_lock_config",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| async { Ctx2::new_with_object(true).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
let config = create_object_lock_config_example();
|
let config = create_object_lock_config_example();
|
||||||
SetObjectLockConfig::new(&ctx.bucket)
|
SetObjectLockConfig::new(ctx.client.clone(), ctx.bucket.clone()).config(config)
|
||||||
.client(&ctx.client)
|
|
||||||
.config(config)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_get_object_lock_config(criterion: &mut Criterion) {
|
pub(crate) fn bench_get_object_lock_config(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_get_object_lock_config") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"get_object_lock_config",
|
"get_object_lock_config",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| 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) {
|
pub(crate) fn bench_delete_object_lock_config(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_delete_object_lock_config") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"delete_object_lock_config",
|
"delete_object_lock_config",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{GetObjectRetention, SetObjectRetention};
|
use minio::s3::builders::{GetObjectRetention, SetObjectRetention};
|
||||||
@ -22,28 +22,31 @@ use minio::s3::types::{RetentionMode, S3Api};
|
|||||||
use minio::s3::utils::utc_now;
|
use minio::s3::utils::utc_now;
|
||||||
|
|
||||||
pub(crate) fn bench_set_object_retention(criterion: &mut Criterion) {
|
pub(crate) fn bench_set_object_retention(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_set_object_retention") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"set_object_retention",
|
"set_object_retention",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(true).await },
|
|| async { Ctx2::new_with_object(true).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
SetObjectRetention::new(&ctx.bucket)
|
SetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||||
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_get_object_retention(criterion: &mut Criterion) {
|
pub(crate) fn bench_get_object_retention(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_get_object_retention") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"get_object_retention",
|
"get_object_retention",
|
||||||
criterion,
|
criterion,
|
||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new_with_object(true).await;
|
let ctx = Ctx2::new_with_object(true).await;
|
||||||
let _resp: SetObjectRetentionResponse = SetObjectRetention::new(&ctx.bucket)
|
let _resp: SetObjectRetentionResponse =
|
||||||
.client(&ctx.client)
|
SetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||||
.object(ctx.object.clone())
|
|
||||||
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
.retention_mode(Some(RetentionMode::GOVERNANCE))
|
||||||
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
.retain_until_date(Some(utc_now() + chrono::Duration::days(1)))
|
||||||
.send()
|
.send()
|
||||||
@ -51,10 +54,6 @@ pub(crate) fn bench_get_object_retention(criterion: &mut Criterion) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
ctx
|
||||||
},
|
},
|
||||||
|ctx| {
|
|ctx| GetObjectRetention::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone()),
|
||||||
GetObjectRetention::new(&ctx.bucket)
|
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 criterion::Criterion;
|
||||||
use minio::s3::builders::{GetObjectTags, SetObjectTags};
|
use minio::s3::builders::{GetObjectTags, SetObjectTags};
|
||||||
@ -22,37 +22,38 @@ use minio::s3::types::S3Api;
|
|||||||
use minio_common::example::create_tags_example;
|
use minio_common::example::create_tags_example;
|
||||||
|
|
||||||
pub(crate) fn bench_set_object_tags(criterion: &mut Criterion) {
|
pub(crate) fn bench_set_object_tags(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_set_object_tags") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"set_object_tags",
|
"set_object_tags",
|
||||||
criterion,
|
criterion,
|
||||||
|| async { Ctx2::new_with_object(false).await },
|
|| async { Ctx2::new_with_object(false).await },
|
||||||
|ctx| {
|
|ctx| {
|
||||||
SetObjectTags::new(&ctx.bucket)
|
SetObjectTags::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone())
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
.tags(create_tags_example())
|
.tags(create_tags_example())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn bench_get_object_tags(criterion: &mut Criterion) {
|
pub(crate) fn bench_get_object_tags(criterion: &mut Criterion) {
|
||||||
|
if skip_express_mode("bench_get_object_tags") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
benchmark_s3_api(
|
benchmark_s3_api(
|
||||||
"get_object_tags",
|
"get_object_tags",
|
||||||
criterion,
|
criterion,
|
||||||
|| async {
|
|| async {
|
||||||
let ctx = Ctx2::new_with_object(false).await;
|
let ctx = Ctx2::new_with_object(false).await;
|
||||||
let _resp: SetObjectTagsResponse = SetObjectTags::new(&ctx.bucket)
|
|
||||||
.client(&ctx.client)
|
let _resp: SetObjectTagsResponse = ctx
|
||||||
.object(ctx.object.clone())
|
.client
|
||||||
|
.set_object_tags(&ctx.bucket, &ctx.object)
|
||||||
.tags(create_tags_example())
|
.tags(create_tags_example())
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx
|
ctx
|
||||||
},
|
},
|
||||||
|ctx| {
|
|ctx| GetObjectTags::new(ctx.client.clone(), ctx.bucket.clone(), ctx.object.clone()),
|
||||||
GetObjectTags::new(&ctx.bucket)
|
|
||||||
.client(&ctx.client)
|
|
||||||
.object(ctx.object.clone())
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,14 @@ use criterion::Criterion;
|
|||||||
use minio::s3::Client;
|
use minio::s3::Client;
|
||||||
use minio::s3::error::Error;
|
use minio::s3::error::Error;
|
||||||
use minio::s3::response::{MakeBucketResponse, PutObjectContentResponse};
|
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::cleanup_guard::CleanupGuard;
|
||||||
use minio_common::test_context::TestContext;
|
use minio_common::test_context::TestContext;
|
||||||
use minio_common::utils::{
|
use minio_common::utils::{
|
||||||
get_bytes_from_response, get_response_from_bytes, rand_bucket_name, rand_object_name,
|
get_bytes_from_response, get_response_from_bytes, rand_bucket_name, rand_object_name,
|
||||||
};
|
};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
pub(crate) struct Ctx2 {
|
pub(crate) struct Ctx2 {
|
||||||
@ -67,7 +68,7 @@ impl Ctx2 {
|
|||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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 object_name = rand_object_name();
|
||||||
let data = bytes::Bytes::from("hello, world".to_string().into_bytes());
|
let data = bytes::Bytes::from("hello, world".to_string().into_bytes());
|
||||||
let _resp: PutObjectContentResponse = ctx
|
let _resp: PutObjectContentResponse = ctx
|
||||||
@ -90,7 +91,7 @@ impl Ctx2 {
|
|||||||
pub async fn new_aux(&mut self) -> String {
|
pub async fn new_aux(&mut self) -> String {
|
||||||
let bucket_name: String = rand_bucket_name();
|
let bucket_name: String = rand_bucket_name();
|
||||||
self.aux_bucket = Some(bucket_name.clone());
|
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
|
let _resp: MakeBucketResponse = self
|
||||||
.client
|
.client
|
||||||
.make_bucket(&bucket_name)
|
.make_bucket(&bucket_name)
|
||||||
@ -140,7 +141,7 @@ pub(crate) fn benchmark_s3_api<ApiType, GlobalSetupFuture>(
|
|||||||
|
|
||||||
// Per-iteration setup for initial request
|
// Per-iteration setup for initial request
|
||||||
let api = per_iter_setup(&ctx);
|
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
|
// Execute the request to get a response, store the bytes for swift cloning
|
||||||
let bytes: bytes::Bytes = rt.block_on(async {
|
let bytes: bytes::Bytes = rt.block_on(async {
|
||||||
@ -165,3 +166,11 @@ pub(crate) fn benchmark_s3_api<ApiType, GlobalSetupFuture>(
|
|||||||
|
|
||||||
group.finish();
|
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 async_std::future::timeout;
|
||||||
use minio::s3::Client;
|
use minio::s3::Client;
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
/// Cleanup guard that removes the bucket when it is dropped
|
/// Cleanup guard that removes the bucket when it is dropped
|
||||||
@ -25,10 +26,10 @@ pub struct CleanupGuard {
|
|||||||
|
|
||||||
impl CleanupGuard {
|
impl CleanupGuard {
|
||||||
#[allow(dead_code)]
|
#[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 {
|
Self {
|
||||||
client: client.clone(),
|
client,
|
||||||
bucket_name: bucket_name.to_string(),
|
bucket_name: bucket_name.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +58,9 @@ impl Drop for CleanupGuard {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
//println!("Bucket {} removed successfully", bucket_name),
|
//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),
|
Err(_) => println!("Timeout after 60s while removing bucket {}", bucket_name),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use minio::s3::args::PostPolicy;
|
use chrono::{DateTime, Utc};
|
||||||
|
use minio::s3::builders::PostPolicy;
|
||||||
use minio::s3::types::{
|
use minio::s3::types::{
|
||||||
AndOperator, Destination, Filter, LifecycleConfig, LifecycleRule, NotificationConfig,
|
AndOperator, CsvInputSerialization, CsvOutputSerialization, Destination, FileHeaderInfo,
|
||||||
ObjectLockConfig, PrefixFilterRule, QueueConfig, ReplicationConfig, ReplicationRule,
|
Filter, LifecycleConfig, LifecycleRule, NotificationConfig, ObjectLockConfig, PrefixFilterRule,
|
||||||
RetentionMode, SuffixFilterRule,
|
QueueConfig, QuoteFields, ReplicationConfig, ReplicationRule, RetentionMode, SelectRequest,
|
||||||
|
SuffixFilterRule,
|
||||||
};
|
};
|
||||||
use minio::s3::utils::utc_now;
|
use minio::s3::utils::utc_now;
|
||||||
use std::collections::HashMap;
|
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()
|
ObjectLockConfig::new(RetentionMode::GOVERNANCE, Some(DURATION_DAYS), None).unwrap()
|
||||||
}
|
}
|
||||||
pub fn create_post_policy_example(bucket_name: &str, object_name: &str) -> PostPolicy {
|
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();
|
let mut policy = PostPolicy::new(&bucket_name, expiration).unwrap();
|
||||||
policy.add_equals_condition("key", &object_name).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();
|
.unwrap();
|
||||||
policy
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct TestContext {
|
pub struct TestContext {
|
||||||
|
pub client: Client,
|
||||||
pub base_url: BaseUrl,
|
pub base_url: BaseUrl,
|
||||||
pub access_key: String,
|
pub access_key: String,
|
||||||
pub secret_key: String,
|
pub secret_key: String,
|
||||||
pub ignore_cert_check: Option<bool>,
|
pub ignore_cert_check: Option<bool>,
|
||||||
pub ssl_cert_file: Option<PathBuf>,
|
pub ssl_cert_file: Option<PathBuf>,
|
||||||
pub client: Client,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestContext {
|
impl TestContext {
|
||||||
@ -66,12 +66,12 @@ impl TestContext {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
client,
|
||||||
base_url,
|
base_url,
|
||||||
access_key,
|
access_key,
|
||||||
secret_key,
|
secret_key,
|
||||||
ignore_cert_check: Some(ignore_cert_check),
|
ignore_cert_check: Some(ignore_cert_check),
|
||||||
ssl_cert_file: ssl_cert_file.map(PathBuf::from),
|
ssl_cert_file: ssl_cert_file.map(PathBuf::from),
|
||||||
client,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const DEFAULT_SERVER_ENDPOINT: &str = "https://play.min.io/";
|
const DEFAULT_SERVER_ENDPOINT: &str = "https://play.min.io/";
|
||||||
@ -123,12 +123,12 @@ impl TestContext {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
client,
|
||||||
base_url,
|
base_url,
|
||||||
access_key,
|
access_key,
|
||||||
secret_key,
|
secret_key,
|
||||||
ignore_cert_check: Some(ignore_cert_check),
|
ignore_cert_check: Some(ignore_cert_check),
|
||||||
ssl_cert_file: Some(ssl_cert_file),
|
ssl_cert_file: Some(ssl_cert_file),
|
||||||
client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ impl TestContext {
|
|||||||
/// - `CleanupGuard` - A guard that automatically deletes the bucket when dropped.
|
/// - `CleanupGuard` - A guard that automatically deletes the bucket when dropped.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```rust
|
/// ```ignore
|
||||||
/// let (bucket_name, guard) = client.create_bucket_helper().await;
|
/// let (bucket_name, guard) = client.create_bucket_helper().await;
|
||||||
/// println!("Created temporary bucket: {}", bucket_name);
|
/// println!("Created temporary bucket: {}", bucket_name);
|
||||||
/// // The bucket will be removed when `guard` is dropped.
|
/// // The bucket will be removed when `guard` is dropped.
|
||||||
@ -152,7 +152,7 @@ impl TestContext {
|
|||||||
pub async fn create_bucket_helper(&self) -> (String, CleanupGuard) {
|
pub async fn create_bucket_helper(&self) -> (String, CleanupGuard) {
|
||||||
let bucket_name = rand_bucket_name();
|
let bucket_name = rand_bucket_name();
|
||||||
let _resp = self.client.make_bucket(&bucket_name).send().await.unwrap();
|
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)
|
(bucket_name, guard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ pub fn rand_bucket_name() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rand_object_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 {
|
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)
|
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(
|
pub async fn create_bucket_if_not_exists(
|
||||||
bucket_name: &str,
|
bucket_name: &str,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
|||||||
@ -16,10 +16,11 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use crate::common::create_bucket_if_not_exists;
|
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::client::ClientBuilder;
|
||||||
use minio::s3::creds::StaticProvider;
|
use minio::s3::creds::StaticProvider;
|
||||||
use minio::s3::http::BaseUrl;
|
use minio::s3::http::BaseUrl;
|
||||||
|
use minio::s3::response::ObjectPromptResponse;
|
||||||
use minio::s3::types::S3Api;
|
use minio::s3::types::S3Api;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -65,12 +66,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
filename.display()
|
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
|
//.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: '{}'", resp.prompt_response);
|
||||||
log::info!("Object prompt result: '{}'", res.prompt_response);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use clap::Parser;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use minio::s3::response::BucketExistsResponse;
|
use minio::s3::response::BucketExistsResponse;
|
||||||
use minio::s3::types::S3Api;
|
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;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Upload a file to the given bucket and object path on the MinIO Play server.
|
/// 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,
|
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)))
|
.provider(Some(Box::new(static_provider)))
|
||||||
.build()?;
|
.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
|
//! Argument builders for [minio::s3::client::Client](crate::s3::client::Client) APIs
|
||||||
|
|
||||||
|
mod append_object;
|
||||||
mod bucket_common;
|
mod bucket_common;
|
||||||
mod bucket_exists;
|
mod bucket_exists;
|
||||||
|
mod copy_object;
|
||||||
mod delete_bucket_encryption;
|
mod delete_bucket_encryption;
|
||||||
mod delete_bucket_lifecycle;
|
mod delete_bucket_lifecycle;
|
||||||
mod delete_bucket_notification;
|
mod delete_bucket_notification;
|
||||||
@ -38,16 +40,19 @@ mod get_object;
|
|||||||
mod get_object_lock_config;
|
mod get_object_lock_config;
|
||||||
mod get_object_retention;
|
mod get_object_retention;
|
||||||
mod get_object_tags;
|
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 is_object_legal_hold_enabled;
|
||||||
mod list_buckets;
|
mod list_buckets;
|
||||||
mod list_objects;
|
mod list_objects;
|
||||||
mod listen_bucket_notification;
|
mod listen_bucket_notification;
|
||||||
mod make_bucket;
|
mod make_bucket;
|
||||||
mod object_content;
|
|
||||||
mod object_prompt;
|
mod object_prompt;
|
||||||
mod put_object;
|
mod put_object;
|
||||||
mod remove_bucket;
|
mod remove_bucket;
|
||||||
mod remove_objects;
|
mod remove_objects;
|
||||||
|
mod select_object_content;
|
||||||
mod set_bucket_encryption;
|
mod set_bucket_encryption;
|
||||||
mod set_bucket_lifecycle;
|
mod set_bucket_lifecycle;
|
||||||
mod set_bucket_notification;
|
mod set_bucket_notification;
|
||||||
@ -58,9 +63,13 @@ mod set_bucket_versioning;
|
|||||||
mod set_object_lock_config;
|
mod set_object_lock_config;
|
||||||
mod set_object_retention;
|
mod set_object_retention;
|
||||||
mod set_object_tags;
|
mod set_object_tags;
|
||||||
|
mod stat_object;
|
||||||
|
|
||||||
|
pub use crate::s3::object_content::*;
|
||||||
|
pub use append_object::*;
|
||||||
pub use bucket_common::*;
|
pub use bucket_common::*;
|
||||||
pub use bucket_exists::*;
|
pub use bucket_exists::*;
|
||||||
|
pub use copy_object::*;
|
||||||
pub use delete_bucket_encryption::*;
|
pub use delete_bucket_encryption::*;
|
||||||
pub use delete_bucket_lifecycle::*;
|
pub use delete_bucket_lifecycle::*;
|
||||||
pub use delete_bucket_notification::*;
|
pub use delete_bucket_notification::*;
|
||||||
@ -82,16 +91,19 @@ pub use get_object::*;
|
|||||||
pub use get_object_lock_config::*;
|
pub use get_object_lock_config::*;
|
||||||
pub use get_object_retention::*;
|
pub use get_object_retention::*;
|
||||||
pub use get_object_tags::*;
|
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 is_object_legal_hold_enabled::*;
|
||||||
pub use list_buckets::*;
|
pub use list_buckets::*;
|
||||||
pub use list_objects::*;
|
pub use list_objects::*;
|
||||||
pub use listen_bucket_notification::*;
|
pub use listen_bucket_notification::*;
|
||||||
pub use make_bucket::*;
|
pub use make_bucket::*;
|
||||||
pub use object_content::*;
|
|
||||||
pub use object_prompt::*;
|
pub use object_prompt::*;
|
||||||
pub use put_object::*;
|
pub use put_object::*;
|
||||||
pub use remove_bucket::*;
|
pub use remove_bucket::*;
|
||||||
pub use remove_objects::*;
|
pub use remove_objects::*;
|
||||||
|
pub use select_object_content::*;
|
||||||
pub use set_bucket_encryption::*;
|
pub use set_bucket_encryption::*;
|
||||||
pub use set_bucket_lifecycle::*;
|
pub use set_bucket_lifecycle::*;
|
||||||
pub use set_bucket_notification::*;
|
pub use set_bucket_notification::*;
|
||||||
@ -102,3 +114,4 @@ pub use set_bucket_versioning::*;
|
|||||||
pub use set_object_lock_config::*;
|
pub use set_object_lock_config::*;
|
||||||
pub use set_object_retention::*;
|
pub use set_object_retention::*;
|
||||||
pub use set_object_tags::*;
|
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 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 struct BucketCommon<A> {
|
||||||
pub(crate) client: Option<Client>,
|
pub(crate) client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
pub(crate) extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
pub(crate) extra_query_params: Option<Multimap>,
|
||||||
@ -30,18 +31,14 @@ pub struct BucketCommon<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Default> BucketCommon<A> {
|
impl<A: Default> BucketCommon<A> {
|
||||||
pub fn new(bucket: &str) -> BucketCommon<A> {
|
pub fn new(client: Client, bucket: String) -> BucketCommon<A> {
|
||||||
BucketCommon {
|
BucketCommon {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::BucketExistsResponse;
|
use crate::s3::response::BucketExistsResponse;
|
||||||
@ -32,30 +31,13 @@ impl S3Api for BucketExists {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::HEAD)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(self.extra_query_params.unwrap_or_default())
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketEncryptionResponse;
|
use crate::s3::response::DeleteBucketEncryptionResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_bucket_encryption()](Client::delete_bucket_encryption) API
|
/// Argument builder for [delete_bucket_encryption()](Client::delete_bucket_encryption) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketEncryption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "encryption"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketLifecycleResponse;
|
use crate::s3::response::DeleteBucketLifecycleResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_bucket_lifecycle()](Client::delete_bucket_lifecycle) API
|
/// Argument builder for [delete_bucket_lifecycle()](Client::delete_bucket_lifecycle) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketLifecycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::builders::{BucketCommon, SegmentedBytes};
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketNotificationResponse;
|
use crate::s3::response::DeleteBucketNotificationResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{NotificationConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
@ -33,24 +33,9 @@ impl S3Api for DeleteBucketNotification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 {
|
const CONFIG: NotificationConfig = NotificationConfig {
|
||||||
cloud_func_config_list: None,
|
cloud_func_config_list: None,
|
||||||
queue_config_list: None,
|
queue_config_list: None,
|
||||||
@ -60,15 +45,11 @@ impl ToS3Request for DeleteBucketNotification {
|
|||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
//TODO consider const body
|
//TODO consider const body
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "notification"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketPolicyResponse;
|
use crate::s3::response::DeleteBucketPolicyResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_bucket_policy()](Client::delete_bucket_policy) API
|
/// Argument builder for [delete_bucket_policy()](Client::delete_bucket_policy) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "policy"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketReplicationResponse;
|
use crate::s3::response::DeleteBucketReplicationResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_bucket_replication()](Client::delete_bucket_replication) API
|
/// Argument builder for [delete_bucket_replication()](Client::delete_bucket_replication) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketReplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "replication"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteBucketTagsResponse;
|
use crate::s3::response::DeleteBucketTagsResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_bucket_tags()](Client::delete_bucket_tags) API
|
/// Argument builder for [delete_bucket_tags()](Client::delete_bucket_tags) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for DeleteBucketTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "tagging"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::builders::{BucketCommon, SegmentedBytes};
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::DeleteObjectLockConfigResponse;
|
use crate::s3::response::DeleteObjectLockConfigResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{ObjectLockConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
@ -33,24 +33,9 @@ impl S3Api for DeleteObjectLockConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 {
|
let config = ObjectLockConfig {
|
||||||
retention_mode: None,
|
retention_mode: None,
|
||||||
retention_duration_days: None,
|
retention_duration_days: None,
|
||||||
@ -60,15 +45,11 @@ impl ToS3Request for DeleteObjectLockConfig {
|
|||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
//TODO consider const body
|
//TODO consider const body
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,36 +15,35 @@
|
|||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::DeleteObjectTagsResponse;
|
use crate::s3::response::DeleteObjectTagsResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [delete_object_tags()](Client::delete_object_tags) API
|
/// Argument builder for [delete_object_tags()](Client::delete_object_tags) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct DeleteObjectTags {
|
pub struct DeleteObjectTags {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object: String,
|
object: String,
|
||||||
pub version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeleteObjectTags {
|
impl DeleteObjectTags {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -61,11 +60,6 @@ impl DeleteObjectTags {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -77,36 +71,18 @@ impl S3Api for DeleteObjectTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let headers = self
|
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||||
.extra_headers
|
query_params.add_version(self.version_id);
|
||||||
.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 {
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
query_params.insert(String::from("versionId"), v.to_string());
|
.region(self.region)
|
||||||
}
|
.bucket(Some(self.bucket))
|
||||||
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)
|
.query_params(query_params)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.headers(headers);
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,41 +14,39 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::DisableObjectLegalHoldResponse;
|
use crate::s3::response::DisableObjectLegalHoldResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [disable_object_legal_hold()](Client::disable_object_legal_hold) API
|
/// Argument builder for [disable_object_legal_hold()](Client::disable_object_legal_hold) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct DisableObjectLegalHold {
|
pub struct DisableObjectLegalHold {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) object: String,
|
object: String,
|
||||||
pub(crate) version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisableObjectLegalHold {
|
impl DisableObjectLegalHold {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -59,11 +57,6 @@ impl DisableObjectLegalHold {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -75,42 +68,25 @@ impl S3Api for DisableObjectLegalHold {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let mut headers = self
|
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||||
.extra_headers
|
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||||
.as_ref()
|
query_params.add_version(self.version_id);
|
||||||
.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());
|
|
||||||
|
|
||||||
const PAYLOAD: &str = "<LegalHold><Status>OFF</Status></LegalHold>";
|
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)));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(PAYLOAD)));
|
||||||
//TODO consider const body
|
//TODO consider const body
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,41 +14,39 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::EnableObjectLegalHoldResponse;
|
use crate::s3::response::EnableObjectLegalHoldResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [enable_object_legal_hold()](Client::enable_object_legal_hold) API
|
/// Argument builder for [enable_object_legal_hold()](Client::enable_object_legal_hold) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct EnableObjectLegalHold {
|
pub struct EnableObjectLegalHold {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) object: String,
|
object: String,
|
||||||
pub(crate) version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnableObjectLegalHold {
|
impl EnableObjectLegalHold {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -59,11 +57,6 @@ impl EnableObjectLegalHold {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -75,42 +68,25 @@ impl S3Api for EnableObjectLegalHold {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let mut headers = self
|
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||||
.extra_headers
|
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||||
.as_ref()
|
query_params.add_version(self.version_id);
|
||||||
.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());
|
|
||||||
|
|
||||||
const PAYLOAD: &str = "<LegalHold><Status>ON</Status></LegalHold>";
|
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)));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(PAYLOAD)));
|
||||||
//TODO consider const body
|
//TODO consider const body
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use crate::s3::builders::BucketCommon;
|
|||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketEncryptionResponse;
|
use crate::s3::response::GetBucketEncryptionResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
|
/// 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 {
|
impl ToS3Request for GetBucketEncryption {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
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();
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
if let Some(v) = &self.extra_query_params {
|
.region(self.region)
|
||||||
merge(&mut query_params, v);
|
.bucket(Some(self.bucket))
|
||||||
}
|
.query_params(insert(self.extra_query_params, "encryption"))
|
||||||
query_params.insert(String::from("encryption"), String::new());
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketLifecycleResponse;
|
use crate::s3::response::GetBucketLifecycleResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_lifecycle()](Client::get_bucket_lifecycle) API
|
/// Argument builder for [get_bucket_lifecycle()](Client::get_bucket_lifecycle) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketLifecycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketNotificationResponse;
|
use crate::s3::response::GetBucketNotificationResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_notification()](Client::get_bucket_notification) API
|
/// Argument builder for [get_bucket_notification()](Client::get_bucket_notification) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketNotification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "notification"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketPolicyResponse;
|
use crate::s3::response::GetBucketPolicyResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_policy()](Client::get_bucket_policy) API
|
/// Argument builder for [get_bucket_policy()](Client::get_bucket_policy) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "policy"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketReplicationResponse;
|
use crate::s3::response::GetBucketReplicationResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_replication()](Client::get_bucket_replication) API
|
/// Argument builder for [get_bucket_replication()](Client::get_bucket_replication) API
|
||||||
@ -32,32 +31,13 @@ impl S3Api for GetBucketReplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "replication"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,38 +15,35 @@
|
|||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::GetBucketTagsResponse;
|
use crate::s3::response::GetBucketTagsResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 http::Method;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API
|
/// Argument builder for [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct GetBucketTags {
|
pub struct GetBucketTags {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) tags: HashMap<String, String>,
|
tags: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBucketTags {
|
impl GetBucketTags {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -73,32 +70,13 @@ impl S3Api for GetBucketTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "tagging"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use crate::s3::builders::BucketCommon;
|
|||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetBucketVersioningResponse;
|
use crate::s3::response::GetBucketVersioningResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API
|
/// 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 {
|
impl ToS3Request for GetBucketVersioning {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
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();
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
if let Some(v) = &self.extra_query_params {
|
.region(self.region)
|
||||||
merge(&mut query_params, v);
|
.bucket(Some(self.bucket))
|
||||||
}
|
.query_params(insert(self.extra_query_params, "versioning"))
|
||||||
query_params.insert(String::from("versioning"), String::new());
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,19 +15,21 @@
|
|||||||
|
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
|
use crate::s3::utils::check_object_name;
|
||||||
use crate::s3::{
|
use crate::s3::{
|
||||||
client::Client,
|
client::Client,
|
||||||
error::Error,
|
error::Error,
|
||||||
response::GetObjectResponse,
|
response::GetObjectResponse,
|
||||||
sse::{Sse, SseCustomerKey},
|
sse::{Sse, SseCustomerKey},
|
||||||
types::{S3Api, S3Request, ToS3Request},
|
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.
|
/// Argument builder for [list_objects()](Client::get_object) API.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct GetObject {
|
pub struct GetObject {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
@ -46,21 +48,16 @@ pub struct GetObject {
|
|||||||
unmodified_since: Option<UtcTime>,
|
unmodified_since: Option<UtcTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// builder interface
|
|
||||||
impl GetObject {
|
impl GetObject {
|
||||||
pub fn new(bucket: &str, object: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_string(),
|
client,
|
||||||
object: object.to_string(),
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -117,99 +114,69 @@ impl GetObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal helpers
|
impl S3Api for GetObject {
|
||||||
impl GetObject {
|
type S3Response = GetObjectResponse;
|
||||||
fn get_range_header_value(&self) -> Option<String> {
|
}
|
||||||
let (offset, length) = match self.length {
|
|
||||||
|
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),
|
Some(_) => (Some(self.offset.unwrap_or(0_u64)), self.length),
|
||||||
None => (self.offset, None),
|
None => (self.offset, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(o) = offset {
|
if let Some(o) = offset {
|
||||||
let mut range = String::new();
|
let mut range: String = String::new();
|
||||||
range.push_str("bytes=");
|
range.push_str("bytes=");
|
||||||
range.push_str(&o.to_string());
|
range.push_str(&o.to_string());
|
||||||
range.push('-');
|
range.push('-');
|
||||||
if let Some(l) = length {
|
if let Some(l) = length {
|
||||||
range.push_str(&(o + l - 1).to_string());
|
range.push_str(&(o + l - 1).to_string());
|
||||||
}
|
}
|
||||||
Some(range)
|
headers.add("Range", range);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_headers(&self) -> Multimap {
|
if let Some(v) = self.match_etag {
|
||||||
let mut headers = Multimap::new();
|
headers.add("if-match", v);
|
||||||
|
|
||||||
if let Some(val) = self.get_range_header_value() {
|
|
||||||
headers.insert(String::from("Range"), val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(v) = &self.match_etag {
|
if let Some(v) = self.not_match_etag {
|
||||||
headers.insert(String::from("if-match"), v.to_string());
|
headers.add("if-none-match", v);
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if let Some(v) = self.modified_since {
|
||||||
headers.insert(String::from("if-modified-since"), to_http_header_value(v));
|
headers.add("if-modified-since", to_http_header_value(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(v) = self.unmodified_since {
|
if let Some(v) = self.unmodified_since {
|
||||||
headers.insert(String::from("if-unmodified-since"), to_http_header_value(v));
|
headers.add("if-unmodified-since", to_http_header_value(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(v) = &self.ssec {
|
if let Some(v) = &self.ssec {
|
||||||
merge(&mut headers, &v.headers());
|
headers.add_multimap(v.headers());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
headers
|
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||||
}
|
query_params.add_version(self.version_id);
|
||||||
}
|
|
||||||
|
|
||||||
impl ToS3Request for GetObject {
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
.region(self.region)
|
||||||
check_bucket_name(&self.bucket, true)?;
|
.bucket(Some(self.bucket))
|
||||||
|
.object(Some(self.object))
|
||||||
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)
|
.query_params(query_params)
|
||||||
.headers(headers);
|
.headers(headers))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl S3Api for GetObject {
|
|
||||||
type S3Response = GetObjectResponse;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -13,12 +13,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::GetObjectLockConfigResponse;
|
use crate::s3::response::GetObjectLockConfigResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_object_lock_config()](Client::get_object_lock_config) API
|
/// 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 {
|
impl ToS3Request for GetObjectLockConfig {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,36 +15,35 @@
|
|||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::GetObjectRetentionResponse;
|
use crate::s3::response::GetObjectRetentionResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_object_retention()](Client::get_object_retention) API
|
/// Argument builder for [get_object_retention()](Client::get_object_retention) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct GetObjectRetention {
|
pub struct GetObjectRetention {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object: String,
|
object: String,
|
||||||
pub version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetObjectRetention {
|
impl GetObjectRetention {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -61,11 +60,6 @@ impl GetObjectRetention {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -77,36 +71,18 @@ impl S3Api for GetObjectRetention {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let headers = self
|
let mut query_params: Multimap = insert(self.extra_query_params, "retention");
|
||||||
.extra_headers
|
query_params.add_version(self.version_id);
|
||||||
.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 {
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
query_params.insert(String::from("versionId"), v.to_string());
|
.region(self.region)
|
||||||
}
|
.bucket(Some(self.bucket))
|
||||||
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))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.object(Some(self.object))
|
||||||
.object(Some(&self.object));
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,36 +15,35 @@
|
|||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::GetObjectTagsResponse;
|
use crate::s3::response::GetObjectTagsResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [get_object_tags()](Client::get_object_tags) API
|
/// Argument builder for [get_object_tags()](Client::get_object_tags) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct GetObjectTags {
|
pub struct GetObjectTags {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object: String,
|
object: String,
|
||||||
pub version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetObjectTags {
|
impl GetObjectTags {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -61,11 +60,6 @@ impl GetObjectTags {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -77,36 +71,18 @@ impl S3Api for GetObjectTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let headers = self
|
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||||
.extra_headers
|
query_params.add_version(self.version_id);
|
||||||
.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 {
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
query_params.insert(String::from("versionId"), v.to_string());
|
.region(self.region)
|
||||||
}
|
.bucket(Some(self.bucket))
|
||||||
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))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.headers(headers);
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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::Client;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::IsObjectLegalHoldEnabledResponse;
|
use crate::s3::response::IsObjectLegalHoldEnabledResponse;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [is_object_legal_hold_enabled()](Client::is_object_legal_hold_enabled) API
|
/// Argument builder for [is_object_legal_hold_enabled()](Client::is_object_legal_hold_enabled) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct IsObjectLegalHoldEnabled {
|
pub struct IsObjectLegalHoldEnabled {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) object: String,
|
object: String,
|
||||||
pub(crate) version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsObjectLegalHoldEnabled {
|
impl IsObjectLegalHoldEnabled {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -57,11 +55,6 @@ impl IsObjectLegalHoldEnabled {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -73,36 +66,18 @@ impl S3Api for IsObjectLegalHoldEnabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
let headers = self
|
let mut query_params: Multimap = insert(self.extra_query_params, "legal-hold");
|
||||||
.extra_headers
|
query_params.add_version(self.version_id);
|
||||||
.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 {
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
query_params.insert(String::from("versionId"), v.to_string());
|
.region(self.region)
|
||||||
}
|
.bucket(Some(self.bucket))
|
||||||
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))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.object(Some(&self.object));
|
.object(Some(self.object)))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,32 +15,29 @@
|
|||||||
|
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::ListBucketsResponse;
|
use crate::s3::response::ListBucketsResponse;
|
||||||
use crate::s3::{
|
use crate::s3::{
|
||||||
Client,
|
Client,
|
||||||
error::Error,
|
error::Error,
|
||||||
types::{S3Api, S3Request, ToS3Request},
|
types::{S3Api, S3Request, ToS3Request},
|
||||||
utils::Multimap,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Argument builder for [list_buckets()](Client::list_buckets) API.
|
/// Argument builder for [list_buckets()](Client::list_buckets) API.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ListBuckets {
|
pub struct ListBuckets {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// builder interface
|
|
||||||
impl ListBuckets {
|
impl ListBuckets {
|
||||||
pub fn new() -> Self {
|
pub fn new(client: Client) -> Self {
|
||||||
Default::default()
|
Self {
|
||||||
|
client,
|
||||||
|
..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 {
|
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 {
|
impl S3Api for ListBuckets {
|
||||||
type S3Response = ListBucketsResponse;
|
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 futures_util::{Stream, StreamExt, stream as futures_stream};
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
|
use crate::s3::utils::insert;
|
||||||
use crate::s3::{
|
use crate::s3::{
|
||||||
client::Client,
|
client::Client,
|
||||||
error::Error,
|
error::Error,
|
||||||
@ -24,34 +26,41 @@ use crate::s3::{
|
|||||||
ListObjectVersionsResponse, ListObjectsV1Response, ListObjectsV2Response,
|
ListObjectVersionsResponse, ListObjectsV1Response, ListObjectsV2Response,
|
||||||
},
|
},
|
||||||
types::{S3Api, S3Request, ToS3Request, ToStream},
|
types::{S3Api, S3Request, ToS3Request, ToStream},
|
||||||
utils::{Multimap, check_bucket_name, merge},
|
utils::check_bucket_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn add_common_list_objects_query_params(
|
fn add_common_list_objects_query_params(
|
||||||
query_params: &mut Multimap,
|
query_params: &mut Multimap,
|
||||||
delimiter: Option<&str>,
|
delimiter: Option<String>,
|
||||||
disable_url_encoding: bool,
|
disable_url_encoding: bool,
|
||||||
max_keys: Option<u16>,
|
max_keys: Option<u16>,
|
||||||
prefix: Option<&str>,
|
prefix: Option<String>,
|
||||||
) {
|
) {
|
||||||
query_params.insert(
|
query_params.add("delimiter", delimiter.unwrap_or("".into()));
|
||||||
String::from("delimiter"),
|
query_params.add("max-keys", max_keys.unwrap_or(1000).to_string());
|
||||||
delimiter.unwrap_or("").to_string(),
|
query_params.add("prefix", prefix.unwrap_or("".into()));
|
||||||
);
|
|
||||||
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());
|
|
||||||
if !disable_url_encoding {
|
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.
|
/// Argument for ListObjectsV1 S3 API.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ListObjectsV1 {
|
struct ListObjectsV1 {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: 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> {
|
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||||
Box::new(Box::pin(futures_stream::unfold(
|
Box::new(Box::pin(futures_stream::unfold(
|
||||||
(self.clone(), false),
|
(self, false),
|
||||||
move |(mut args, mut is_done)| async move {
|
move |(args, mut is_done)| async move {
|
||||||
|
// Stop the stream if no more data is available
|
||||||
if is_done {
|
if is_done {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let resp = args.send().await;
|
// Prepare a clone of `args` for the next iteration
|
||||||
match resp {
|
let mut args_for_next_request: ListObjectsV1 = args.clone();
|
||||||
|
|
||||||
|
// Handle the result of the API call
|
||||||
|
match args.send().await {
|
||||||
Ok(resp) => {
|
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;
|
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 {
|
impl S3Api for ListObjectsV1 {
|
||||||
type S3Response = ListObjectsV1Response;
|
type S3Response = ListObjectsV1Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function delimiter based on recursive flag when delimiter is not
|
impl ToS3Request for ListObjectsV1 {
|
||||||
// provided.
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
fn delim_helper(delim: Option<String>, recursive: bool) -> Option<String> {
|
check_bucket_name(&self.bucket, true)?;
|
||||||
if delim.is_some() {
|
|
||||||
return delim;
|
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);
|
||||||
}
|
}
|
||||||
match recursive {
|
}
|
||||||
true => None,
|
|
||||||
false => Some(String::from("/")),
|
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.
|
/// Argument for ListObjectsV2 S3 API.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ListObjectsV2 {
|
struct ListObjectsV2 {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
@ -177,6 +174,7 @@ struct ListObjectsV2 {
|
|||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
fetch_owner: bool,
|
fetch_owner: bool,
|
||||||
include_user_metadata: bool,
|
include_user_metadata: bool,
|
||||||
|
unsorted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[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> {
|
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||||
Box::new(Box::pin(futures_stream::unfold(
|
Box::new(Box::pin(futures_stream::unfold(
|
||||||
(self.clone(), false),
|
(self, false),
|
||||||
move |(mut args, mut is_done)| async move {
|
move |(args, mut is_done)| async move {
|
||||||
|
// Stop the stream if no more data is available
|
||||||
if is_done {
|
if is_done {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let resp = args.send().await;
|
// Prepare a clone of `args` for the next iteration
|
||||||
match resp {
|
let mut args_for_next_request = args.clone();
|
||||||
|
match args.send().await {
|
||||||
Ok(resp) => {
|
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);
|
.clone_from(&resp.next_continuation_token);
|
||||||
|
|
||||||
|
// Determine if there are more results to fetch
|
||||||
is_done = !resp.is_truncated;
|
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 {
|
impl ToS3Request for ListObjectsV2 {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let mut headers = Multimap::new();
|
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||||
if let Some(v) = &self.extra_headers {
|
{
|
||||||
merge(&mut headers, v);
|
query_params.add("list-type", "2");
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
add_common_list_objects_query_params(
|
||||||
&mut query_params,
|
&mut query_params,
|
||||||
self.delimiter.as_deref(),
|
self.delimiter,
|
||||||
self.disable_url_encoding,
|
self.disable_url_encoding,
|
||||||
self.max_keys,
|
self.max_keys,
|
||||||
self.prefix.as_deref(),
|
self.prefix,
|
||||||
);
|
);
|
||||||
if let Some(v) = &self.continuation_token {
|
if let Some(v) = self.continuation_token {
|
||||||
query_params.insert(String::from("continuation-token"), v.to_string());
|
query_params.add("continuation-token", v);
|
||||||
}
|
}
|
||||||
if self.fetch_owner {
|
if self.fetch_owner {
|
||||||
query_params.insert(String::from("fetch-owner"), String::from("true"));
|
query_params.add("fetch-owner", "true");
|
||||||
}
|
}
|
||||||
if let Some(v) = &self.start_after {
|
if let Some(v) = self.start_after {
|
||||||
query_params.insert(String::from("start-after"), v.to_string());
|
query_params.add("start-after", v);
|
||||||
}
|
}
|
||||||
if self.include_user_metadata {
|
if self.include_user_metadata {
|
||||||
query_params.insert(String::from("metadata"), String::from("true"));
|
query_params.add("metadata", "true");
|
||||||
|
}
|
||||||
|
if self.unsorted {
|
||||||
|
query_params.add("unsorted", "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = S3Request::new(
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
.region(self.region)
|
||||||
Method::GET,
|
.bucket(Some(self.bucket))
|
||||||
)
|
|
||||||
.region(self.region.as_deref())
|
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers);
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,14 +270,18 @@ impl From<ListObjects> for ListObjectsV2 {
|
|||||||
continuation_token: value.continuation_token,
|
continuation_token: value.continuation_token,
|
||||||
fetch_owner: value.fetch_owner,
|
fetch_owner: value.fetch_owner,
|
||||||
include_user_metadata: value.include_user_metadata,
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ListObjectVersions {
|
struct ListObjectVersions {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
@ -291,6 +294,7 @@ struct ListObjectVersions {
|
|||||||
key_marker: Option<String>,
|
key_marker: Option<String>,
|
||||||
version_id_marker: Option<String>,
|
version_id_marker: Option<String>,
|
||||||
include_user_metadata: bool,
|
include_user_metadata: bool,
|
||||||
|
unsorted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[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> {
|
async fn to_stream(self) -> Box<dyn Stream<Item = Result<Self::Item, Error>> + Unpin + Send> {
|
||||||
Box::new(Box::pin(futures_stream::unfold(
|
Box::new(Box::pin(futures_stream::unfold(
|
||||||
(self.clone(), false),
|
(self, false),
|
||||||
move |(mut args, mut is_done)| async move {
|
move |(args, mut is_done)| async move {
|
||||||
|
// Stop the stream if no more data is available
|
||||||
if is_done {
|
if is_done {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let resp = args.send().await;
|
// Prepare a clone of `args` for the next iteration
|
||||||
match resp {
|
let mut args_for_next_request = args.clone();
|
||||||
|
match args.send().await {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
args.key_marker.clone_from(&resp.next_key_marker);
|
// Update the key_marker for the next request
|
||||||
args.version_id_marker
|
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);
|
.clone_from(&resp.next_version_id_marker);
|
||||||
|
|
||||||
|
// Determine if there are more results to fetch
|
||||||
is_done = !resp.is_truncated;
|
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 {
|
impl ToS3Request for ListObjectVersions {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let mut headers = Multimap::new();
|
let mut query_params: Multimap = insert(self.extra_query_params, "versions");
|
||||||
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("versions"), String::new());
|
|
||||||
add_common_list_objects_query_params(
|
add_common_list_objects_query_params(
|
||||||
&mut query_params,
|
&mut query_params,
|
||||||
self.delimiter.as_deref(),
|
self.delimiter,
|
||||||
self.disable_url_encoding,
|
self.disable_url_encoding,
|
||||||
self.max_keys,
|
self.max_keys,
|
||||||
self.prefix.as_deref(),
|
self.prefix,
|
||||||
);
|
);
|
||||||
if let Some(v) = &self.key_marker {
|
if let Some(v) = self.key_marker {
|
||||||
query_params.insert(String::from("key-marker"), v.to_string());
|
query_params.add("key-marker", v);
|
||||||
}
|
}
|
||||||
if let Some(v) = &self.version_id_marker {
|
if let Some(v) = self.version_id_marker {
|
||||||
query_params.insert(String::from("version-id-marker"), v.to_string());
|
query_params.add("version-id-marker", v);
|
||||||
}
|
}
|
||||||
if self.include_user_metadata {
|
if self.include_user_metadata {
|
||||||
query_params.insert(String::from("metadata"), String::from("true"));
|
query_params.add("metadata", "true");
|
||||||
|
}
|
||||||
|
if self.unsorted {
|
||||||
|
query_params.add("unsorted", "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = S3Request::new(
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
self.client.as_ref().ok_or(Error::NoClientProvided)?,
|
.region(self.region)
|
||||||
Method::GET,
|
.bucket(Some(self.bucket))
|
||||||
)
|
|
||||||
.region(self.region.as_deref())
|
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers);
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ListObjects> for ListObjectVersions {
|
impl From<ListObjects> for ListObjectVersions {
|
||||||
fn from(value: ListObjects) -> Self {
|
fn from(value: ListObjects) -> Self {
|
||||||
ListObjectVersions {
|
Self {
|
||||||
client: value.client,
|
client: value.client,
|
||||||
extra_headers: value.extra_headers,
|
extra_headers: value.extra_headers,
|
||||||
extra_query_params: value.extra_query_params,
|
extra_query_params: value.extra_query_params,
|
||||||
@ -383,10 +389,15 @@ impl From<ListObjects> for ListObjectVersions {
|
|||||||
key_marker: value.key_marker,
|
key_marker: value.key_marker,
|
||||||
version_id_marker: value.version_id_marker,
|
version_id_marker: value.version_id_marker,
|
||||||
include_user_metadata: value.include_user_metadata,
|
include_user_metadata: value.include_user_metadata,
|
||||||
|
unsorted: value.unsorted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion: list-object-versions
|
||||||
|
|
||||||
|
// region: list-objects
|
||||||
|
|
||||||
/// Argument builder for
|
/// Argument builder for
|
||||||
/// [list_objects()](crate::s3::client::Client::list_objects) API.
|
/// [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.
|
/// a stream of results. Pagination is automatically performed.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ListObjects {
|
pub struct ListObjects {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
// Parameters common to all ListObjects APIs.
|
// Parameters common to all ListObjects APIs.
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
@ -424,6 +435,7 @@ pub struct ListObjects {
|
|||||||
recursive: bool,
|
recursive: bool,
|
||||||
use_api_v1: bool,
|
use_api_v1: bool,
|
||||||
include_versions: bool,
|
include_versions: bool,
|
||||||
|
unsorted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -445,18 +457,13 @@ impl ToStream for ListObjects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ListObjects {
|
impl ListObjects {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -556,4 +563,11 @@ impl ListObjects {
|
|||||||
self.include_versions = include_versions;
|
self.include_versions = include_versions;
|
||||||
self
|
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 futures_util::Stream;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::{
|
use crate::s3::{
|
||||||
client::Client,
|
client::Client,
|
||||||
error::Error,
|
error::Error,
|
||||||
response::ListenBucketNotificationResponse,
|
response::ListenBucketNotificationResponse,
|
||||||
types::{NotificationRecords, S3Api, S3Request, ToS3Request},
|
types::{NotificationRecords, S3Api, S3Request, ToS3Request},
|
||||||
utils::{Multimap, check_bucket_name, merge},
|
utils::check_bucket_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Argument builder for
|
/// Argument builder for
|
||||||
@ -30,7 +31,7 @@ use crate::s3::{
|
|||||||
/// API.
|
/// API.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ListenBucketNotification {
|
pub struct ListenBucketNotification {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
@ -41,72 +42,15 @@ pub struct ListenBucketNotification {
|
|||||||
events: Option<Vec<String>>,
|
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 {
|
impl ListenBucketNotification {
|
||||||
pub fn new(bucket_name: &str) -> ListenBucketNotification {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
ListenBucketNotification {
|
Self {
|
||||||
bucket: bucket_name.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -137,3 +81,47 @@ impl ListenBucketNotification {
|
|||||||
self
|
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.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::client::DEFAULT_REGION;
|
use crate::s3::client::DEFAULT_REGION;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::http::BaseUrl;
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::MakeBucketResponse;
|
use crate::s3::response::MakeBucketResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [make_bucket()](Client::make_bucket) API
|
/// Argument builder for [make_bucket()](Client::make_bucket) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct MakeBucket {
|
pub struct MakeBucket {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object_lock: bool,
|
object_lock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MakeBucket {
|
impl MakeBucket {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -70,64 +66,37 @@ impl MakeBucket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct MakeBucketPhantomData;
|
|
||||||
|
|
||||||
impl S3Api for MakeBucket {
|
impl S3Api for MakeBucket {
|
||||||
type S3Response = MakeBucketResponse;
|
type S3Response = MakeBucketResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request for MakeBucket {
|
impl ToS3Request for MakeBucket {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
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 region1: Option<&str> = self.region.as_deref();
|
||||||
let region2: Option<&str> = if base_url.region.is_empty() {
|
let region2: Option<&str> = self.client.get_region_from_url();
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(base_url.region.as_str())
|
|
||||||
};
|
|
||||||
|
|
||||||
let region: &str = match (region1, region2) {
|
let region_str: String = match (region1, region2) {
|
||||||
(None, None) => DEFAULT_REGION,
|
(None, None) => DEFAULT_REGION.to_string(),
|
||||||
(Some(r), None) | (None, Some(r)) => r, // Take the non-None value
|
(Some(_), None) => self.region.unwrap(),
|
||||||
(Some(r1), Some(r2)) if r1 == r2 => r1, // Both are Some and equal
|
(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)) => {
|
(Some(r1), Some(r2)) => {
|
||||||
return Err(Error::RegionMismatch(r1.to_string(), r2.to_string()));
|
return Err(Error::RegionMismatch(r1.to_string(), r2.to_string()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut headers: Multimap = self
|
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||||
.extra_headers
|
|
||||||
.as_ref()
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if self.object_lock {
|
if self.object_lock {
|
||||||
headers.insert(
|
headers.add("x-amz-bucket-object-lock-enabled", "true");
|
||||||
String::from("x-amz-bucket-object-lock-enabled"),
|
|
||||||
String::from("true"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let query_params: Multimap = self
|
let data: String = match region_str.as_str() {
|
||||||
.extra_query_params
|
|
||||||
.as_ref()
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let data: String = match region {
|
|
||||||
DEFAULT_REGION => String::new(),
|
DEFAULT_REGION => String::new(),
|
||||||
_ => format!(
|
_ => format!(
|
||||||
"<CreateBucketConfiguration><LocationConstraint>{}</LocationConstraint></CreateBucketConfiguration>",
|
"<CreateBucketConfiguration><LocationConstraint>{}</LocationConstraint></CreateBucketConfiguration>",
|
||||||
region
|
region_str
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -136,15 +105,11 @@ impl ToS3Request for MakeBucket {
|
|||||||
false => Some(SegmentedBytes::from(data)),
|
false => Some(SegmentedBytes::from(data)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(Some(region_str))
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(Some(region))
|
.query_params(self.extra_query_params.unwrap_or_default())
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::builders::SegmentedBytes;
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::sse::{Sse, SseCustomerKey};
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::utils::{Multimap, check_bucket_name, merge};
|
use crate::s3::sse::SseCustomerKey;
|
||||||
|
use crate::s3::utils::{check_bucket_name, check_object_name};
|
||||||
use crate::s3::{
|
use crate::s3::{
|
||||||
client::Client,
|
client::Client,
|
||||||
error::Error,
|
error::Error,
|
||||||
@ -28,7 +29,7 @@ use serde_json::json;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjectPrompt {
|
pub struct ObjectPrompt {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
bucket: String,
|
bucket: String,
|
||||||
object: String,
|
object: String,
|
||||||
prompt: String,
|
prompt: String,
|
||||||
@ -43,21 +44,16 @@ pub struct ObjectPrompt {
|
|||||||
|
|
||||||
// builder interface
|
// builder interface
|
||||||
impl ObjectPrompt {
|
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 {
|
ObjectPrompt {
|
||||||
client: None,
|
client,
|
||||||
bucket: bucket.to_string(),
|
bucket,
|
||||||
object: object.to_string(),
|
object,
|
||||||
prompt: prompt.to_string(),
|
prompt,
|
||||||
..Default::default()
|
..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 {
|
pub fn lambda_arn(mut self, lambda_arn: &str) -> Self {
|
||||||
self.lambda_arn = Some(lambda_arn.to_string());
|
self.lambda_arn = Some(lambda_arn.to_string());
|
||||||
self
|
self
|
||||||
@ -89,47 +85,28 @@ impl ObjectPrompt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal helpers
|
impl S3Api for ObjectPrompt {
|
||||||
impl ObjectPrompt {
|
type S3Response = ObjectPromptResponse;
|
||||||
fn get_headers(&self) -> Multimap {
|
|
||||||
let mut headers = Multimap::new();
|
|
||||||
if let Some(v) = &self.ssec {
|
|
||||||
merge(&mut headers, &v.headers());
|
|
||||||
}
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request for ObjectPrompt {
|
impl ToS3Request for ObjectPrompt {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
|
{
|
||||||
check_bucket_name(&self.bucket, true)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
if self.object.is_empty() {
|
if self.client.is_aws_host() {
|
||||||
return Err(Error::InvalidObjectName(String::from(
|
return Err(Error::UnsupportedApi("ObjectPrompt".into()));
|
||||||
"object name cannot be empty",
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
if self.ssec.is_some() && !self.client.is_secure() {
|
||||||
|
|
||||||
if self.ssec.is_some() && !client.is_secure() {
|
|
||||||
return Err(Error::SseTlsRequired(None));
|
return Err(Error::SseTlsRequired(None));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
|
||||||
|
query_params.add_version(self.version_id);
|
||||||
|
|
||||||
let mut headers = Multimap::new();
|
query_params.add(
|
||||||
if let Some(v) = &self.extra_headers {
|
"lambdaArn",
|
||||||
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"),
|
|
||||||
self.lambda_arn
|
self.lambda_arn
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
@ -139,18 +116,12 @@ impl ToS3Request for ObjectPrompt {
|
|||||||
let prompt_body = json!({ "prompt": self.prompt });
|
let prompt_body = json!({ "prompt": self.prompt });
|
||||||
let body: SegmentedBytes = SegmentedBytes::from(Bytes::from(prompt_body.to_string()));
|
let body: SegmentedBytes = SegmentedBytes::from(Bytes::from(prompt_body.to_string()));
|
||||||
|
|
||||||
let req = S3Request::new(client, Method::POST)
|
Ok(S3Request::new(self.client, Method::POST)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.body(Some(body));
|
.body(Some(body)))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
|
||||||
use crate::s3::builders::BucketCommon;
|
use crate::s3::builders::BucketCommon;
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
use crate::s3::response::RemoveBucketResponse;
|
use crate::s3::response::RemoveBucketResponse;
|
||||||
@ -32,30 +31,13 @@ impl S3Api for RemoveBucket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
Ok(S3Request::new(self.client, Method::DELETE)
|
||||||
.extra_headers
|
.region(self.region)
|
||||||
.as_ref()
|
.bucket(Some(self.bucket))
|
||||||
.filter(|v| !v.is_empty())
|
.query_params(self.extra_query_params.unwrap_or_default())
|
||||||
.cloned()
|
.headers(self.extra_headers.unwrap_or_default()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,26 +15,30 @@
|
|||||||
|
|
||||||
//! Builders for RemoveObject APIs.
|
//! Builders for RemoveObject APIs.
|
||||||
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::{Stream, StreamExt, stream as futures_stream};
|
use futures_util::{Stream, StreamExt, stream as futures_stream};
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use tokio_stream::iter as stream_iter;
|
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::{
|
use crate::s3::{
|
||||||
Client,
|
Client,
|
||||||
client_core::ClientCore,
|
|
||||||
error::Error,
|
error::Error,
|
||||||
response::{RemoveObjectResponse, RemoveObjectsResponse},
|
response::{RemoveObjectResponse, RemoveObjectsResponse},
|
||||||
types::{S3Api, S3Request, ToS3Request, ToStream},
|
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
|
/// Specify an object to be deleted. The object can be specified by key or by
|
||||||
/// key and version_id via the From trait.
|
/// key and version_id via the From trait.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjectToDelete {
|
pub struct ObjectToDelete {
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<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.
|
/// A key can be converted into a DeleteObject. The version_id is set to None.
|
||||||
impl From<&str> for ObjectToDelete {
|
impl From<&str> for ObjectToDelete {
|
||||||
fn from(key: &str) -> Self {
|
fn from(key: &str) -> Self {
|
||||||
ObjectToDelete {
|
Self {
|
||||||
key: key.to_string(),
|
key: key.to_owned(),
|
||||||
version_id: None,
|
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.
|
/// A tuple of key and version_id can be converted into a DeleteObject.
|
||||||
impl From<(&str, &str)> for ObjectToDelete {
|
impl From<(&str, &str)> for ObjectToDelete {
|
||||||
fn from((key, version_id): (&str, &str)) -> Self {
|
fn from((key, version_id): (&str, &str)) -> Self {
|
||||||
ObjectToDelete {
|
Self {
|
||||||
key: key.to_string(),
|
key: key.to_string(),
|
||||||
version_id: Some(version_id.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.
|
/// A tuple of key and option version_id can be converted into a DeleteObject.
|
||||||
impl From<(&str, Option<&str>)> for ObjectToDelete {
|
impl From<(&str, Option<&str>)> for ObjectToDelete {
|
||||||
fn from((key, version_id): (&str, Option<&str>)) -> Self {
|
fn from((key, version_id): (&str, Option<&str>)) -> Self {
|
||||||
ObjectToDelete {
|
Self {
|
||||||
key: key.to_string(),
|
key: key.to_string(),
|
||||||
version_id: version_id.map(|v| v.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 {
|
pub struct RemoveObject {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
bucket: String,
|
|
||||||
object: ObjectToDelete,
|
|
||||||
|
|
||||||
bypass_governance_mode: bool,
|
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
region: Option<String>,
|
region: Option<String>,
|
||||||
|
bucket: String,
|
||||||
|
|
||||||
|
object: ObjectToDelete,
|
||||||
|
bypass_governance_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoveObject {
|
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 {
|
Self {
|
||||||
client: None,
|
client,
|
||||||
|
bucket,
|
||||||
bucket: bucket.to_string(),
|
|
||||||
object: object.into(),
|
object: object.into(),
|
||||||
|
..Default::default()
|
||||||
bypass_governance_mode: false,
|
|
||||||
|
|
||||||
extra_headers: None,
|
|
||||||
extra_query_params: None,
|
|
||||||
region: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||||
self.bypass_governance_mode = bypass_governance_mode;
|
self.bypass_governance_mode = bypass_governance_mode;
|
||||||
self
|
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 {
|
impl S3Api for RemoveObject {
|
||||||
type S3Response = RemoveObjectResponse;
|
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 {
|
pub struct RemoveObjectsApi {
|
||||||
client: Option<ClientCore>,
|
client: Client,
|
||||||
|
|
||||||
bucket: String,
|
bucket: String,
|
||||||
objects: Vec<ObjectToDelete>,
|
objects: Vec<ObjectToDelete>,
|
||||||
@ -176,27 +180,16 @@ pub struct RemoveObjectsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
RemoveObjectsApi {
|
||||||
client: None,
|
client,
|
||||||
|
bucket,
|
||||||
bucket: bucket.to_string(),
|
|
||||||
objects,
|
objects,
|
||||||
|
..Default::default()
|
||||||
bypass_governance_mode: false,
|
|
||||||
verbose_mode: false,
|
|
||||||
|
|
||||||
extra_headers: None,
|
|
||||||
extra_query_params: None,
|
|
||||||
region: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||||
self.bypass_governance_mode = bypass_governance_mode;
|
self.bypass_governance_mode = bypass_governance_mode;
|
||||||
self
|
self
|
||||||
@ -226,8 +219,12 @@ impl RemoveObjectsApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl S3Api for RemoveObjectsApi {
|
||||||
|
type S3Response = RemoveObjectsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
impl ToS3Request for RemoveObjectsApi {
|
impl ToS3Request for RemoveObjectsApi {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let mut data = String::from("<Delete>");
|
let mut data = String::from("<Delete>");
|
||||||
@ -249,56 +246,40 @@ impl ToS3Request for RemoveObjectsApi {
|
|||||||
data.push_str("</Delete>");
|
data.push_str("</Delete>");
|
||||||
let data: Bytes = data.into();
|
let data: Bytes = data.into();
|
||||||
|
|
||||||
let mut headers = Multimap::new();
|
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||||
if let Some(v) = &self.extra_headers {
|
{
|
||||||
merge(&mut headers, v);
|
|
||||||
}
|
|
||||||
if self.bypass_governance_mode {
|
if self.bypass_governance_mode {
|
||||||
headers.insert(
|
headers.add("x-amz-bypass-governance-retention", "true");
|
||||||
String::from("x-amz-bypass-governance-retention"),
|
|
||||||
String::from("true"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
headers.insert(
|
headers.add("Content-Type", "application/xml");
|
||||||
String::from("Content-Type"),
|
headers.add("Content-MD5", md5sum_hash(data.as_ref()));
|
||||||
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();
|
Ok(S3Request::new(self.client, Method::POST)
|
||||||
let req = S3Request::new(client, Method::POST)
|
.region(self.region)
|
||||||
.region(self.region.as_deref())
|
.bucket(Some(self.bucket))
|
||||||
.bucket(Some(&self.bucket))
|
.query_params(insert(self.extra_query_params, "delete"))
|
||||||
.query_params(query_params)
|
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.body(Some(data.into()));
|
.body(Some(data.into())))
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl S3Api for RemoveObjectsApi {
|
// endregion: remove-object-api
|
||||||
type S3Response = RemoveObjectsResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// region: delete-object
|
||||||
pub struct DeleteObjects {
|
pub struct DeleteObjects {
|
||||||
items: Pin<Box<dyn Stream<Item = ObjectToDelete> + Send + Sync>>,
|
items: Pin<Box<dyn Stream<Item = ObjectToDelete> + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeleteObjects {
|
impl DeleteObjects {
|
||||||
pub fn from_stream(s: impl Stream<Item = ObjectToDelete> + Send + Sync + 'static) -> Self {
|
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 {
|
impl From<ObjectToDelete> for DeleteObjects {
|
||||||
fn from(delete_object: ObjectToDelete) -> Self {
|
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,
|
I: Iterator<Item = ObjectToDelete> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn from(keys: I) -> Self {
|
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 {
|
pub struct RemoveObjects {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
bucket: String,
|
bucket: String,
|
||||||
objects: DeleteObjects,
|
objects: DeleteObjects,
|
||||||
@ -326,11 +311,10 @@ pub struct RemoveObjects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RemoveObjects {
|
impl RemoveObjects {
|
||||||
pub fn new(bucket: &str, objects: impl Into<DeleteObjects>) -> Self {
|
pub fn new(client: Client, bucket: String, objects: impl Into<DeleteObjects>) -> Self {
|
||||||
RemoveObjects {
|
Self {
|
||||||
client: None,
|
client,
|
||||||
|
bucket,
|
||||||
bucket: bucket.to_string(),
|
|
||||||
objects: objects.into(),
|
objects: objects.into(),
|
||||||
|
|
||||||
bypass_governance_mode: false,
|
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 {
|
pub fn bypass_governance_mode(mut self, bypass_governance_mode: bool) -> Self {
|
||||||
self.bypass_governance_mode = bypass_governance_mode;
|
self.bypass_governance_mode = bypass_governance_mode;
|
||||||
self
|
self
|
||||||
@ -386,15 +365,15 @@ impl RemoveObjects {
|
|||||||
if objects.is_empty() {
|
if objects.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let client_core = ClientCore::new(self.client.as_ref().ok_or(Error::NoClientProvided)?);
|
|
||||||
let request = RemoveObjectsApi::new(&self.bucket, objects)
|
Ok(Some(
|
||||||
.client(&client_core)
|
RemoveObjectsApi::new(self.client.clone(), self.bucket.clone(), objects)
|
||||||
.bypass_governance_mode(self.bypass_governance_mode)
|
.bypass_governance_mode(self.bypass_governance_mode)
|
||||||
.verbose_mode(self.verbose_mode)
|
.verbose_mode(self.verbose_mode)
|
||||||
.extra_headers(self.extra_headers.clone())
|
.extra_headers(self.extra_headers.clone())
|
||||||
.extra_query_params(self.extra_query_params.clone())
|
.extra_query_params(self.extra_query_params.clone())
|
||||||
.region(self.region.clone());
|
.region(self.region.clone()),
|
||||||
Ok(Some(request))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketEncryptionResponse;
|
use crate::s3::response::SetBucketEncryptionResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, SseConfig, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_bucket_encryption()](Client::set_bucket_encryption) API
|
/// Argument builder for [set_bucket_encryption()](Client::set_bucket_encryption) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketEncryption {
|
pub struct SetBucketEncryption {
|
||||||
client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
@ -36,18 +37,14 @@ pub struct SetBucketEncryption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketEncryption {
|
impl SetBucketEncryption {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -74,35 +71,17 @@ impl S3Api for SetBucketEncryption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 bytes: Bytes = self.config.to_xml().into();
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(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::GET)
|
Ok(S3Request::new(self.client, Method::GET)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.query_params(query_params)
|
.query_params(insert(self.extra_query_params, "encryption"))
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,38 +14,36 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::SetBucketLifecycleResponse;
|
use crate::s3::response::SetBucketLifecycleResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{LifecycleConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
|
/// Argument builder for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketLifecycle {
|
pub struct SetBucketLifecycle {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) config: LifecycleConfig,
|
config: LifecycleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketLifecycle {
|
impl SetBucketLifecycle {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -73,37 +71,20 @@ impl S3Api for SetBucketLifecycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let mut headers = self
|
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
|
||||||
.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 bytes: Bytes = self.config.to_xml().into();
|
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 body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "lifecycle"))
|
||||||
.bucket(Some(&self.bucket))
|
|
||||||
.query_params(query_params)
|
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,38 +14,36 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketNotificationResponse;
|
use crate::s3::response::SetBucketNotificationResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{NotificationConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API
|
/// Argument builder for [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketNotification {
|
pub struct SetBucketNotification {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) config: NotificationConfig,
|
config: NotificationConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketNotification {
|
impl SetBucketNotification {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketNotification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 bytes: Bytes = self.config.to_xml().into();
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "notification"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,38 +14,36 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketPolicyResponse;
|
use crate::s3::response::SetBucketPolicyResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API
|
/// Argument builder for [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketPolicy {
|
pub struct SetBucketPolicy {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) config: String, //TODO consider PolicyConfig struct
|
config: String, //TODO consider PolicyConfig struct
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketPolicy {
|
impl SetBucketPolicy {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
let bytes: Bytes = self.config.into();
|
||||||
.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 body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "policy"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,38 +14,36 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketReplicationResponse;
|
use crate::s3::response::SetBucketReplicationResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{ReplicationConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API
|
/// Argument builder for [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketReplication {
|
pub struct SetBucketReplication {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) config: ReplicationConfig,
|
config: ReplicationConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketReplication {
|
impl SetBucketReplication {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -73,36 +71,17 @@ impl S3Api for SetBucketReplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 bytes: Bytes = self.config.to_xml().into();
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "replication"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketTagsResponse;
|
use crate::s3::response::SetBucketTagsResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use std::collections::HashMap;
|
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
|
/// Argument builder for [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketTags {
|
pub struct SetBucketTags {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) tags: HashMap<String, String>,
|
tags: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketTags {
|
impl SetBucketTags {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -75,24 +72,10 @@ impl S3Api for SetBucketTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
let data: String = {
|
||||||
.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>");
|
let mut data = String::from("<Tagging>");
|
||||||
if !self.tags.is_empty() {
|
if !self.tags.is_empty() {
|
||||||
data.push_str("<TagSet>");
|
data.push_str("<TagSet>");
|
||||||
@ -109,17 +92,15 @@ impl ToS3Request for SetBucketTags {
|
|||||||
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 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)
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.query_params(query_params)
|
.query_params(insert(self.extra_query_params, "tagging"))
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetBucketVersioningResponse;
|
use crate::s3::response::SetBucketVersioningResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use std::fmt;
|
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
|
/// Argument builder for [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetBucketVersioning {
|
pub struct SetBucketVersioning {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) status: Option<VersioningStatus>,
|
status: Option<VersioningStatus>,
|
||||||
pub(crate) mfa_delete: Option<bool>,
|
mfa_delete: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetBucketVersioning {
|
impl SetBucketVersioning {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -98,24 +95,10 @@ impl S3Api for SetBucketVersioning {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
let headers = self
|
let data: String = {
|
||||||
.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("versioning".into(), String::new());
|
|
||||||
|
|
||||||
let mut data = "<VersioningConfiguration>".to_string();
|
let mut data = "<VersioningConfiguration>".to_string();
|
||||||
|
|
||||||
if let Some(v) = self.mfa_delete {
|
if let Some(v) = self.mfa_delete {
|
||||||
@ -135,17 +118,15 @@ impl ToS3Request for SetBucketVersioning {
|
|||||||
};
|
};
|
||||||
|
|
||||||
data.push_str("</VersioningConfiguration>");
|
data.push_str("</VersioningConfiguration>");
|
||||||
|
data
|
||||||
|
};
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(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)
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.query_params(query_params)
|
.query_params(insert(self.extra_query_params, "versioning"))
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::Multimap;
|
||||||
use crate::s3::response::SetObjectLockConfigResponse;
|
use crate::s3::response::SetObjectLockConfigResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{ObjectLockConfig, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
@ -26,29 +27,25 @@ use http::Method;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetObjectLockConfig {
|
pub struct SetObjectLockConfig {
|
||||||
pub(crate) client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub(crate) extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub(crate) extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub(crate) region: Option<String>,
|
region: Option<String>,
|
||||||
pub(crate) bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub(crate) config: ObjectLockConfig,
|
config: ObjectLockConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetObjectLockConfig {
|
impl SetObjectLockConfig {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -75,36 +72,17 @@ impl S3Api for SetObjectLockConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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)?;
|
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 bytes: Bytes = self.config.to_xml().into();
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
|
||||||
|
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
|
.region(self.region)
|
||||||
let req = S3Request::new(client, Method::PUT)
|
.bucket(Some(self.bucket))
|
||||||
.region(self.region.as_deref())
|
.query_params(insert(self.extra_query_params, "object-lock"))
|
||||||
.bucket(Some(&self.bucket))
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.query_params(query_params)
|
.body(body))
|
||||||
.headers(headers)
|
|
||||||
.body(body);
|
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,45 +14,44 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::SetObjectRetentionResponse;
|
use crate::s3::response::SetObjectRetentionResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{RetentionMode, S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
/// Argument builder for [set_object_retention()](Client::set_object_retention) API
|
/// Argument builder for [set_object_retention()](Client::set_object_retention) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetObjectRetention {
|
pub struct SetObjectRetention {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object: String,
|
object: String,
|
||||||
pub version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
pub bypass_governance_mode: bool,
|
bypass_governance_mode: bool,
|
||||||
pub retention_mode: Option<RetentionMode>,
|
retention_mode: Option<RetentionMode>,
|
||||||
pub retain_until_date: Option<UtcTime>,
|
retain_until_date: Option<UtcTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetObjectRetention {
|
impl SetObjectRetention {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
bypass_governance_mode: false,
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
self
|
self
|
||||||
@ -68,11 +67,6 @@ impl SetObjectRetention {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -99,49 +93,20 @@ impl S3Api for SetObjectRetention {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request for SetObjectRetention {
|
impl ToS3Request for SetObjectRetention {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(self) -> Result<S3Request, Error> {
|
||||||
//TODO move the following checks to a validate fn
|
{
|
||||||
check_bucket_name(&self.bucket, true)?;
|
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() {
|
if self.retention_mode.is_some() ^ self.retain_until_date.is_some() {
|
||||||
return Err(Error::InvalidRetentionConfig(String::from(
|
return Err(Error::InvalidRetentionConfig(String::from(
|
||||||
"both mode and retain_until_date must be set or unset",
|
"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();
|
|
||||||
|
|
||||||
if self.bypass_governance_mode {
|
|
||||||
headers.insert(
|
|
||||||
String::from("x-amz-bypass-governance-retention"),
|
|
||||||
String::from("true"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut query_params = self
|
let data: String = {
|
||||||
.extra_query_params
|
let mut data: String = "<Retention>".into();
|
||||||
.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("retention"), String::new());
|
|
||||||
|
|
||||||
let mut data: String = String::from("<Retention>");
|
|
||||||
if let Some(v) = &self.retention_mode {
|
if let Some(v) = &self.retention_mode {
|
||||||
data.push_str("<Mode>");
|
data.push_str("<Mode>");
|
||||||
data.push_str(&v.to_string());
|
data.push_str(&v.to_string());
|
||||||
@ -153,20 +118,24 @@ impl ToS3Request for SetObjectRetention {
|
|||||||
data.push_str("</RetainUntilDate>");
|
data.push_str("</RetainUntilDate>");
|
||||||
}
|
}
|
||||||
data.push_str("</Retention>");
|
data.push_str("</Retention>");
|
||||||
|
data
|
||||||
|
};
|
||||||
|
|
||||||
headers.insert(String::from("Content-MD5"), md5sum_hash(data.as_ref()));
|
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-MD5", md5sum_hash(data.as_ref()));
|
||||||
|
|
||||||
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(Bytes::from(data)));
|
let mut query_params: Multimap = insert(self.extra_query_params, "retention");
|
||||||
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
|
query_params.add_version(self.version_id);
|
||||||
|
|
||||||
let req = S3Request::new(client, Method::PUT)
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.body(body);
|
.body(Some(SegmentedBytes::from(Bytes::from(data)))))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::s3::Client;
|
use crate::s3::Client;
|
||||||
use crate::s3::builders::SegmentedBytes;
|
|
||||||
use crate::s3::error::Error;
|
use crate::s3::error::Error;
|
||||||
|
use crate::s3::multimap::{Multimap, MultimapExt};
|
||||||
use crate::s3::response::SetObjectTagsResponse;
|
use crate::s3::response::SetObjectTagsResponse;
|
||||||
|
use crate::s3::segmented_bytes::SegmentedBytes;
|
||||||
use crate::s3::types::{S3Api, S3Request, ToS3Request};
|
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 bytes::Bytes;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -26,29 +27,27 @@ use std::collections::HashMap;
|
|||||||
/// Argument builder for [set_object_tags()](Client::set_object_tags) API
|
/// Argument builder for [set_object_tags()](Client::set_object_tags) API
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SetObjectTags {
|
pub struct SetObjectTags {
|
||||||
pub client: Option<Client>,
|
client: Client,
|
||||||
|
|
||||||
pub extra_headers: Option<Multimap>,
|
extra_headers: Option<Multimap>,
|
||||||
pub extra_query_params: Option<Multimap>,
|
extra_query_params: Option<Multimap>,
|
||||||
pub region: Option<String>,
|
region: Option<String>,
|
||||||
pub bucket: String,
|
bucket: String,
|
||||||
|
|
||||||
pub object: String,
|
object: String,
|
||||||
pub version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
pub tags: HashMap<String, String>,
|
tags: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetObjectTags {
|
impl SetObjectTags {
|
||||||
pub fn new(bucket: &str) -> Self {
|
pub fn new(client: Client, bucket: String, object: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bucket: bucket.to_owned(),
|
client,
|
||||||
|
bucket,
|
||||||
|
object,
|
||||||
..Default::default()
|
..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 {
|
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
|
||||||
self.extra_headers = extra_headers;
|
self.extra_headers = extra_headers;
|
||||||
@ -65,11 +64,6 @@ impl SetObjectTags {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object(mut self, object: String) -> Self {
|
|
||||||
self.object = object;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
pub fn version_id(mut self, version_id: Option<String>) -> Self {
|
||||||
self.version_id = version_id;
|
self.version_id = version_id;
|
||||||
self
|
self
|
||||||
@ -86,35 +80,14 @@ impl S3Api for SetObjectTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request 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_bucket_name(&self.bucket, true)?;
|
||||||
|
check_object_name(&self.object)?;
|
||||||
|
|
||||||
// TODO add to all other function (that use object) the following test
|
let mut query_params: Multimap = insert(self.extra_query_params, "tagging");
|
||||||
// TODO should it be moved to the object setter function? or use validate as in put_object
|
query_params.add_version(self.version_id);
|
||||||
if self.object.is_empty() {
|
|
||||||
return Err(Error::InvalidObjectName(String::from(
|
|
||||||
"object name cannot be empty",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 data: String = {
|
||||||
let mut data = String::from("<Tagging>");
|
let mut data = String::from("<Tagging>");
|
||||||
if !self.tags.is_empty() {
|
if !self.tags.is_empty() {
|
||||||
data.push_str("<TagSet>");
|
data.push_str("<TagSet>");
|
||||||
@ -131,18 +104,16 @@ impl ToS3Request for SetObjectTags {
|
|||||||
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 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)
|
Ok(S3Request::new(self.client, Method::PUT)
|
||||||
.region(self.region.as_deref())
|
.region(self.region)
|
||||||
.bucket(Some(&self.bucket))
|
.bucket(Some(self.bucket))
|
||||||
.query_params(query_params)
|
.query_params(query_params)
|
||||||
.object(Some(&self.object))
|
.object(Some(self.object))
|
||||||
.headers(headers)
|
.headers(self.extra_headers.unwrap_or_default())
|
||||||
.body(body);
|
.body(body))
|
||||||
|
|
||||||
Ok(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
1390
src/s3/client.rs
1390
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;
|
use crate::s3::builders::BucketExists;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a BucketExists request builder.
|
/// Creates a [`BucketExists`] request builder.
|
||||||
pub fn bucket_exists(&self, bucket: &str) -> BucketExists {
|
///
|
||||||
BucketExists::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteBucketEncryption;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteBucketEncryption request builder.
|
/// Creates a [`DeleteBucketEncryption`] request builder.
|
||||||
pub fn delete_bucket_encryption(&self, bucket: &str) -> DeleteBucketEncryption {
|
///
|
||||||
DeleteBucketEncryption::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteBucketLifecycle;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteBucketLifecycle request builder.
|
/// Creates a [`DeleteBucketLifecycle`] request builder.
|
||||||
pub fn delete_bucket_lifecycle(&self, bucket: &str) -> DeleteBucketLifecycle {
|
///
|
||||||
DeleteBucketLifecycle::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteBucketNotification;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteBucketNotification request builder.
|
/// Creates a [`DeleteBucketNotification`] request builder.
|
||||||
pub fn delete_bucket_notification(&self, bucket: &str) -> DeleteBucketNotification {
|
///
|
||||||
DeleteBucketNotification::new(bucket).client(self)
|
/// 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 {
|
impl Client {
|
||||||
/// Create a DeleteBucketPolicy request builder.
|
/// Create a DeleteBucketPolicy request builder.
|
||||||
pub fn delete_bucket_policy(&self, bucket: &str) -> DeleteBucketPolicy {
|
/// Creates a [`DeleteBucketPolicy`] request builder.
|
||||||
DeleteBucketPolicy::new(bucket).client(self)
|
///
|
||||||
|
/// 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;
|
use crate::s3::builders::DeleteBucketReplication;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteBucketReplication request builder.
|
/// Creates a [`DeleteBucketReplication`] request builder.
|
||||||
pub fn delete_bucket_replication(&self, bucket: &str) -> DeleteBucketReplication {
|
///
|
||||||
DeleteBucketReplication::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteBucketTags;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteBucketTags request builder.
|
/// Creates a [`DeleteBucketTags`] request builder.
|
||||||
pub fn delete_bucket_tags(&self, bucket: &str) -> DeleteBucketTags {
|
///
|
||||||
DeleteBucketTags::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteObjectLockConfig;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteObjectLockConfig request builder.
|
/// Creates a [`DeleteObjectLockConfig`] request builder.
|
||||||
pub fn delete_object_lock_config(&self, bucket: &str) -> DeleteObjectLockConfig {
|
///
|
||||||
DeleteObjectLockConfig::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DeleteObjectTags;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DeleteObjectTags request builder.
|
/// Creates a [`DeleteObjectTags`] request builder.
|
||||||
pub fn delete_object_tags(&self, bucket: &str) -> DeleteObjectTags {
|
///
|
||||||
DeleteObjectTags::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::DisableObjectLegalHold;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a DisableObjectLegalHold request builder.
|
/// Creates a [`DisableObjectLegalHold`] request builder.
|
||||||
pub fn disable_object_legal_hold(&self, bucket: &str) -> DisableObjectLegalHold {
|
///
|
||||||
DisableObjectLegalHold::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::EnableObjectLegalHold;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a EnableObjectLegalHold request builder.
|
/// Creates a [`EnableObjectLegalHold`] request builder.
|
||||||
pub fn enable_object_legal_hold(&self, bucket: &str) -> EnableObjectLegalHold {
|
///
|
||||||
EnableObjectLegalHold::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::GetBucketEncryption;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketEncryption request builder.
|
/// Creates a [`GetBucketEncryption`] request builder.
|
||||||
pub fn get_bucket_encryption(&self, bucket: &str) -> GetBucketEncryption {
|
///
|
||||||
GetBucketEncryption::new(bucket).client(self)
|
/// 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 {
|
impl Client {
|
||||||
/// Create a GetBucketLifecycle request builder.
|
/// 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;
|
use crate::s3::builders::GetBucketNotification;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketNotification request builder.
|
/// Creates a [`GetBucketNotification`] request builder.
|
||||||
pub fn get_bucket_notification(&self, bucket: &str) -> GetBucketNotification {
|
///
|
||||||
GetBucketNotification::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::GetBucketPolicy;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketPolicy request builder.
|
/// Creates a [`GetBucketPolicy`] request builder.
|
||||||
pub fn get_bucket_policy(&self, bucket: &str) -> GetBucketPolicy {
|
///
|
||||||
GetBucketPolicy::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::GetBucketReplication;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketReplication request builder.
|
/// Creates a [`GetBucketReplication`] request builder.
|
||||||
pub fn get_bucket_replication(&self, bucket: &str) -> GetBucketReplication {
|
///
|
||||||
GetBucketReplication::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::GetBucketTags;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketTags request builder.
|
/// Creates a [`GetBucketTags`] request builder.
|
||||||
pub fn get_bucket_tags(&self, bucket: &str) -> GetBucketTags {
|
///
|
||||||
GetBucketTags::new(bucket).client(self)
|
/// 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;
|
use crate::s3::builders::GetBucketVersioning;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetBucketVersioning request builder.
|
/// Creates a [`GetBucketVersioning`] request builder.
|
||||||
pub fn get_bucket_versioning(&self, bucket: &str) -> GetBucketVersioning {
|
///
|
||||||
GetBucketVersioning::new(bucket).client(self)
|
/// 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.
|
//! S3 APIs for downloading objects.
|
||||||
|
|
||||||
|
use super::Client;
|
||||||
use crate::s3::builders::GetObject;
|
use crate::s3::builders::GetObject;
|
||||||
|
|
||||||
use super::Client;
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a GetObject request builder.
|
/// Creates a [`GetObject`] request builder.
|
||||||
pub fn get_object(&self, bucket: &str, object: &str) -> GetObject {
|
///
|
||||||
GetObject::new(bucket, object).client(self)
|
/// 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