diff --git a/src/s3/args.rs b/src/s3/args.rs index fa78eb3..1815d7b 100644 --- a/src/s3/args.rs +++ b/src/s3/args.rs @@ -301,27 +301,6 @@ impl<'a> MakeBucketArgs<'a> { } } -#[derive(Clone, Debug, Default)] -/// Argument for [list_buckets()](crate::s3::client::Client::list_buckets) API -pub struct ListBucketsArgs<'a> { - pub extra_headers: Option<&'a Multimap>, - pub extra_query_params: Option<&'a Multimap>, -} - -impl<'a> ListBucketsArgs<'a> { - /// Returns argument for [list_buckets()](crate::s3::client::Client::list_buckets) API - /// - /// # Examples - /// - /// ``` - /// use minio::s3::args::*; - /// let args = ListBucketsArgs::new(); - /// ``` - pub fn new() -> ListBucketsArgs<'a> { - ListBucketsArgs::default() - } -} - #[derive(Clone, Debug, Default)] /// Argument for [abort_multipart_upload()](crate::s3::client::Client::abort_multipart_upload) API pub struct AbortMultipartUploadArgs<'a> { @@ -1767,9 +1746,6 @@ impl<'a> SetBucketTagsArgs<'a> { } } -/// Argument for [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API -pub type GetBucketVersioningArgs<'a> = BucketArgs<'a>; - /// Argument for [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API pub struct SetBucketVersioningArgs<'a> { pub extra_headers: Option<&'a Multimap>, diff --git a/src/s3/builders.rs b/src/s3/builders.rs index b4e9df1..e161987 100644 --- a/src/s3/builders.rs +++ b/src/s3/builders.rs @@ -12,8 +12,10 @@ //! Argument builders for [minio::s3::client::Client](crate::s3::client::Client) APIs +mod buckets; mod list_objects; mod listen_bucket_notification; +pub use buckets::*; pub use list_objects::*; pub use listen_bucket_notification::*; diff --git a/src/s3/builders/buckets.rs b/src/s3/builders/buckets.rs new file mode 100644 index 0000000..f4f2b10 --- /dev/null +++ b/src/s3/builders/buckets.rs @@ -0,0 +1,140 @@ +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, + + extra_headers: Option, + extra_query_params: Option, +} + +impl S3Api for ListBuckets { + type S3Response = ListBucketsResponse; +} + +impl ToS3Request for ListBuckets { + fn to_s3request(&self) -> Result { + 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) -> Self { + self.extra_headers = extra_headers; + self + } + + pub fn extra_query_params(mut self, extra_query_params: Option) -> Self { + self.extra_query_params = extra_query_params; + self + } +} + +#[derive(Clone, Debug, Default)] +pub struct BucketCommon { + client: Option, + + extra_headers: Option, + extra_query_params: Option, + region: Option, + 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) -> Self { + self.extra_headers = extra_headers; + self + } + + pub fn extra_query_params(mut self, extra_query_params: Option) -> Self { + self.extra_query_params = extra_query_params; + self + } + + pub fn region(mut self, region: Option) -> 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 { + 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) + } +} diff --git a/src/s3/client.rs b/src/s3/client.rs index febd930..7716f46 100644 --- a/src/s3/client.rs +++ b/src/s3/client.rs @@ -23,7 +23,7 @@ use crate::s3::response::*; use crate::s3::signer::{presign_v4, sign_v4_s3}; use crate::s3::sse::SseCustomerKey; use crate::s3::types::{ - Bucket, DeleteObject, Directive, LifecycleConfig, NotificationConfig, ObjectLockConfig, Part, + DeleteObject, Directive, LifecycleConfig, NotificationConfig, ObjectLockConfig, Part, ReplicationConfig, RetentionMode, SseConfig, }; use crate::s3::utils::{ @@ -46,6 +46,8 @@ use xmltree::Element; mod list_objects; mod listen_bucket_notification; +use super::builders::{GetBucketVersioning, ListBuckets}; + /// Client Builder manufactures a Client using given parameters. #[derive(Debug, Default)] pub struct ClientBuilder { @@ -1846,46 +1848,8 @@ impl Client { } } - pub async fn get_bucket_versioning( - &self, - args: &GetBucketVersioningArgs<'_>, - ) -> Result { - 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("versioning"), String::new()); - - let resp = self - .execute( - Method::GET, - ®ion, - &mut headers, - &query_params, - Some(args.bucket), - None, - None, - ) - .await?; - - let header_map = resp.headers().clone(); - let body = resp.bytes().await?; - let root = Element::parse(body.reader())?; - - Ok(GetBucketVersioningResponse { - headers: header_map.clone(), - region: region.clone(), - bucket_name: args.bucket.to_string(), - status: get_option_text(&root, "Status").map(|v| v == "Enabled"), - mfa_delete: get_option_text(&root, "MFADelete").map(|v| v == "Enabled"), - }) + pub fn get_bucket_versioning(&self, bucket: &str) -> GetBucketVersioning { + GetBucketVersioning::new(bucket).client(self) } pub async fn get_object(&self, args: &GetObjectArgs<'_>) -> Result { @@ -2230,49 +2194,8 @@ impl Client { } } - pub async fn list_buckets( - &self, - args: &ListBucketsArgs<'_>, - ) -> Result { - 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 { - query_params = v; - } - let resp = self - .execute( - Method::GET, - &String::from("us-east-1"), - &mut headers, - query_params, - None, - None, - None, - ) - .await?; - let header_map = resp.headers().clone(); - let body = resp.bytes().await?; - let mut root = Element::parse(body.reader())?; - let buckets = root - .get_mut_child("Buckets") - .ok_or(Error::XmlError(String::from(" tag not found")))?; - - let mut bucket_list: Vec = Vec::new(); - while let Some(b) = buckets.take_child("Bucket") { - let bucket = b; - bucket_list.push(Bucket { - name: get_text(&bucket, "Name")?, - creation_date: from_iso8601utc(&get_text(&bucket, "CreationDate")?)?, - }) - } - - Ok(ListBucketsResponse { - headers: header_map.clone(), - buckets: bucket_list, - }) + pub fn list_buckets(&self) -> ListBuckets { + ListBuckets::new().client(self) } pub async fn make_bucket( diff --git a/src/s3/response.rs b/src/s3/response.rs index e387eba..4b00602 100644 --- a/src/s3/response.rs +++ b/src/s3/response.rs @@ -24,28 +24,23 @@ use xmltree::Element; use crate::s3::error::Error; use crate::s3::types::{ - parse_legal_hold, Bucket, LifecycleConfig, NotificationConfig, ObjectLockConfig, - ReplicationConfig, RetentionMode, SelectProgress, SseConfig, + parse_legal_hold, LifecycleConfig, NotificationConfig, ObjectLockConfig, ReplicationConfig, + RetentionMode, SelectProgress, SseConfig, }; use crate::s3::utils::{ copy_slice, crc32, from_http_header_value, from_iso8601utc, get_text, uint32, UtcTime, }; +mod buckets; mod list_objects; mod listen_bucket_notification; +pub use buckets::{GetBucketVersioningResponse, ListBucketsResponse}; pub use list_objects::{ ListObjectVersionsResponse, ListObjectsResponse, ListObjectsV1Response, ListObjectsV2Response, }; pub use listen_bucket_notification::ListenBucketNotificationResponse; -#[derive(Debug)] -/// Response of [list_buckets()](crate::s3::client::Client::list_buckets) API -pub struct ListBucketsResponse { - pub headers: HeaderMap, - pub buckets: Vec, -} - #[derive(Debug)] /// Base response for bucket operation pub struct BucketResponse { @@ -675,16 +670,6 @@ pub struct GetBucketTagsResponse { /// Response of [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API pub type SetBucketTagsResponse = BucketResponse; -#[derive(Clone, Debug)] -/// Response of [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API -pub struct GetBucketVersioningResponse { - pub headers: HeaderMap, - pub region: String, - pub bucket_name: String, - pub status: Option, - pub mfa_delete: Option, -} - /// Response of [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API pub type SetBucketVersioningResponse = BucketResponse; diff --git a/src/s3/response/buckets.rs b/src/s3/response/buckets.rs new file mode 100644 index 0000000..9745f76 --- /dev/null +++ b/src/s3/response/buckets.rs @@ -0,0 +1,78 @@ +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 { + pub headers: HeaderMap, + pub buckets: Vec, +} + +#[async_trait] +impl FromS3Response for ListBucketsResponse { + async fn from_s3response<'a>( + _req: S3Request<'a>, + resp: reqwest::Response, + ) -> Result { + let header_map = resp.headers().clone(); + let body = resp.bytes().await?; + let mut root = Element::parse(body.reader())?; + let buckets = root + .get_mut_child("Buckets") + .ok_or(Error::XmlError(String::from(" tag not found")))?; + + let mut bucket_list: Vec = Vec::new(); + while let Some(b) = buckets.take_child("Bucket") { + let bucket = b; + bucket_list.push(Bucket { + name: get_text(&bucket, "Name")?, + creation_date: from_iso8601utc(&get_text(&bucket, "CreationDate")?)?, + }) + } + + Ok(ListBucketsResponse { + headers: header_map.clone(), + buckets: bucket_list, + }) + } +} + +/// 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, + pub mfa_delete: Option, +} + +#[async_trait] +impl FromS3Response for GetBucketVersioningResponse { + async fn from_s3response<'a>( + req: S3Request<'a>, + resp: reqwest::Response, + ) -> Result { + 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"), + }) + } +} diff --git a/tests/tests.rs b/tests/tests.rs index 5c9b20d..99af889 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -163,11 +163,7 @@ impl ClientTest { } let mut count = 0; - let resp = self - .client - .list_buckets(&ListBucketsArgs::new()) - .await - .unwrap(); + let resp = self.client.list_buckets().send().await.unwrap(); for bucket in resp.buckets.iter() { if names.contains(&bucket.name) { count += 1; @@ -1016,7 +1012,8 @@ impl ClientTest { let resp = self .client - .get_bucket_versioning(&GetBucketVersioningArgs::new(&bucket_name).unwrap()) + .get_bucket_versioning(&bucket_name) + .send() .await .unwrap(); assert!(match resp.status { @@ -1031,7 +1028,8 @@ impl ClientTest { let resp = self .client - .get_bucket_versioning(&GetBucketVersioningArgs::new(&bucket_name).unwrap()) + .get_bucket_versioning(&bucket_name) + .send() .await .unwrap(); assert!(