Refactor bucket lifecycle (#121)

* bugfix & added example

* refactor set_bucket_lifecycle amd get_bucket_lifecycle
This commit is contained in:
Henk-Jan Lebbink 2025-03-04 02:41:04 +01:00 committed by GitHub
parent c4e302dda7
commit b2a6cb2655
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 497 additions and 191 deletions

View File

@ -34,7 +34,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let _resp: SetBucketEncryptionResponse = client
.set_bucket_encryption(bucket_name)
.config(SseConfig::default())
.sse_config(SseConfig::default())
.send()
.await?;

View File

@ -0,0 +1,88 @@
// 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_play};
use minio::s3::args::DeleteBucketLifecycleArgs;
use minio::s3::response::{
DeleteBucketLifecycleResponse, GetBucketLifecycleResponse, SetBucketLifecycleResponse,
};
use minio::s3::types::{Filter, LifecycleConfig, LifecycleRule, S3Api};
use minio::s3::Client;
#[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_play()?;
let bucket_name: &str = "lifecycle-rust-bucket";
create_bucket_if_not_exists(bucket_name, &client).await?;
if false {
// TODO
let resp: GetBucketLifecycleResponse =
client.get_bucket_lifecycle(bucket_name).send().await?;
log::info!("life cycle settings before setting: resp={:?}", resp);
}
let rules: Vec<LifecycleRule> = vec![LifecycleRule {
abort_incomplete_multipart_upload_days_after_initiation: None,
expiration_date: None,
expiration_days: Some(365),
expiration_expired_object_delete_marker: None,
filter: Filter {
and_operator: None,
prefix: Some(String::from("logs/")),
tag: None,
},
id: String::from("rule1"),
noncurrent_version_expiration_noncurrent_days: None,
noncurrent_version_transition_noncurrent_days: None,
noncurrent_version_transition_storage_class: None,
status: true,
transition_date: None,
transition_days: None,
transition_storage_class: None,
}];
let resp: SetBucketLifecycleResponse = client
.set_bucket_lifecycle(bucket_name)
.life_cycle_config(LifecycleConfig { rules })
.send()
.await?;
log::info!("response of setting life cycle config: resp={:?}", resp);
if false {
// TODO
let resp: GetBucketLifecycleResponse =
client.get_bucket_lifecycle(bucket_name).send().await?;
log::info!("life cycle settings after setting: resp={:?}", resp);
}
if false {
// TODO
let resp: DeleteBucketLifecycleResponse = client
.delete_bucket_lifecycle(&DeleteBucketLifecycleArgs {
extra_headers: None,
extra_query_params: None,
region: None,
bucket: "",
})
.await?;
log::info!("response of deleting lifecycle config: resp={:?}", resp);
}
Ok(())
}

View File

@ -19,8 +19,8 @@ use crate::s3::error::Error;
use crate::s3::signer::post_presign_v4;
use crate::s3::sse::{Sse, SseCustomerKey};
use crate::s3::types::{
Directive, LifecycleConfig, NotificationConfig, ObjectLockConfig, Part, ReplicationConfig,
Retention, RetentionMode, SelectRequest,
Directive, NotificationConfig, ObjectLockConfig, Part, ReplicationConfig, Retention,
RetentionMode, SelectRequest,
};
use crate::s3::utils::{
b64encode, check_bucket_name, merge, to_amz_date, to_http_header_value, to_iso8601utc,
@ -1333,58 +1333,6 @@ pub type DeleteBucketLifecycleArgs<'a> = BucketArgs<'a>;
/// Argument for [get_bucket_lifecycle()](crate::s3::client::Client::get_bucket_lifecycle) API
pub type GetBucketLifecycleArgs<'a> = BucketArgs<'a>;
/// Argument for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
pub struct SetBucketLifecycleArgs<'a> {
pub extra_headers: Option<&'a Multimap>,
pub extra_query_params: Option<&'a Multimap>,
pub region: Option<&'a str>,
pub bucket: &'a str,
pub config: &'a LifecycleConfig,
}
impl<'a> SetBucketLifecycleArgs<'a> {
/// Returns argument for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API with given bucket name and configuration
///
/// # Examples
///
/// ```
/// use minio::s3::args::*;
/// use minio::s3::types::*;
/// let mut rules: Vec<LifecycleRule> = Vec::new();
/// rules.push(LifecycleRule {
/// abort_incomplete_multipart_upload_days_after_initiation: None,
/// expiration_date: None,
/// expiration_days: Some(365),
/// expiration_expired_object_delete_marker: None,
/// filter: Filter {and_operator: None, prefix: Some(String::from("logs/")), tag: None},
/// id: String::from("rule1"),
/// noncurrent_version_expiration_noncurrent_days: None,
/// noncurrent_version_transition_noncurrent_days: None,
/// noncurrent_version_transition_storage_class: None,
/// status: true,
/// transition_date: None,
/// transition_days: None,
/// transition_storage_class: None,
/// });
/// let mut config = LifecycleConfig {rules};
/// let args = SetBucketLifecycleArgs::new("my-bucket", &config).unwrap();
/// ```
pub fn new(
bucket_name: &'a str,
config: &'a LifecycleConfig,
) -> Result<SetBucketLifecycleArgs<'a>, Error> {
check_bucket_name(bucket_name, true)?;
Ok(SetBucketLifecycleArgs {
extra_headers: None,
extra_query_params: None,
region: None,
bucket: bucket_name,
config,
})
}
}
/// Argument for [delete_bucket_notification()](crate::s3::client::Client::delete_bucket_notification) API
pub type DeleteBucketNotificationArgs<'a> = BucketArgs<'a>;

View File

@ -17,6 +17,7 @@
mod bucket_common;
mod get_bucket_encryption;
mod get_bucket_lifecycle;
mod get_bucket_versioning;
mod get_object;
mod list_buckets;
@ -27,10 +28,12 @@ mod object_prompt;
mod put_object;
mod remove_objects;
mod set_bucket_encryption;
mod set_bucket_lifecycle;
mod set_bucket_versioning;
pub use bucket_common::*;
pub use get_bucket_encryption::*;
pub use get_bucket_lifecycle::*;
pub use get_bucket_versioning::*;
pub use get_object::*;
pub use list_buckets::*;
@ -41,4 +44,5 @@ pub use object_prompt::*;
pub use put_object::*;
pub use remove_objects::*;
pub use set_bucket_encryption::*;
pub use set_bucket_lifecycle::*;
pub use set_bucket_versioning::*;

View File

@ -20,7 +20,7 @@ use crate::s3::types::{S3Api, S3Request, ToS3Request};
use crate::s3::utils::{check_bucket_name, merge, Multimap};
use http::Method;
/// Argument builder for [get_bucket_encryption()](Client::get_bucket_encryption) API
/// Argument builder for [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
pub type GetBucketEncryption = BucketCommon<GetBucketEncryptionPhantomData>;
#[derive(Default, Debug)]

View File

@ -0,0 +1,63 @@
// 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::builders::BucketCommon;
use crate::s3::error::Error;
use crate::s3::response::GetBucketLifecycleResponse;
use crate::s3::types::{S3Api, S3Request, ToS3Request};
use crate::s3::utils::check_bucket_name;
use crate::s3::Client;
use http::Method;
/// Argument builder for [get_bucket_lifecycle()](Client::get_bucket_lifecycle) API
pub type GetBucketLifecycle = BucketCommon<GetBucketLifecyclePhantomData>;
#[derive(Default, Debug)]
pub struct GetBucketLifecyclePhantomData;
impl S3Api for GetBucketLifecycle {
type S3Response = GetBucketLifecycleResponse;
}
impl ToS3Request for GetBucketLifecycle {
fn to_s3request(&self) -> Result<S3Request, Error> {
check_bucket_name(&self.bucket, true)?;
let headers = self
.extra_headers
.as_ref()
.filter(|v| !v.is_empty())
.cloned()
.unwrap_or_default();
let mut query_params = self
.extra_query_params
.as_ref()
.filter(|v| !v.is_empty())
.cloned()
.unwrap_or_default();
query_params.insert("lifecycle".into(), String::new());
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
let req = S3Request::new(client, Method::GET)
.region(self.region.as_deref())
.bucket(Some(&self.bucket))
.query_params(query_params)
.headers(headers);
Ok(req)
}
}

View File

@ -20,7 +20,7 @@ use crate::s3::types::{S3Api, S3Request, ToS3Request};
use crate::s3::utils::{check_bucket_name, merge, Multimap};
use http::Method;
/// Argument builder for [get_bucket_versioning()](Client::get_bucket_versioning) API
/// Argument builder for [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API
pub type GetBucketVersioning = BucketCommon<GetBucketVersioningPhantomData>;
#[derive(Default, Debug)]

View File

@ -63,7 +63,7 @@ impl SetBucketEncryption {
self
}
pub fn config(mut self, config: SseConfig) -> Self {
pub fn sse_config(mut self, config: SseConfig) -> Self {
self.config = config;
self
}

View File

@ -0,0 +1,109 @@
// 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::builders::SegmentedBytes;
use crate::s3::error::Error;
use crate::s3::response::SetBucketLifecycleResponse;
use crate::s3::types::{LifecycleConfig, S3Api, S3Request, ToS3Request};
use crate::s3::utils::{check_bucket_name, md5sum_hash, Multimap};
use crate::s3::Client;
use bytes::Bytes;
use http::Method;
/// Argument builder for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
#[derive(Clone, Debug, Default)]
pub struct SetBucketLifecycle {
pub(crate) client: Option<Client>,
pub(crate) extra_headers: Option<Multimap>,
pub(crate) extra_query_params: Option<Multimap>,
pub(crate) region: Option<String>,
pub(crate) bucket: String,
pub(crate) config: LifecycleConfig,
}
impl SetBucketLifecycle {
pub fn new(bucket: &str) -> Self {
Self {
bucket: bucket.to_owned(),
..Default::default()
}
}
pub fn client(mut self, client: &Client) -> Self {
self.client = Some(client.clone());
self
}
pub fn extra_headers(mut self, extra_headers: Option<Multimap>) -> Self {
self.extra_headers = extra_headers;
self
}
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 life_cycle_config(mut self, config: LifecycleConfig) -> Self {
self.config = config;
self
}
}
impl S3Api for SetBucketLifecycle {
type S3Response = SetBucketLifecycleResponse;
}
impl ToS3Request for SetBucketLifecycle {
fn to_s3request(&self) -> Result<S3Request, Error> {
check_bucket_name(&self.bucket, true)?;
let mut headers = self
.extra_headers
.as_ref()
.filter(|v| !v.is_empty())
.cloned()
.unwrap_or_default();
let mut query_params = self
.extra_query_params
.as_ref()
.filter(|v| !v.is_empty())
.cloned()
.unwrap_or_default();
query_params.insert("lifecycle".into(), String::new());
let bytes: Bytes = self.config.to_xml().into();
headers.insert(String::from("Content-MD5"), md5sum_hash(&bytes));
let body: Option<SegmentedBytes> = Some(SegmentedBytes::from(bytes));
let client: &Client = self.client.as_ref().ok_or(Error::NoClientProvided)?;
let req = S3Request::new(client, Method::PUT)
.region(self.region.as_deref())
.bucket(Some(&self.bucket))
.query_params(query_params)
.headers(headers)
.body(body);
Ok(req)
}
}

View File

@ -40,7 +40,7 @@ impl fmt::Display for VersioningStatus {
}
}
/// Argument builder for [set_bucket_encryption()](Client::set_bucket_encryption) API
/// Argument builder for [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
#[derive(Clone, Debug, Default)]
pub struct SetBucketVersioning {
pub(crate) client: Option<Client>,

View File

@ -29,8 +29,7 @@ use crate::s3::response::*;
use crate::s3::signer::{presign_v4, sign_v4_s3};
use crate::s3::sse::SseCustomerKey;
use crate::s3::types::{
Directive, LifecycleConfig, NotificationConfig, ObjectLockConfig, Part, ReplicationConfig,
RetentionMode,
Directive, NotificationConfig, ObjectLockConfig, Part, ReplicationConfig, RetentionMode,
};
use crate::s3::utils::{
from_iso8601utc, get_default_text, get_option_text, get_text, md5sum_hash, md5sum_hash_sb,
@ -48,6 +47,7 @@ use tokio::fs;
use xmltree::Element;
mod get_bucket_encryption;
mod get_bucket_lifecycle;
mod get_bucket_versioning;
mod get_object;
mod list_objects;
@ -56,6 +56,7 @@ mod object_prompt;
mod put_object;
mod remove_objects;
mod set_bucket_encryption;
mod set_bucket_lifecycle;
mod set_bucket_versioning;
use super::builders::{ListBuckets, SegmentedBytes};
@ -1204,7 +1205,7 @@ impl Client {
Ok(resp) => Ok(DeleteBucketEncryptionResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
}),
Err(e) => match e {
Error::S3Error(ref err) => {
@ -1212,7 +1213,7 @@ impl Client {
return Ok(DeleteBucketEncryptionResponse {
headers: HeaderMap::new(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
});
}
Err(e)
@ -1297,7 +1298,7 @@ impl Client {
Ok(DeleteBucketLifecycleResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -1351,7 +1352,7 @@ impl Client {
Ok(resp) => Ok(DeleteBucketPolicyResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
}),
Err(e) => match e {
Error::S3Error(ref err) => {
@ -1359,7 +1360,7 @@ impl Client {
return Ok(DeleteBucketPolicyResponse {
headers: HeaderMap::new(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
});
}
Err(e)
@ -1401,7 +1402,7 @@ impl Client {
Ok(resp) => Ok(DeleteBucketReplicationResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
}),
Err(e) => match e {
Error::S3Error(ref err) => {
@ -1409,7 +1410,7 @@ impl Client {
return Ok(DeleteBucketReplicationResponse {
headers: HeaderMap::new(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
});
}
Err(e)
@ -1451,7 +1452,7 @@ impl Client {
Ok(DeleteBucketTagsResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -1606,64 +1607,6 @@ impl Client {
})
}
pub async fn get_bucket_lifecycle(
&self,
args: &GetBucketLifecycleArgs<'_>,
) -> Result<GetBucketLifecycleResponse, Error> {
let region = self.get_region(args.bucket, args.region).await?;
let mut headers = Multimap::new();
if let Some(v) = &args.extra_headers {
merge(&mut headers, v);
}
let mut query_params = Multimap::new();
if let Some(v) = &args.extra_query_params {
merge(&mut query_params, v);
}
query_params.insert(String::from("lifecycle"), String::new());
match self
.execute(
Method::GET,
&region,
&mut headers,
&query_params,
Some(args.bucket),
None,
None,
)
.await
{
Ok(resp) => {
let header_map = resp.headers().clone();
let body = resp.bytes().await?;
let root = Element::parse(body.reader())?;
Ok(GetBucketLifecycleResponse {
headers: header_map.clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
config: LifecycleConfig::from_xml(&root)?,
})
}
Err(e) => match e {
Error::S3Error(ref err) => {
if err.code == "NoSuchLifecycleConfiguration" {
return Ok(GetBucketLifecycleResponse {
headers: HeaderMap::new(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
config: LifecycleConfig { rules: Vec::new() },
});
}
Err(e)
}
_ => Err(e),
},
}
}
pub async fn get_bucket_notification(
&self,
args: &GetBucketNotificationArgs<'_>,
@ -2274,7 +2217,7 @@ impl Client {
Ok(MakeBucketResponse {
headers: resp.headers().clone(),
region: region.to_string(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2521,43 +2464,7 @@ impl Client {
Ok(RemoveBucketResponse {
headers: resp.headers().clone(),
region: region.to_string(),
bucket_name: args.bucket.to_string(),
})
}
pub async fn set_bucket_lifecycle(
&self,
args: &SetBucketLifecycleArgs<'_>,
) -> Result<SetBucketLifecycleResponse, Error> {
let region = self.get_region(args.bucket, args.region).await?;
let mut headers = Multimap::new();
if let Some(v) = &args.extra_headers {
merge(&mut headers, v);
}
let mut query_params = Multimap::new();
if let Some(v) = &args.extra_query_params {
merge(&mut query_params, v);
}
query_params.insert(String::from("lifecycle"), String::new());
let resp = self
.execute(
Method::PUT,
&region,
&mut headers,
&query_params,
Some(args.bucket),
None,
Some(args.config.to_xml().into()),
)
.await?;
Ok(SetBucketLifecycleResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2593,7 +2500,7 @@ impl Client {
Ok(SetBucketNotificationResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2629,7 +2536,7 @@ impl Client {
Ok(SetBucketPolicyResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2665,7 +2572,7 @@ impl Client {
Ok(SetBucketReplicationResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2718,7 +2625,7 @@ impl Client {
Ok(SetBucketTagsResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2754,7 +2661,7 @@ impl Client {
Ok(SetObjectLockConfigResponse {
headers: resp.headers().clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
bucket: args.bucket.to_string(),
})
}
@ -2797,7 +2704,7 @@ impl Client {
}
data.push_str("</Retention>");
headers.insert(String::from("Content-MD5"), md5sum_hash(data.as_bytes()));
headers.insert(String::from("Content-MD5"), md5sum_hash(data.as_ref()));
let resp = self
.execute(

View File

@ -0,0 +1,26 @@
// 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::GetBucketLifecycle;
impl Client {
/// Create a GetBucketLifecycle request builder.
pub fn get_bucket_lifecycle(&self, bucket: &str) -> GetBucketLifecycle {
GetBucketLifecycle::new(bucket).client(self)
}
}

View File

@ -0,0 +1,59 @@
// 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::SetBucketLifecycle;
impl Client {
/// Create a SetBucketLifecycle request builder.
///
/// Returns argument for [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API with given bucket name and configuration
///
/// # Examples
///
/// ```ignore
/// use minio::s3::Client;
/// use minio::s3::types::{Filter, LifecycleConfig, LifecycleRule, S3Api};
///
/// #[tokio::main]
/// async fn main() {
/// let rules: Vec<LifecycleRule> = vec![LifecycleRule {
/// abort_incomplete_multipart_upload_days_after_initiation: None,
/// expiration_date: None,
/// expiration_days: Some(365),
/// expiration_expired_object_delete_marker: None,
/// filter: Filter {and_operator: None, prefix: Some(String::from("logs/")), tag: None},
/// id: String::from("rule1"),
/// noncurrent_version_expiration_noncurrent_days: None,
/// noncurrent_version_transition_noncurrent_days: None,
/// noncurrent_version_transition_storage_class: None,
/// status: true,
/// transition_date: None,
/// transition_days: None,
/// transition_storage_class: None,
/// }];
/// let client = Client::default();
/// let _resp = client
/// .set_bucket_lifecycle("my-bucket-name")
/// .life_cycle_config(LifecycleConfig { rules })
/// .send().await;
/// }
/// ```
pub fn set_bucket_lifecycle(&self, bucket: &str) -> SetBucketLifecycle {
SetBucketLifecycle::new(bucket).client(self)
}
}

View File

@ -24,14 +24,15 @@ use xmltree::Element;
use crate::s3::error::Error;
use crate::s3::types::{
parse_legal_hold, LifecycleConfig, NotificationConfig, ObjectLockConfig, ReplicationConfig,
RetentionMode, SelectProgress,
parse_legal_hold, NotificationConfig, ObjectLockConfig, ReplicationConfig, RetentionMode,
SelectProgress,
};
use crate::s3::utils::{
copy_slice, crc32, from_http_header_value, from_iso8601utc, get_text, uint32, UtcTime,
};
mod get_bucket_encryption;
mod get_bucket_lifecycle;
mod get_bucket_versioning;
mod get_object;
mod list_buckets;
@ -41,9 +42,11 @@ mod object_prompt;
mod put_object;
mod remove_objects;
mod set_bucket_encryption;
mod set_bucket_lifecycle;
mod set_bucket_versioning;
pub use get_bucket_encryption::GetBucketEncryptionResponse;
pub use get_bucket_lifecycle::GetBucketLifecycleResponse;
pub use get_bucket_versioning::GetBucketVersioningResponse;
pub use get_object::GetObjectResponse;
pub use list_buckets::ListBucketsResponse;
@ -57,6 +60,7 @@ pub use put_object::{
};
pub use remove_objects::{DeleteError, DeletedObject, RemoveObjectResponse, RemoveObjectsResponse};
pub use set_bucket_encryption::SetBucketEncryptionResponse;
pub use set_bucket_lifecycle::SetBucketLifecycleResponse;
pub use set_bucket_versioning::SetBucketVersioningResponse;
#[derive(Debug)]
@ -64,7 +68,7 @@ pub use set_bucket_versioning::SetBucketVersioningResponse;
pub struct BucketResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket_name: String,
pub bucket: String,
}
/// Response of [make_bucket()](crate::s3::client::Client::make_bucket) API
@ -577,18 +581,6 @@ pub struct IsObjectLegalHoldEnabledResponse {
/// Response of [delete_bucket_lifecycle()](crate::s3::client::Client::delete_bucket_lifecycle) API
pub type DeleteBucketLifecycleResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_lifecycle()](crate::s3::client::Client::get_bucket_lifecycle) API
pub struct GetBucketLifecycleResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket_name: String,
pub config: LifecycleConfig,
}
/// Response of [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
pub type SetBucketLifecycleResponse = BucketResponse;
/// Response of [delete_bucket_notification()](crate::s3::client::Client::delete_bucket_notification) API
pub type DeleteBucketNotificationResponse = BucketResponse;

View 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::s3::error::Error;
use crate::s3::types::{FromS3Response, LifecycleConfig, S3Request};
use async_trait::async_trait;
use bytes::Buf;
use http::HeaderMap;
use xmltree::Element;
/// Response of
/// [get_bucket_lifecycle()](crate::s3::client::Client::get_bucket_lifecycle)
/// API
#[derive(Clone, Debug)]
pub struct GetBucketLifecycleResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
pub config: LifecycleConfig,
}
#[async_trait]
impl FromS3Response for GetBucketLifecycleResponse {
async fn from_s3response<'a>(
req: S3Request<'a>,
resp: reqwest::Response,
) -> Result<Self, Error> {
let bucket: String = match req.bucket {
None => return Err(Error::InvalidBucketName("no bucket specified".to_string())),
Some(v) => v.to_string(),
};
let headers = resp.headers().clone();
let body = resp.bytes().await?;
let root = Element::parse(body.reader())?;
let config = LifecycleConfig::from_xml(&root)?;
Ok(GetBucketLifecycleResponse {
headers,
region: req.get_computed_region(),
bucket,
config,
})
}
}

View File

@ -24,6 +24,7 @@ use xmltree::Element;
/// Response of
/// [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption)
/// API
/// TODO equal to GetBucketEncryptionResponse
#[derive(Clone, Debug)]
pub struct SetBucketEncryptionResponse {
pub headers: HeaderMap,

View File

@ -0,0 +1,47 @@
// 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::error::Error;
use crate::s3::types::{FromS3Response, S3Request};
use async_trait::async_trait;
use http::HeaderMap;
/// Response of [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
#[derive(Debug)]
/// TODO is identical ot BucketResponse
pub struct SetBucketLifecycleResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
}
#[async_trait]
impl FromS3Response for SetBucketLifecycleResponse {
async fn from_s3response<'a>(
req: S3Request<'a>,
resp: reqwest::Response,
) -> Result<Self, Error> {
let bucket: String = match req.bucket {
None => return Err(Error::InvalidBucketName("no bucket specified".to_string())),
Some(v) => v.to_string(),
};
Ok(SetBucketLifecycleResponse {
headers: resp.headers().clone(),
region: req.get_computed_region(),
bucket,
})
}
}

View File

@ -14,12 +14,18 @@
// limitations under the License.
use crate::s3::error::Error;
use crate::s3::response::BucketResponse;
use crate::s3::types::{FromS3Response, S3Request};
use async_trait::async_trait;
use http::HeaderMap;
/// Response of [set_bucket_versioning()](Client::set_bucket_versioning) API
pub type SetBucketVersioningResponse = BucketResponse;
/// Response of [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API
#[derive(Debug)]
/// TODO is identical ot BucketResponse
pub struct SetBucketVersioningResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
}
#[async_trait]
impl FromS3Response for SetBucketVersioningResponse {
@ -35,7 +41,7 @@ impl FromS3Response for SetBucketVersioningResponse {
Ok(SetBucketVersioningResponse {
headers: resp.headers().clone(),
region: req.get_computed_region(),
bucket_name: bucket,
bucket,
})
}
}

View File

@ -1099,7 +1099,7 @@ impl LifecycleRule {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
/// Lifecycle configuration
pub struct LifecycleConfig {
pub rules: Vec<LifecycleRule>,