Refactor get bucket encryption (#110)

This commit is contained in:
Henk-Jan Lebbink 2025-02-01 01:38:50 +01:00 committed by GitHub
parent 26d67b80df
commit e98264fa3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 626 additions and 351 deletions

View File

@ -20,12 +20,11 @@ jobs:
- name: Build
run: |
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -A clippy::result_large_err -A clippy::type_complexity -A clippy::too_many_arguments
cargo clippy --all-targets --all-features
cargo build --bins --examples --tests --benches --verbose
- name: Run tests
run: |
truncate --size=6M asiaphotos-2015.zip
./tests/start-server.sh
export SERVER_ENDPOINT=localhost:9000
export ACCESS_KEY=minioadmin

48
examples/common.rs Normal file
View File

@ -0,0 +1,48 @@
use minio::s3::args::{BucketExistsArgs, MakeBucketArgs};
use minio::s3::creds::StaticProvider;
use minio::s3::http::BaseUrl;
use minio::s3::{Client, ClientBuilder};
#[allow(dead_code)]
pub fn create_client_on_play() -> Result<Client, Box<dyn std::error::Error + Send + Sync>> {
let base_url = "https://play.min.io".parse::<BaseUrl>()?;
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
let static_provider = StaticProvider::new(
"Q3AM3UQ867SPQQA43P2F",
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
None,
);
let client = ClientBuilder::new(base_url.clone())
.provider(Some(Box::new(static_provider)))
.build()?;
Ok(client)
}
pub async fn create_bucket_if_not_exists(
bucket_name: &str,
client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Check 'bucket_name' bucket exist or not.
let exists: bool = client
.bucket_exists(&BucketExistsArgs::new(bucket_name).unwrap())
.await
.unwrap();
// Make 'bucket_name' bucket if not exist.
if !exists {
client
.make_bucket(&MakeBucketArgs::new(bucket_name).unwrap())
.await
.unwrap();
};
Ok(())
}
#[allow(dead_code)]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// dummy code just to prevent an error because files in examples need to have a main
Ok(())
}

View File

@ -1,27 +1,30 @@
use minio::s3::args::{BucketExistsArgs, MakeBucketArgs};
// 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::builders::ObjectContent;
use minio::s3::client::ClientBuilder;
use minio::s3::creds::StaticProvider;
use minio::s3::http::BaseUrl;
use minio::s3::types::S3Api;
use minio::s3::Client;
use std::path::Path;
#[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 base_url = "https://play.min.io".parse::<BaseUrl>()?;
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
let static_provider = StaticProvider::new(
"Q3AM3UQ867SPQQA43P2F",
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
None,
);
let client = ClientBuilder::new(base_url.clone())
.provider(Some(Box::new(static_provider)))
.build()?;
let client: Client = create_client_on_play()?;
let bucket_name: &str = "file-download-rust-bucket";
let object_name: &str = "cat.png";
@ -30,19 +33,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename: &Path = Path::new("./examples/cat.png");
let download_path: &str = &format!("/tmp/downloads/{object_name}");
// Check 'bucket_name' bucket exist or not.
let exists: bool = client
.bucket_exists(&BucketExistsArgs::new(bucket_name).unwrap())
.await
.unwrap();
// Make 'bucket_name' bucket if not exist.
if !exists {
client
.make_bucket(&MakeBucketArgs::new(bucket_name).unwrap())
.await
.unwrap();
}
create_bucket_if_not_exists(bucket_name, &client).await?;
if filename.exists() {
log::info!("File '{}' exists.", &filename.to_str().unwrap());
@ -64,10 +55,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let get_object = client.get_object(bucket_name, object_name).send().await?;
get_object
.content
.to_file(&Path::new(download_path))
.await?;
get_object.content.to_file(Path::new(download_path)).await?;
log::info!("Object '{object_name}' is successfully downloaded to file '{download_path}'.");

View File

@ -12,46 +12,20 @@
// 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 minio::s3::args::{BucketExistsArgs, MakeBucketArgs};
use crate::common::{create_bucket_if_not_exists, create_client_on_play};
use minio::s3::builders::ObjectContent;
use minio::s3::client::ClientBuilder;
use minio::s3::creds::StaticProvider;
use minio::s3::http::BaseUrl;
use minio::s3::Client;
use std::path::Path;
#[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 base_url = "https://play.min.io".parse::<BaseUrl>()?;
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
let static_provider = StaticProvider::new(
"Q3AM3UQ867SPQQA43P2F",
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
None,
);
let client = ClientBuilder::new(base_url.clone())
.provider(Some(Box::new(static_provider)))
.build()?;
let client: Client = create_client_on_play()?;
let bucket_name: &str = "file-upload-rust-bucket";
// Check 'bucket_name' bucket exist or not.
let exists: bool = client
.bucket_exists(&BucketExistsArgs::new(bucket_name).unwrap())
.await
.unwrap();
// Make 'bucket_name' bucket if not exist.
if !exists {
client
.make_bucket(&MakeBucketArgs::new(bucket_name).unwrap())
.await
.unwrap();
}
create_bucket_if_not_exists(bucket_name, &client).await?;
// File we are going to upload to the bucket
let filename: &Path = Path::new("./examples/cat.png");

View File

@ -0,0 +1,35 @@
// 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::{create_bucket_if_not_exists, create_client_on_play};
use minio::s3::builders::GetBucketEncryption;
use minio::s3::Client;
mod common;
#[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 = "encryption-rust-bucket";
create_bucket_if_not_exists(bucket_name, &client).await?;
let be: GetBucketEncryption = client.get_bucket_encryption(bucket_name);
log::info!("{:?}", be);
Ok(())
}

View File

@ -0,0 +1,35 @@
// 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::builders::GetBucketVersioning;
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 = "versioning-rust-bucket";
create_bucket_if_not_exists(bucket_name, &client).await?;
let bv: GetBucketVersioning = client.get_bucket_versioning(bucket_name);
log::info!("{:?}", bv);
Ok(())
}

View File

@ -13,7 +13,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use minio::s3::args::{BucketExistsArgs, MakeBucketArgs};
mod common;
use crate::common::create_bucket_if_not_exists;
use minio::s3::builders::{ObjectContent, ObjectPrompt};
use minio::s3::client::ClientBuilder;
use minio::s3::creds::StaticProvider;
@ -25,14 +27,11 @@ use std::path::Path;
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 base_url = "https://play.min.io".parse::<BaseUrl>()?;
//Note: object prompt is not supported on play.min.io, you will need point to AIStor
let base_url = "http://localhost:9000".parse::<BaseUrl>()?;
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
let static_provider = StaticProvider::new(
"Q3AM3UQ867SPQQA43P2F",
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
None,
);
let static_provider = StaticProvider::new("admin", "admin", None);
let client = ClientBuilder::new(base_url.clone())
.provider(Some(Box::new(static_provider)))
@ -40,20 +39,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
.build()?;
let bucket_name: &str = "object-prompt-rust-bucket";
// Check 'bucket_name' bucket exist or not.
let exists: bool = client
.bucket_exists(&BucketExistsArgs::new(bucket_name).unwrap())
.await
.unwrap();
// Make 'bucket_name' bucket if not exist.
if !exists {
client
.make_bucket(&MakeBucketArgs::new(bucket_name).unwrap())
.await
.unwrap();
}
create_bucket_if_not_exists(bucket_name, &client).await?;
// File we are going to upload to the bucket
let filename: &Path = Path::new("./examples/cat.png");

View File

@ -1318,9 +1318,6 @@ impl<'a> ComposeObjectArgs<'a> {
/// Argument for [delete_bucket_encryption()](crate::s3::client::Client::delete_bucket_encryption) API
pub type DeleteBucketEncryptionArgs<'a> = BucketArgs<'a>;
/// Argument for [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
pub type GetBucketEncryptionArgs<'a> = BucketArgs<'a>;
#[derive(Clone, Debug)]
/// Argument for [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
pub struct SetBucketEncryptionArgs<'a> {

View File

@ -15,8 +15,11 @@
//! Argument builders for [minio::s3::client::Client](crate::s3::client::Client) APIs
mod buckets;
mod bucket_common;
mod get_bucket_encryption;
mod get_bucket_versioning;
mod get_object;
mod list_buckets;
mod list_objects;
mod listen_bucket_notification;
mod object_content;
@ -24,8 +27,11 @@ mod object_prompt;
mod put_object;
mod remove_objects;
pub use buckets::*;
pub use bucket_common::*;
pub use get_bucket_encryption::*;
pub use get_bucket_versioning::*;
pub use get_object::*;
pub use list_buckets::*;
pub use list_objects::*;
pub use listen_bucket_notification::*;
pub use object_content::*;

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.
use std::marker::PhantomData;
use crate::s3::{client::Client, utils::Multimap};
#[derive(Clone, Debug, Default)]
pub struct BucketCommon<A> {
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,
_operation: PhantomData<A>,
}
impl<A: Default> BucketCommon<A> {
pub fn new(bucket: &str) -> BucketCommon<A> {
BucketCommon {
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
}
}

View File

@ -1,140 +0,0 @@
use http::Method;
use crate::s3::{
client::Client,
error::Error,
response::{GetBucketVersioningResponse, ListBucketsResponse},
types::{S3Api, S3Request, ToS3Request},
utils::{check_bucket_name, merge, Multimap},
};
/// Argument builder for
/// [list_buckets()](crate::s3::client::Client::list_buckets) API.
#[derive(Clone, Debug, Default)]
pub struct ListBuckets {
client: Option<Client>,
extra_headers: Option<Multimap>,
extra_query_params: Option<Multimap>,
}
impl S3Api for ListBuckets {
type S3Response = ListBucketsResponse;
}
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 ListBuckets {
pub fn new() -> Self {
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
}
}
#[derive(Clone, Debug, Default)]
pub struct BucketCommon {
client: Option<Client>,
extra_headers: Option<Multimap>,
extra_query_params: Option<Multimap>,
region: Option<String>,
bucket: String,
}
impl BucketCommon {
pub fn new(bucket_name: &str) -> Self {
BucketCommon {
bucket: bucket_name.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
}
}
/// Argument builder for
/// [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning)
/// API
pub type GetBucketVersioning = BucketCommon;
impl S3Api for GetBucketVersioning {
type S3Response = GetBucketVersioningResponse;
}
impl ToS3Request for GetBucketVersioning {
fn to_s3request(&self) -> Result<S3Request, Error> {
check_bucket_name(&self.bucket, true)?;
let mut headers = Multimap::new();
if let Some(v) = &self.extra_headers {
merge(&mut headers, v);
}
let mut query_params = Multimap::new();
if let Some(v) = &self.extra_query_params {
merge(&mut query_params, v);
}
query_params.insert(String::from("versioning"), String::new());
let req = S3Request::new(
self.client.as_ref().ok_or(Error::NoClientProvided)?,
Method::GET,
)
.region(self.region.as_deref())
.bucket(Some(&self.bucket))
.query_params(query_params)
.headers(headers);
Ok(req)
}
}

View File

@ -0,0 +1,57 @@
// 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::GetBucketEncryptionResponse;
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
pub type GetBucketEncryption = BucketCommon<GetBucketEncryptionPhantomData>;
#[derive(Default, Debug)]
pub struct GetBucketEncryptionPhantomData;
impl S3Api for GetBucketEncryption {
type S3Response = GetBucketEncryptionResponse;
}
impl ToS3Request for GetBucketEncryption {
fn to_s3request(&self) -> Result<S3Request, Error> {
check_bucket_name(&self.bucket, true)?;
let mut headers = Multimap::new();
if let Some(v) = &self.extra_headers {
merge(&mut headers, v);
}
let mut query_params = Multimap::new();
if let Some(v) = &self.extra_query_params {
merge(&mut query_params, v);
}
query_params.insert(String::from("encryption"), String::new());
let req = S3Request::new(
self.client.as_ref().ok_or(Error::NoClientProvided)?,
Method::GET,
)
.region(self.region.as_deref())
.bucket(Some(&self.bucket))
.query_params(query_params)
.headers(headers);
Ok(req)
}
}

View File

@ -0,0 +1,57 @@
// 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::GetBucketVersioningResponse;
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
pub type GetBucketVersioning = BucketCommon<GetBucketVersioningPhantomData>;
#[derive(Default, Debug)]
pub struct GetBucketVersioningPhantomData;
impl S3Api for GetBucketVersioning {
type S3Response = GetBucketVersioningResponse;
}
impl ToS3Request for GetBucketVersioning {
fn to_s3request(&self) -> Result<S3Request, Error> {
check_bucket_name(&self.bucket, true)?;
let mut headers = Multimap::new();
if let Some(v) = &self.extra_headers {
merge(&mut headers, v);
}
let mut query_params = Multimap::new();
if let Some(v) = &self.extra_query_params {
merge(&mut query_params, v);
}
query_params.insert(String::from("versioning"), String::new());
let req = S3Request::new(
self.client.as_ref().ok_or(Error::NoClientProvided)?,
Method::GET,
)
.region(self.region.as_deref())
.bucket(Some(&self.bucket))
.query_params(query_params)
.headers(headers);
Ok(req)
}
}

View File

@ -24,6 +24,7 @@ use crate::s3::{
utils::{check_bucket_name, merge, to_http_header_value, Multimap, UtcTime},
};
/// Argument builder for [list_objects()](Client::get_object) API.
#[derive(Debug, Clone, Default)]
pub struct GetObject {
client: Option<Client>,

View File

@ -0,0 +1,80 @@
// 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 http::Method;
use crate::s3::response::ListBucketsResponse;
use crate::s3::{
error::Error,
types::{S3Api, S3Request, ToS3Request},
utils::Multimap,
Client,
};
/// Argument builder for [list_buckets()](Client::list_buckets) API.
#[derive(Clone, Debug, Default)]
pub struct ListBuckets {
client: Option<Client>,
extra_headers: Option<Multimap>,
extra_query_params: Option<Multimap>,
}
// builder interface
impl ListBuckets {
pub fn new() -> Self {
Default::default()
}
pub fn client(mut self, client: &Client) -> Self {
self.client = Some(client.clone());
self
}
pub fn 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 ToS3Request for ListBuckets {
fn to_s3request(&self) -> Result<S3Request, Error> {
let mut headers = Multimap::new();
if let Some(v) = &self.extra_headers {
headers = v.clone();
}
let mut query_params = Multimap::new();
if let Some(v) = &self.extra_query_params {
query_params = v.clone();
}
let req = S3Request::new(
self.client.as_ref().ok_or(Error::NoClientProvided)?,
Method::GET,
)
.query_params(query_params)
.headers(headers);
Ok(req)
}
}
impl S3Api for ListBuckets {
type S3Response = ListBucketsResponse;
}

View File

@ -200,10 +200,11 @@ impl ObjectContent {
.to_path_buf()
.join(Path::new(tmp_file_name.as_os_str()));
let mut total = 0;
let mut total_bytes_written = 0;
let mut fp = fs::OpenOptions::new()
.write(true)
.create(true)
.create(true) // Ensures that the file will be created if it does not already exist
.truncate(true) // Clears the contents (truncates the file size to 0) before writing
.open(&tmp_file_path)
.await?;
let (mut r, _) = self.to_stream().await?;
@ -212,12 +213,12 @@ impl ObjectContent {
if bytes.is_empty() {
break;
}
total += bytes.len() as u64;
total_bytes_written += bytes.len() as u64;
fp.write_all(&bytes).await?;
}
fp.flush().await?;
fs::rename(&tmp_file_path, file_path).await?;
Ok(total)
Ok(total_bytes_written)
}
}

View File

@ -30,7 +30,7 @@ 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, SseConfig,
RetentionMode,
};
use crate::s3::utils::{
from_iso8601utc, get_default_text, get_option_text, get_text, md5sum_hash, md5sum_hash_sb,
@ -47,6 +47,8 @@ use tokio::fs;
use xmltree::Element;
mod get_bucket_encryption;
mod get_bucket_versioning;
mod get_object;
mod list_objects;
mod listen_bucket_notification;
@ -54,7 +56,7 @@ mod object_prompt;
mod put_object;
mod remove_objects;
use super::builders::{GetBucketVersioning, ListBuckets, SegmentedBytes};
use super::builders::{ListBuckets, SegmentedBytes};
/// Client Builder manufactures a Client using given parameters.
#[derive(Debug, Default)]
@ -1602,58 +1604,6 @@ impl Client {
})
}
pub async fn get_bucket_encryption(
&self,
args: &GetBucketEncryptionArgs<'_>,
) -> Result<GetBucketEncryptionResponse, 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("encryption"), String::new());
let resp = self
.execute(
Method::GET,
&region,
&mut headers,
&query_params,
Some(args.bucket),
None,
None,
)
.await?;
let header_map = resp.headers().clone();
let body = resp.bytes().await?;
let mut root = Element::parse(body.reader())?;
let rule = root
.get_mut_child("Rule")
.ok_or(Error::XmlError(String::from("<Rule> tag not found")))?;
let sse_by_default = rule
.get_mut_child("ApplyServerSideEncryptionByDefault")
.ok_or(Error::XmlError(String::from(
"<ApplyServerSideEncryptionByDefault> tag not found",
)))?;
Ok(GetBucketEncryptionResponse {
headers: header_map.clone(),
region: region.clone(),
bucket_name: args.bucket.to_string(),
config: SseConfig {
sse_algorithm: get_text(sse_by_default, "SSEAlgorithm")?,
kms_master_key_id: get_option_text(sse_by_default, "KMSMasterKeyID"),
},
})
}
pub async fn get_bucket_lifecycle(
&self,
args: &GetBucketLifecycleArgs<'_>,
@ -1912,10 +1862,6 @@ impl Client {
}
}
pub fn get_bucket_versioning(&self, bucket: &str) -> GetBucketVersioning {
GetBucketVersioning::new(bucket).client(self)
}
pub async fn get_object_old(
&self,
args: &GetObjectArgs<'_>,

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::GetBucketEncryption;
impl Client {
/// Create a GetBucketEncryption request builder.
pub fn get_bucket_encryption(&self, bucket: &str) -> GetBucketEncryption {
GetBucketEncryption::new(bucket).client(self)
}
}

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::GetBucketVersioning;
impl Client {
/// Create a GetBucketVersioning request builder.
pub fn get_bucket_versioning(&self, bucket: &str) -> GetBucketVersioning {
GetBucketVersioning::new(bucket).client(self)
}
}

View File

@ -25,22 +25,26 @@ use xmltree::Element;
use crate::s3::error::Error;
use crate::s3::types::{
parse_legal_hold, LifecycleConfig, NotificationConfig, ObjectLockConfig, ReplicationConfig,
RetentionMode, SelectProgress, SseConfig,
RetentionMode, SelectProgress,
};
use crate::s3::utils::{
copy_slice, crc32, from_http_header_value, from_iso8601utc, get_text, uint32, UtcTime,
};
mod buckets;
mod get_bucket_encryption;
mod get_bucket_versioning;
mod get_object;
mod list_buckets;
pub(crate) mod list_objects;
mod listen_bucket_notification;
mod object_prompt;
mod put_object;
mod remove_objects;
pub use buckets::{GetBucketVersioningResponse, ListBucketsResponse};
pub use get_bucket_encryption::GetBucketEncryptionResponse;
pub use get_bucket_versioning::GetBucketVersioningResponse;
pub use get_object::GetObjectResponse;
pub use list_buckets::ListBucketsResponse;
pub use list_objects::ListObjectsResponse;
pub use listen_bucket_notification::ListenBucketNotificationResponse;
pub use object_prompt::ObjectPromptResponse;
@ -550,15 +554,6 @@ impl SelectObjectContentResponse {
/// Response of [delete_bucket_encryption()](crate::s3::client::Client::delete_bucket_encryption) API
pub type DeleteBucketEncryptionResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
pub struct GetBucketEncryptionResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket_name: String,
pub config: SseConfig,
}
/// Response of [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
pub type SetBucketEncryptionResponse = BucketResponse;

View File

@ -0,0 +1,65 @@
// 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, SseConfig};
use crate::s3::utils::{get_option_text, get_text};
use async_trait::async_trait;
use bytes::Buf;
use http::HeaderMap;
use xmltree::Element;
/// Response of
/// [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption)
/// API
#[derive(Clone, Debug)]
pub struct GetBucketEncryptionResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
pub config: SseConfig,
}
#[async_trait]
impl FromS3Response for GetBucketEncryptionResponse {
async fn from_s3response<'a>(
req: S3Request<'a>,
resp: reqwest::Response,
) -> Result<Self, Error> {
let headers = resp.headers().clone();
let body = resp.bytes().await?;
let mut root = Element::parse(body.reader())?;
let rule = root
.get_mut_child("Rule")
.ok_or(Error::XmlError(String::from("<Rule> tag not found")))?;
let sse_by_default = rule
.get_mut_child("ApplyServerSideEncryptionByDefault")
.ok_or(Error::XmlError(String::from(
"<ApplyServerSideEncryptionByDefault> tag not found",
)))?;
Ok(GetBucketEncryptionResponse {
headers,
region: req.get_computed_region(),
bucket: req.bucket.unwrap().to_string(), // TODO remove unwrap
config: SseConfig {
sse_algorithm: get_text(sse_by_default, "SSEAlgorithm")?,
kms_master_key_id: get_option_text(sse_by_default, "KMSMasterKeyID"),
},
})
}
}

View File

@ -0,0 +1,54 @@
// 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 crate::s3::utils::get_option_text;
use async_trait::async_trait;
use bytes::Buf;
use http::HeaderMap;
use xmltree::Element;
/// Response of
/// [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning)
/// API
#[derive(Clone, Debug)]
pub struct GetBucketVersioningResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
pub status: Option<bool>,
pub mfa_delete: Option<bool>,
}
#[async_trait]
impl FromS3Response for GetBucketVersioningResponse {
async fn from_s3response<'a>(
req: S3Request<'a>,
resp: reqwest::Response,
) -> Result<Self, Error> {
let headers = resp.headers().clone();
let body = resp.bytes().await?;
let root = Element::parse(body.reader())?;
Ok(GetBucketVersioningResponse {
headers,
region: req.get_computed_region(),
bucket: req.bucket.unwrap().to_string(), // TODO remove unwrap
status: get_option_text(&root, "Status").map(|v| v == "Enabled"),
mfa_delete: get_option_text(&root, "MFADelete").map(|v| v == "Enabled"),
})
}
}

View File

@ -1,14 +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.
use crate::s3::error::Error;
use crate::s3::types::{Bucket, FromS3Response, S3Request};
use crate::s3::utils::{from_iso8601utc, get_text};
use async_trait::async_trait;
use bytes::Buf;
use http::HeaderMap;
use xmltree::Element;
use crate::s3::{
error::Error,
types::{Bucket, FromS3Response, S3Request},
utils::{from_iso8601utc, get_option_text, get_text},
};
/// Response of [list_buckets()](crate::s3::client::Client::list_buckets) API
#[derive(Debug, Clone)]
pub struct ListBucketsResponse {
@ -44,35 +56,3 @@ impl FromS3Response for ListBucketsResponse {
})
}
}
/// Response of
/// [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning)
/// API
#[derive(Clone, Debug)]
pub struct GetBucketVersioningResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket: String,
pub status: Option<bool>,
pub mfa_delete: Option<bool>,
}
#[async_trait]
impl FromS3Response for GetBucketVersioningResponse {
async fn from_s3response<'a>(
req: S3Request<'a>,
resp: reqwest::Response,
) -> Result<Self, Error> {
let headers = resp.headers().clone();
let body = resp.bytes().await?;
let root = Element::parse(body.reader())?;
Ok(GetBucketVersioningResponse {
headers,
region: req.get_computed_region(),
bucket: req.bucket.unwrap().to_string(),
status: get_option_text(&root, "Status").map(|v| v == "Enabled"),
mfa_delete: get_option_text(&root, "MFADelete").map(|v| v == "Enabled"),
})
}
}

View File

@ -24,6 +24,7 @@ use crate::s3::{
utils::get_text,
};
/// Response of [put_object_api()](crate::s3::client::Client::put_object) API
#[derive(Debug, Clone)]
pub struct PutObjectResponse {
pub headers: HeaderMap,

View File

@ -39,13 +39,12 @@ use minio::s3::client::Client;
use minio::s3::creds::StaticProvider;
use minio::s3::error::Error;
use minio::s3::http::BaseUrl;
use minio::s3::types::ToStream;
use minio::s3::response::GetBucketVersioningResponse;
use minio::s3::types::{
CsvInputSerialization, CsvOutputSerialization, FileHeaderInfo, NotificationConfig,
ObjectLockConfig, PrefixFilterRule, QueueConfig, QuoteFields, RetentionMode, SelectRequest,
SuffixFilterRule,
NotificationRecords, ObjectLockConfig, PrefixFilterRule, QueueConfig, QuoteFields,
RetentionMode, S3Api, SelectRequest, SuffixFilterRule, ToStream,
};
use minio::s3::types::{NotificationRecords, S3Api};
use minio::s3::utils::{to_iso8601utc, utc_now};
struct RandReader {
@ -1263,7 +1262,7 @@ impl ClientTest {
.await
.unwrap();
let resp = self
let resp: GetBucketVersioningResponse = self
.client
.get_bucket_versioning(&bucket_name)
.send()
@ -1276,7 +1275,7 @@ impl ClientTest {
.await
.unwrap();
let resp = self
let resp: GetBucketVersioningResponse = self
.client
.get_bucket_versioning(&bucket_name)
.send()