diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 71c47c1..b39fb7d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,6 +24,7 @@ jobs: - name: Run tests run: | + truncate --size=6M asiaphotos-2015.zip ./tests/start-server.sh export SERVER_ENDPOINT=localhost:9000 export ACCESS_KEY=minioadmin diff --git a/src/s3/args.rs b/src/s3/args.rs index 90af6fd..12ea28e 100644 --- a/src/s3/args.rs +++ b/src/s3/args.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Arguments for [minio::s3::client::Client](crate::s3::client::Client) APIs + use crate::s3::error::Error; use crate::s3::signer::post_presign_v4; use crate::s3::sse::{Sse, SseCustomerKey}; @@ -150,6 +152,7 @@ fn calc_part_info( } #[derive(Clone, Debug, Default)] +/// Base bucket argument pub struct BucketArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -158,6 +161,14 @@ pub struct BucketArgs<'a> { } impl<'a> BucketArgs<'a> { + /// Returns a bucket argument with given bucket name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = BucketArgs::new("my-bucket").unwrap(); + /// ``` pub fn new(bucket_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -170,11 +181,14 @@ impl<'a> BucketArgs<'a> { } } +/// Argument for [bucket_exists()](crate::s3::client::Client::bucket_exists) API pub type BucketExistsArgs<'a> = BucketArgs<'a>; +/// Argument for [remove_bucket()](crate::s3::client::Client::remove_bucket) API pub type RemoveBucketArgs<'a> = BucketArgs<'a>; #[derive(Clone, Debug, Default)] +/// Base object argument pub struct ObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -184,6 +198,14 @@ pub struct ObjectArgs<'a> { } impl<'a> ObjectArgs<'a> { + /// Returns a object argument with given bucket name and object name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ObjectArgs::new("my-bucket", "my-object").unwrap(); + /// ``` pub fn new(bucket_name: &'a str, object_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -204,6 +226,7 @@ impl<'a> ObjectArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Base object argument with optional version ID pub struct ObjectVersionArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -214,6 +237,15 @@ pub struct ObjectVersionArgs<'a> { } impl<'a> ObjectVersionArgs<'a> { + /// Returns a object argument with given bucket name and object name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let mut args = ObjectVersionArgs::new("my-bucket", "my-object").unwrap(); + /// args.version_id = Some("ef090b89-cfbe-4a04-aa90-03c09110ba23"); + /// ``` pub fn new(bucket_name: &'a str, object_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -234,9 +266,11 @@ impl<'a> ObjectVersionArgs<'a> { } } +/// Argument for [remove_object()](crate::s3::client::Client::remove_object) API pub type RemoveObjectArgs<'a> = ObjectVersionArgs<'a>; #[derive(Clone, Debug, Default)] +/// Argument for [make_bucket()](crate::s3::client::Client::make_bucket) API pub struct MakeBucketArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -246,6 +280,14 @@ pub struct MakeBucketArgs<'a> { } impl<'a> MakeBucketArgs<'a> { + /// Returns argument for [make_bucket()](crate::s3::client::Client::make_bucket) API with given bucket name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = MakeBucketArgs::new("my-bucket").unwrap(); + /// ``` pub fn new(bucket_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -260,18 +302,28 @@ 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> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -282,6 +334,18 @@ pub struct AbortMultipartUploadArgs<'a> { } impl<'a> AbortMultipartUploadArgs<'a> { + /// Returns argument for [abort_multipart_upload()](crate::s3::client::Client::abort_multipart_upload) API with given bucket name, object name and upload ID + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = AbortMultipartUploadArgs::new( + /// "my-bucket", + /// "my-object", + /// "c53a2b73-f5e6-484a-9bc0-09cce13e8fd0", + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -313,6 +377,7 @@ impl<'a> AbortMultipartUploadArgs<'a> { } #[derive(Clone, Debug)] +/// Argument for [complete_multipart_upload()](crate::s3::client::Client::complete_multipart_upload) API pub struct CompleteMultipartUploadArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -324,6 +389,23 @@ pub struct CompleteMultipartUploadArgs<'a> { } impl<'a> CompleteMultipartUploadArgs<'a> { + /// Returns argument for [complete_multipart_upload()](crate::s3::client::Client::complete_multipart_upload) API with given bucket name, object name, upload ID and parts information + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::Part; + /// let mut parts: Vec = Vec::new(); + /// parts.push(Part {number: 1, etag: String::from("0b2daaba1d0b52a15a98c7ab6927347a")}); + /// parts.push(Part {number: 2, etag: String::from("acc0485d88ec53f47b599e4e8998706d")}); + /// let args = CompleteMultipartUploadArgs::new( + /// "my-bucket", + /// "my-object", + /// "c53a2b73-f5e6-484a-9bc0-09cce13e8fd0", + /// &parts, + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -361,6 +443,7 @@ impl<'a> CompleteMultipartUploadArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Argument for [create_multipart_upload()](crate::s3::client::Client::create_multipart_upload) API pub struct CreateMultipartUploadArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -371,6 +454,14 @@ pub struct CreateMultipartUploadArgs<'a> { } impl<'a> CreateMultipartUploadArgs<'a> { + /// Returns argument for [create_multipart_upload()](crate::s3::client::Client::create_multipart_upload) API with given bucket name and object name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = CreateMultipartUploadArgs::new("my-bucket", "my-object").unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -395,6 +486,7 @@ impl<'a> CreateMultipartUploadArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Argument for [put_object_api()](crate::s3::client::Client::put_object_api) S3 API pub struct PutObjectApiArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -412,6 +504,15 @@ pub struct PutObjectApiArgs<'a> { } impl<'a> PutObjectApiArgs<'a> { + /// Returns argument for [put_object_api()](crate::s3::client::Client::put_object_api) S3 API with given bucket name, object name and data + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let data: &[u8] = &[65, 67, 69]; + /// let args = PutObjectApiArgs::new("my-bucket", "my-object", data).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -456,6 +557,7 @@ impl<'a> PutObjectApiArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Argument for [upload_part()](crate::s3::client::Client::upload_part) S3 API pub struct UploadPartArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -474,6 +576,21 @@ pub struct UploadPartArgs<'a> { } impl<'a> UploadPartArgs<'a> { + /// Returns argument for [upload_part()](crate::s3::client::Client::upload_part) API with given bucket name, object name, part number and data + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let data: &[u8] = &[65, 67, 69]; + /// let args = UploadPartArgs::new( + /// "my-bucket", + /// "my-object", + /// "c53a2b73-f5e6-484a-9bc0-09cce13e8fd0", + /// 3, + /// data, + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -532,6 +649,7 @@ impl<'a> UploadPartArgs<'a> { } } +/// Argument for [put_object()](crate::s3::client::Client::put_object) API pub struct PutObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -552,6 +670,22 @@ pub struct PutObjectArgs<'a> { } impl<'a> PutObjectArgs<'a> { + /// Returns argument for [put_object()](crate::s3::client::Client::put_object) API with given bucket name, object name, stream, optional object size and optional part size + /// + /// * If stream size is known and wanted to create object with entire stream data, pass stream size as object size. + /// * If part size is omitted, this API calculates optimal part size for given object size. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use std::fs::File; + /// let filename = "asiaphotos-2015.zip"; + /// let meta = std::fs::metadata(filename).unwrap(); + /// let object_size = Some(meta.len() as usize); + /// let mut file = File::open(filename).unwrap(); + /// let args = PutObjectArgs::new("my-bucket", "my-object", &mut file, object_size, None).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -603,6 +737,7 @@ impl<'a> PutObjectArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Base argument for object conditional read APIs pub struct ObjectConditionalReadArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -620,6 +755,14 @@ pub struct ObjectConditionalReadArgs<'a> { } impl<'a> ObjectConditionalReadArgs<'a> { + /// Returns a object conditional read argument with given bucket name and object name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ObjectConditionalReadArgs::new("my-bucket", "my-object").unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -750,13 +893,17 @@ impl<'a> ObjectConditionalReadArgs<'a> { } } +/// Argument for [get_object()](crate::s3::client::Client::get_object) API pub type GetObjectArgs<'a> = ObjectConditionalReadArgs<'a>; +/// Argument for [stat_object()](crate::s3::client::Client::stat_object) API pub type StatObjectArgs<'a> = ObjectConditionalReadArgs<'a>; +/// Source object information for [copy object argument](CopyObjectArgs) pub type CopySource<'a> = ObjectConditionalReadArgs<'a>; #[derive(Derivative, Clone, Debug, Default)] +/// Argument for [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API pub struct RemoveObjectsApiArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -769,6 +916,18 @@ pub struct RemoveObjectsApiArgs<'a> { } impl<'a> RemoveObjectsApiArgs<'a> { + /// Returns argument for [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API with given bucket name and list of delete object information + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::DeleteObject; + /// let mut objects: Vec = Vec::new(); + /// objects.push(DeleteObject{name: "my-object-1", version_id: None}); + /// objects.push(DeleteObject{name: "my-object-2", version_id: Some("0e295d23-10e1-4c39-b134-5b08ad146df6")}); + /// let args = RemoveObjectsApiArgs::new("my-bucket", &objects).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, objects: &'a [DeleteObject], @@ -787,6 +946,7 @@ impl<'a> RemoveObjectsApiArgs<'a> { } } +/// Argument for [remove_objects()](crate::s3::client::Client::remove_objects) API pub struct RemoveObjectsArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -797,6 +957,18 @@ pub struct RemoveObjectsArgs<'a> { } impl<'a> RemoveObjectsArgs<'a> { + /// Returns argument for [remove_objects()](crate::s3::client::Client::remove_objects) API with given bucket name and iterable delete object information + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::DeleteObject; + /// let mut objects: Vec = Vec::new(); + /// objects.push(DeleteObject{name: "my-object-1", version_id: None}); + /// objects.push(DeleteObject{name: "my-object-2", version_id: Some("0e295d23-10e1-4c39-b134-5b08ad146df6")}); + /// let args = RemoveObjectsArgs::new("my-bucket", &mut objects.iter()).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, objects: &'a mut core::slice::Iter<'a, DeleteObject<'a>>, @@ -814,6 +986,7 @@ impl<'a> RemoveObjectsArgs<'a> { } } +/// Argument for [list_objects_v1()](crate::s3::client::Client::list_objects_v1) S3 API pub struct ListObjectsV1Args<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -827,6 +1000,14 @@ pub struct ListObjectsV1Args<'a> { } impl<'a> ListObjectsV1Args<'a> { + /// Returns argument for [list_objects_v1()](crate::s3::client::Client::list_objects_v1) S3 API with given bucket name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ListObjectsV1Args::new("my-bucket").unwrap(); + /// ``` pub fn new(bucket_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -844,6 +1025,7 @@ impl<'a> ListObjectsV1Args<'a> { } } +/// Argument for [list_objects_v2()](crate::s3::client::Client::list_objects_v2) S3 API pub struct ListObjectsV2Args<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -860,6 +1042,14 @@ pub struct ListObjectsV2Args<'a> { } impl<'a> ListObjectsV2Args<'a> { + /// Returns argument for [list_objects_v2()](crate::s3::client::Client::list_objects_v2) S3 API with given bucket name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ListObjectsV2Args::new("my-bucket").unwrap(); + /// ``` pub fn new(bucket_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -880,6 +1070,7 @@ impl<'a> ListObjectsV2Args<'a> { } } +/// Argument for [list_object_versions()](crate::s3::client::Client::list_object_versions) S3 API pub struct ListObjectVersionsArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -894,6 +1085,14 @@ pub struct ListObjectVersionsArgs<'a> { } impl<'a> ListObjectVersionsArgs<'a> { + /// Returns argument for [list_object_versions()](crate::s3::client::Client::list_object_versions) S3 API with given bucket name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ListObjectVersionsArgs::new("my-bucket").unwrap(); + /// ``` pub fn new(bucket_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -912,6 +1111,7 @@ impl<'a> ListObjectVersionsArgs<'a> { } } +/// Argument for [list_objects()](crate::s3::client::Client::list_objects) API pub struct ListObjectsArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -935,6 +1135,22 @@ pub struct ListObjectsArgs<'a> { } impl<'a> ListObjectsArgs<'a> { + /// Returns argument for [list_objects()](crate::s3::client::Client::list_objects) API with given bucket name and callback function for results. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = ListObjectsArgs::new( + /// "my-bucket", + /// &|items| { + /// for item in items.iter() { + /// println!("{:?}", item.name); + /// } + /// true + /// }, + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, result_fn: &'a dyn Fn(Vec) -> bool, @@ -965,6 +1181,7 @@ impl<'a> ListObjectsArgs<'a> { } } +/// Argument for [select_object_content()](crate::s3::client::Client::select_object_content) API pub struct SelectObjectContentArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -977,6 +1194,35 @@ pub struct SelectObjectContentArgs<'a> { } impl<'a> SelectObjectContentArgs<'a> { + /// Returns argument for [select_object_content()](crate::s3::client::Client::select_object_content) API with given bucket name, object name and callback function for results. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::*; + /// 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(); + /// let args = SelectObjectContentArgs::new("my-bucket", "my-object", &request).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1003,6 +1249,7 @@ impl<'a> SelectObjectContentArgs<'a> { } } +/// Argument for [listen_bucket_notification()](crate::s3::client::Client::listen_bucket_notification) API pub struct ListenBucketNotificationArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1015,6 +1262,27 @@ pub struct ListenBucketNotificationArgs<'a> { } impl<'a> ListenBucketNotificationArgs<'a> { + /// Returns argument for [listen_bucket_notification()](crate::s3::client::Client::listen_bucket_notification) API with given bucket name and callback function for results. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::NotificationRecords; + /// let event_fn = |event: NotificationRecords| { + /// for record in event.records.iter() { + /// if let Some(s3) = &record.s3 { + /// if let Some(object) = &s3.object { + /// if let Some(key) = &object.key { + /// println!("{:?} {:?}", record.event_name, key); + /// } + /// } + /// } + /// } + /// true + /// }; + /// let args = ListenBucketNotificationArgs::new("my-bucket", &event_fn).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, event_fn: &'a (dyn Fn(NotificationRecords) -> bool + Send + Sync), @@ -1035,6 +1303,7 @@ impl<'a> ListenBucketNotificationArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Argument for [upload_part_copy()](crate::s3::client::Client::upload_part_copy) S3 API pub struct UploadPartCopyArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1047,6 +1316,21 @@ pub struct UploadPartCopyArgs<'a> { } impl<'a> UploadPartCopyArgs<'a> { + /// Returns argument for [upload_part_copy()](crate::s3::client::Client::upload_part_copy) S3 API with given bucket name, object name, upload ID, part number and headers + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let src = CopySource::new("my-src-bucket", "my-src-object").unwrap(); + /// let args = UploadPartCopyArgs::new( + /// "my-bucket", + /// "my-object", + /// "c53a2b73-f5e6-484a-9bc0-09cce13e8fd0", + /// 3, + /// src.get_copy_headers(), + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1088,6 +1372,7 @@ impl<'a> UploadPartCopyArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Argument for [copy_object()](crate::s3::client::Client::copy_object) API pub struct CopyObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1106,6 +1391,15 @@ pub struct CopyObjectArgs<'a> { } impl<'a> CopyObjectArgs<'a> { + /// Returns argument for [copy_object()](crate::s3::client::Client::copy_object) API with given bucket name, object name and copy source. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let src = CopySource::new("my-src-bucket", "my-src-object").unwrap(); + /// let args = CopyObjectArgs::new("my-bucket", "my-object", src).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1151,6 +1445,7 @@ impl<'a> CopyObjectArgs<'a> { } #[derive(Clone, Debug, Default)] +/// Source object information for [compose object argument](ComposeObjectArgs) pub struct ComposeSource<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1171,6 +1466,14 @@ pub struct ComposeSource<'a> { } impl<'a> ComposeSource<'a> { + /// Returns a compose source with given bucket name and object name + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let src = ComposeSource::new("my-src-bucket", "my-src-object").unwrap(); + /// ``` pub fn new(bucket_name: &'a str, object_name: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -1295,6 +1598,7 @@ impl<'a> ComposeSource<'a> { } } +/// Argument for [compose_object()](crate::s3::client::Client::compose_object) API pub struct ComposeObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1311,6 +1615,17 @@ pub struct ComposeObjectArgs<'a> { } impl<'a> ComposeObjectArgs<'a> { + /// Returns argument for [compose_object()](crate::s3::client::Client::compose_object) API with given bucket name, object name and list of compose sources. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let mut sources: Vec = Vec::new(); + /// sources.push(ComposeSource::new("my-src-bucket", "my-src-object-1").unwrap()); + /// sources.push(ComposeSource::new("my-src-bucket", "my-src-object-2").unwrap()); + /// let args = ComposeObjectArgs::new("my-bucket", "my-object", &mut sources).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1353,11 +1668,14 @@ 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> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1367,6 +1685,15 @@ pub struct SetBucketEncryptionArgs<'a> { } impl<'a> SetBucketEncryptionArgs<'a> { + /// Returns argument for [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API with given bucket name and configuration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::SseConfig; + /// let args = SetBucketEncryptionArgs::new("my-bucket", &SseConfig::s3()).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, config: &'a SseConfig, @@ -1383,16 +1710,22 @@ impl<'a> SetBucketEncryptionArgs<'a> { } } +/// Argument for [enable_object_legal_hold()](crate::s3::client::Client::enable_object_legal_hold) API pub type EnableObjectLegalHoldArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [disable_object_legal_hold()](crate::s3::client::Client::disable_object_legal_hold) API pub type DisableObjectLegalHoldArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [is_object_legal_hold_enabled()](crate::s3::client::Client::is_object_legal_hold_enabled) API pub type IsObjectLegalHoldEnabledArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [delete_bucket_lifecycle()](crate::s3::client::Client::delete_bucket_lifecycle) API 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>, @@ -1401,10 +1734,56 @@ pub struct SetBucketLifecycleArgs<'a> { 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 = 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, 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>; +/// Argument for [delete_bucket_notification()](crate::s3::client::Client::delete_bucket_notification) API pub type GetBucketNotificationArgs<'a> = BucketArgs<'a>; +/// Argument for [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API pub struct SetBucketNotificationArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1414,6 +1793,33 @@ pub struct SetBucketNotificationArgs<'a> { } impl<'a> SetBucketNotificationArgs<'a> { + /// Returns argument for [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API with given bucket name and configuration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::*; + /// let config = NotificationConfig { + /// cloud_func_config_list: None, + /// queue_config_list: Some(vec![QueueConfig { + /// events: vec![ + /// String::from("s3:ObjectCreated:Put"), + /// String::from("s3:ObjectCreated:Copy"), + /// ], + /// id: None, + /// prefix_filter_rule: Some(PrefixFilterRule { + /// value: String::from("images"), + /// }), + /// suffix_filter_rule: Some(SuffixFilterRule { + /// value: String::from("pg"), + /// }), + /// queue: String::from("arn:minio:sqs::miniojavatest:webhook"), + /// }]), + /// topic_config_list: None, + /// }; + /// let args = SetBucketNotificationArgs::new("my-bucket", &config).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, config: &'a NotificationConfig, @@ -1430,10 +1836,13 @@ impl<'a> SetBucketNotificationArgs<'a> { } } +/// Argument for [delete_bucket_policy()](crate::s3::client::Client::delete_bucket_policy) API pub type DeleteBucketPolicyArgs<'a> = BucketArgs<'a>; +/// Argument for [get_bucket_policy()](crate::s3::client::Client::get_bucket_policy) API pub type GetBucketPolicyArgs<'a> = BucketArgs<'a>; +/// Argument for [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API pub struct SetBucketPolicyArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1443,6 +1852,38 @@ pub struct SetBucketPolicyArgs<'a> { } impl<'a> SetBucketPolicyArgs<'a> { + /// Returns argument for [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API with given bucket name and configuration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let config = r#"{ + /// "Version": "2012-10-17", + /// "Statement": [ + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": [ + /// "s3:GetBucketLocation", + /// "s3:ListBucket" + /// ], + /// "Resource": "arn:aws:s3:::my-bucket" + /// }, + /// { + /// "Effect": "Allow", + /// "Principal": { + /// "AWS": "*" + /// }, + /// "Action": "s3:GetObject", + /// "Resource": "arn:aws:s3:::my-bucket/*" + /// } + /// ] + /// }"#; + /// let args = SetBucketPolicyArgs::new("my-bucket", config).unwrap(); + /// ``` pub fn new(bucket_name: &'a str, config: &'a str) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -1456,10 +1897,13 @@ impl<'a> SetBucketPolicyArgs<'a> { } } +/// Argument for [delete_bucket_replication()](crate::s3::client::Client::delete_bucket_replication) API pub type DeleteBucketReplicationArgs<'a> = BucketArgs<'a>; +/// Argument for [get_bucket_replication()](crate::s3::client::Client::get_bucket_replication) API pub type GetBucketReplicationArgs<'a> = BucketArgs<'a>; +/// Argument for [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API pub struct SetBucketReplicationArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1468,10 +1912,72 @@ pub struct SetBucketReplicationArgs<'a> { pub config: &'a ReplicationConfig, } +impl<'a> SetBucketReplicationArgs<'a> { + /// Returns argument for [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API with given bucket name and configuration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::*; + /// use std::collections::HashMap; + /// let mut tags: HashMap = HashMap::new(); + /// tags.insert(String::from("key1"), String::from("value1")); + /// tags.insert(String::from("key2"), String::from("value2")); + /// let mut rules: Vec = Vec::new(); + /// rules.push(ReplicationRule { + /// destination: Destination { + /// bucket_arn: String::from("REPLACE-WITH-ACTUAL-DESTINATION-BUCKET-ARN"), + /// access_control_translation: None, + /// account: None, + /// encryption_config: None, + /// metrics: None, + /// replication_time: None, + /// storage_class: None, + /// }, + /// delete_marker_replication_status: None, + /// existing_object_replication_status: None, + /// filter: Some(Filter { + /// and_operator: Some(AndOperator { + /// prefix: Some(String::from("TaxDocs")), + /// tags: Some(tags), + /// }), + /// prefix: None, + /// tag: None, + /// }), + /// id: Some(String::from("rule1")), + /// prefix: None, + /// priority: Some(1), + /// source_selection_criteria: None, + /// delete_replication_status: Some(false), + /// status: true, + /// }); + /// let config = ReplicationConfig {role: None, rules: rules}; + /// let args = SetBucketReplicationArgs::new("my-bucket", &config).unwrap(); + /// ``` + pub fn new( + bucket_name: &'a str, + config: &'a ReplicationConfig, + ) -> Result, Error> { + check_bucket_name(bucket_name, true)?; + + Ok(SetBucketReplicationArgs { + extra_headers: None, + extra_query_params: None, + region: None, + bucket: bucket_name, + config, + }) + } +} + +/// Argument for [delete_bucket_tags()](crate::s3::client::Client::delete_bucket_tags) API pub type DeleteBucketTagsArgs<'a> = BucketArgs<'a>; +/// Argument for [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API pub type GetBucketTagsArgs<'a> = BucketArgs<'a>; +/// Argument for [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API pub struct SetBucketTagsArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1481,6 +1987,18 @@ pub struct SetBucketTagsArgs<'a> { } impl<'a> SetBucketTagsArgs<'a> { + /// Returns argument for [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API with given bucket name and tags + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use std::collections::HashMap; + /// let mut tags: HashMap = HashMap::new(); + /// tags.insert(String::from("Project"), String::from("Project One")); + /// tags.insert(String::from("User"), String::from("jsmith")); + /// let args = SetBucketTagsArgs::new("my-bucket", &tags).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, tags: &'a HashMap, @@ -1497,8 +2015,10 @@ 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>, pub extra_query_params: Option<&'a Multimap>, @@ -1509,6 +2029,14 @@ pub struct SetBucketVersioningArgs<'a> { } impl<'a> SetBucketVersioningArgs<'a> { + /// Returns argument for [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API with given bucket name and status + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = SetBucketVersioningArgs::new("my-bucket", true).unwrap(); + /// ``` pub fn new(bucket_name: &'a str, status: bool) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -1523,10 +2051,13 @@ impl<'a> SetBucketVersioningArgs<'a> { } } +/// Argument for [delete_object_lock_config()](crate::s3::client::Client::delete_object_lock_config) API pub type DeleteObjectLockConfigArgs<'a> = BucketArgs<'a>; +/// Argument for [get_object_lock_config()](crate::s3::client::Client::get_object_lock_config) API pub type GetObjectLockConfigArgs<'a> = BucketArgs<'a>; +/// Argument for [set_object_lock_config()](crate::s3::client::Client::set_object_lock_config) API pub struct SetObjectLockConfigArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1536,6 +2067,16 @@ pub struct SetObjectLockConfigArgs<'a> { } impl<'a> SetObjectLockConfigArgs<'a> { + /// Returns argument for [set_object_lock_config()](crate::s3::client::Client::set_object_lock_config) API with given bucket name and configuration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::*; + /// let config = ObjectLockConfig::new(RetentionMode::GOVERNANCE, Some(100), None).unwrap(); + /// let args = SetObjectLockConfigArgs::new("my-bucket", &config).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, config: &'a ObjectLockConfig, @@ -1552,8 +2093,10 @@ impl<'a> SetObjectLockConfigArgs<'a> { } } +/// Argument for [get_object_retention()](crate::s3::client::Client::get_object_retention) API pub type GetObjectRetentionArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [set_object_retention()](crate::s3::client::Client::set_object_retention) API pub struct SetObjectRetentionArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1567,9 +2110,27 @@ pub struct SetObjectRetentionArgs<'a> { } impl<'a> SetObjectRetentionArgs<'a> { + /// Returns argument for [set_object_retention()](crate::s3::client::Client::set_object_retention) API with given bucket name, object name, retention mode and retain-until date. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::types::RetentionMode; + /// use minio::s3::utils::*; + /// use chrono::Timelike; + /// let args = SetObjectRetentionArgs::new( + /// "my-bucket", + /// "my-object", + /// Some(RetentionMode::COMPLIANCE), + /// Some(utc_now().with_nanosecond(0).unwrap()), + /// ).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, + retention_mode: Option, + retain_until_date: Option, ) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -1579,6 +2140,12 @@ impl<'a> SetObjectRetentionArgs<'a> { ))); } + if retention_mode.is_some() ^ retain_until_date.is_some() { + return Err(Error::InvalidRetentionConfig(String::from( + "both mode and retain_until_date must be set or unset", + ))); + } + Ok(SetObjectRetentionArgs { extra_headers: None, extra_query_params: None, @@ -1587,16 +2154,19 @@ impl<'a> SetObjectRetentionArgs<'a> { object: object_name, version_id: None, bypass_governance_mode: false, - retention_mode: None, - retain_until_date: None, + retention_mode, + retain_until_date, }) } } +/// Argument for [delete_object_tags()](crate::s3::client::Client::delete_object_tags) API pub type DeleteObjectTagsArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [get_object_tags()](crate::s3::client::Client::get_object_tags) API pub type GetObjectTagsArgs<'a> = ObjectVersionArgs<'a>; +/// Argument for [set_object_tags()](crate::s3::client::Client::set_object_tags) API pub struct SetObjectTagsArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1608,6 +2178,18 @@ pub struct SetObjectTagsArgs<'a> { } impl<'a> SetObjectTagsArgs<'a> { + /// Returns argument for [set_object_tags()](crate::s3::client::Client::set_object_tags) API with given bucket name, object name and tags + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use std::collections::HashMap; + /// let mut tags: HashMap = HashMap::new(); + /// tags.insert(String::from("Project"), String::from("Project One")); + /// tags.insert(String::from("User"), String::from("jsmith")); + /// let args = SetObjectTagsArgs::new("my-bucket", "my-object", &tags).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1633,6 +2215,7 @@ impl<'a> SetObjectTagsArgs<'a> { } } +/// Argument for [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API pub struct GetPresignedObjectUrlArgs<'a> { pub extra_query_params: Option<&'a Multimap>, pub region: Option<&'a str>, @@ -1645,6 +2228,15 @@ pub struct GetPresignedObjectUrlArgs<'a> { } impl<'a> GetPresignedObjectUrlArgs<'a> { + /// Returns argument for [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API with given bucket name, object name and method + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use hyper::http::Method; + /// let args = GetPresignedObjectUrlArgs::new("my-bucket", "my-object", Method::GET).unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1671,6 +2263,10 @@ impl<'a> GetPresignedObjectUrlArgs<'a> { } } +/// Post policy information for presigned post policy form-data +/// +/// Condition elements and respective condition for Post policy is available here. pub struct PostPolicy<'a> { pub region: Option<&'a str>, pub bucket: &'a str, @@ -1687,6 +2283,17 @@ impl<'a> PostPolicy<'a> { const STARTS_WITH: &str = "starts-with"; const ALGORITHM: &str = "AWS4-HMAC-SHA256"; + /// Returns post policy with given bucket name and expiration + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// let expiration = utc_now() + Duration::days(7); + /// let policy = PostPolicy::new("my-bucket", &expiration).unwrap(); + /// ``` pub fn new(bucket_name: &'a str, expiration: &'a UtcTime) -> Result, Error> { check_bucket_name(bucket_name, true)?; @@ -1727,6 +2334,19 @@ impl<'a> PostPolicy<'a> { ) } + /// Adds equals condition for given element and value + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// let expiration = utc_now() + Duration::days(7); + /// let mut policy = PostPolicy::new("my-bucket", &expiration).unwrap(); + /// + /// // Add condition that 'key' (object name) equals to 'my-objectname' + /// policy.add_equals_condition("key", "my-object"); + /// ``` pub fn add_equals_condition(&mut self, element: &str, value: &str) -> Result<(), Error> { if element.is_empty() { return Err(Error::PostPolicyError( @@ -1750,10 +2370,36 @@ impl<'a> PostPolicy<'a> { Ok(()) } + /// Removes equals condition for given element + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// let expiration = utc_now() + Duration::days(7); + /// let mut policy = PostPolicy::new("my-bucket", &expiration).unwrap(); + /// policy.add_equals_condition("key", "my-object"); + /// + /// 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::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// let expiration = utc_now() + Duration::days(7); + /// let mut policy = PostPolicy::new("my-bucket", &expiration).unwrap(); + /// + /// // Add condition that 'Content-Type' starts with 'image/' + /// policy.add_starts_with_condition("Content-Type", "image/"); + /// ``` pub fn add_starts_with_condition(&mut self, element: &str, value: &str) -> Result<(), Error> { if element.is_empty() { return Err(Error::PostPolicyError( @@ -1780,10 +2426,36 @@ impl<'a> PostPolicy<'a> { Ok(()) } + /// Removes starts-with condition for given element + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// let expiration = utc_now() + Duration::days(7); + /// let mut policy = PostPolicy::new("my-bucket", &expiration).unwrap(); + /// policy.add_starts_with_condition("Content-Type", "image/"); + /// + /// 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::args::*; + /// use minio::s3::utils::*; + /// use chrono::Duration; + /// 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); + /// ``` pub fn add_content_length_range_condition( &mut self, lower_limit: usize, @@ -1800,11 +2472,14 @@ impl<'a> PostPolicy<'a> { 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, @@ -1883,6 +2558,7 @@ impl<'a> PostPolicy<'a> { } } +/// Argument for [download_object()](crate::s3::client::Client::download_object) API pub struct DownloadObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1896,6 +2572,14 @@ pub struct DownloadObjectArgs<'a> { } impl<'a> DownloadObjectArgs<'a> { + /// Returns argument for [download_object()](crate::s3::client::Client::download_object) API with given bucket name, object name and filename + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = DownloadObjectArgs::new("my-bucket", "my-object", "/path/to/my/object/download").unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, @@ -1923,6 +2607,7 @@ impl<'a> DownloadObjectArgs<'a> { } } +/// Argument for [upload_object()](crate::s3::client::Client::upload_object) API pub struct UploadObjectArgs<'a> { pub extra_headers: Option<&'a Multimap>, pub extra_query_params: Option<&'a Multimap>, @@ -1943,6 +2628,14 @@ pub struct UploadObjectArgs<'a> { } impl<'a> UploadObjectArgs<'a> { + /// Returns argument for [upload_object()](crate::s3::client::Client::upload_object) API with given bucket name, object name and filename + /// + /// # Examples + /// + /// ``` + /// use minio::s3::args::*; + /// let args = UploadObjectArgs::new("my-bucket", "my-object", "asiaphotos-2015.zip").unwrap(); + /// ``` pub fn new( bucket_name: &'a str, object_name: &'a str, diff --git a/src/s3/client.rs b/src/s3/client.rs index 0544c7c..1c2c495 100644 --- a/src/s3/client.rs +++ b/src/s3/client.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! S3 client to perform bucket and object operations + use crate::s3::args::*; use crate::s3::creds::Provider; use crate::s3::error::{Error, ErrorResponse}; @@ -201,6 +203,10 @@ fn parse_list_objects_common_prefixes( } #[derive(Clone, Debug, Default)] +/// Simple Storage Service (aka S3) client to perform bucket and object operations. +/// +/// If credential provider is passed, all S3 operation requests are signed using AWS Signature +/// Version 4; else they are performed anonymously. pub struct Client<'a> { client: reqwest::Client, base_url: BaseUrl, @@ -209,6 +215,22 @@ pub struct Client<'a> { } impl<'a> Client<'a> { + /// Returns a S3 client with given base URL. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::client::Client; + /// use minio::s3::creds::StaticProvider; + /// use minio::s3::http::BaseUrl; + /// let mut base_url = BaseUrl::from_string("play.min.io".to_string()).unwrap(); + /// let static_provider = StaticProvider::new( + /// "Q3AM3UQ867SPQQA43P2F", + /// "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", + /// None, + /// ); + /// let client = Client::new(base_url.clone(), Some(&static_provider), None, None).unwrap(); + /// ``` pub fn new( base_url: BaseUrl, provider: Option<&(dyn Provider + Send + Sync)>, @@ -626,6 +648,7 @@ impl<'a> Client<'a> { Ok(location) } + /// Executes [AbortMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) S3 API pub async fn abort_multipart_upload( &self, args: &AbortMultipartUploadArgs<'_>, @@ -712,6 +735,7 @@ impl<'a> Client<'a> { } } + /// Executes [CompleteMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) S3 API pub async fn complete_multipart_upload( &self, args: &CompleteMultipartUploadArgs<'_>, @@ -1131,6 +1155,7 @@ impl<'a> Client<'a> { }) } + /// Executes [CreateMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) S3 API pub async fn create_multipart_upload( &self, args: &CreateMultipartUploadArgs<'_>, @@ -2434,6 +2459,7 @@ impl<'a> Client<'a> { )) } + /// Executes [ListObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) S3 API pub async fn list_objects_v1( &self, args: &ListObjectsV1Args<'_>, @@ -2500,6 +2526,7 @@ impl<'a> Client<'a> { }) } + /// Executes [ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) S3 API pub async fn list_objects_v2( &self, args: &ListObjectsV2Args<'_>, @@ -2584,6 +2611,7 @@ impl<'a> Client<'a> { }) } + /// Executes [ListObjectVersions](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectVersions.html) S3 API pub async fn list_object_versions( &self, args: &ListObjectVersionsArgs<'_>, @@ -2980,6 +3008,7 @@ impl<'a> Client<'a> { res } + /// Executes [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) S3 API pub async fn put_object_api( &self, args: &PutObjectApiArgs<'_>, @@ -3099,6 +3128,7 @@ impl<'a> Client<'a> { }) } + /// Executes [DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) S3 API pub async fn remove_objects_api( &self, args: &RemoveObjectsApiArgs<'_>, @@ -3779,6 +3809,7 @@ impl<'a> Client<'a> { .await } + /// Executes [UploadPart](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) S3 API pub async fn upload_part( &self, args: &UploadPartArgs<'_>, @@ -3803,6 +3834,7 @@ impl<'a> Client<'a> { self.put_object_api(&poa_args).await } + /// Executes [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) S3 API pub async fn upload_part_copy( &self, args: &UploadPartCopyArgs<'_>, diff --git a/src/s3/creds.rs b/src/s3/creds.rs index 8bfe0de..c194d20 100644 --- a/src/s3/creds.rs +++ b/src/s3/creds.rs @@ -13,23 +13,36 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Credential providers + #[derive(Clone, Debug, Default)] +/// Credentials contain access key, secret key and session token optionally pub struct Credentials { pub access_key: String, pub secret_key: String, pub session_token: Option, } +/// Provider trait to fetch credentials pub trait Provider: std::fmt::Debug { fn fetch(&self) -> Credentials; } #[derive(Clone, Debug)] +/// Static credential provider pub struct StaticProvider { creds: Credentials, } impl StaticProvider { + /// Returns a static provider with given access key, secret key and optional session token + /// + /// # Examples + /// + /// ``` + /// use minio::s3::creds::StaticProvider; + /// let provider = StaticProvider::new("minioadmin", "minio123", None); + /// ``` pub fn new(access_key: &str, secret_key: &str, session_token: Option<&str>) -> StaticProvider { StaticProvider { creds: Credentials { diff --git a/src/s3/error.rs b/src/s3/error.rs index d44dca5..b2f3ce2 100644 --- a/src/s3/error.rs +++ b/src/s3/error.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Error definitions for S3 operations + extern crate alloc; use crate::s3::utils::get_default_text; use bytes::{Buf, Bytes}; @@ -20,6 +22,7 @@ use std::fmt; use xmltree::Element; #[derive(Clone, Debug, Default)] +/// Error response for S3 operations pub struct ErrorResponse { pub code: String, pub message: String, @@ -50,6 +53,7 @@ impl ErrorResponse { } #[derive(Debug)] +/// Error definitions pub enum Error { TimeParseError(chrono::ParseError), InvalidUrl(http::uri::InvalidUri), @@ -74,6 +78,7 @@ pub enum Error { InvalidPartNumber(String), EmptyParts(String), InvalidRetentionMode(String), + InvalidRetentionConfig(String), InvalidMinPartSize(usize), InvalidMaxPartSize(usize), InvalidObjectSize(usize), @@ -128,6 +133,7 @@ impl fmt::Display for Error { Error::InvalidPartNumber(m) => write!(f, "{}", m), Error::EmptyParts(m) => write!(f, "{}", m), Error::InvalidRetentionMode(m) => write!(f, "invalid retention mode {}", m), + Error::InvalidRetentionConfig(m) => write!(f, "invalid retention configuration; {}", m), Error::InvalidMinPartSize(s) => write!(f, "part size {} is not supported; minimum allowed 5MiB", s), Error::InvalidMaxPartSize(s) => write!(f, "part size {} is not supported; maximum allowed 5GiB", s), Error::InvalidObjectSize(s) => write!(f, "object size {} is not supported; maximum allowed 5TiB", s), diff --git a/src/s3/http.rs b/src/s3/http.rs index d181a72..2511dc3 100644 --- a/src/s3/http.rs +++ b/src/s3/http.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! HTTP URL definitions + use crate::s3::error::Error; use crate::s3::utils::{to_query_string, Multimap}; use derivative::Derivative; @@ -22,6 +24,7 @@ use std::fmt; #[derive(Derivative)] #[derivative(Clone, Debug, Default)] +/// Represents HTTP URL pub struct Url { #[derivative(Default(value = "true"))] pub https: bool, @@ -90,6 +93,7 @@ fn extract_region(host: &str) -> String { #[derive(Derivative)] #[derivative(Clone, Debug, Default)] +/// Represents Base URL of S3 endpoint pub struct BaseUrl { #[derivative(Default(value = "true"))] pub https: bool, @@ -103,6 +107,7 @@ pub struct BaseUrl { } impl BaseUrl { + /// Builds URL from base URL for given parameters for S3 operation pub fn build_url( &self, method: &Method, @@ -188,6 +193,21 @@ impl BaseUrl { Ok(url) } + /// Returns a base URL from given host string + /// + /// # Examples + /// + /// ``` + /// use minio::s3::http::BaseUrl; + /// // Get base URL from host name + /// let base_url = BaseUrl::from_string("play.min.io".to_string()).unwrap(); + /// // Get base URL from host:port + /// let base_url = BaseUrl::from_string("play.minio.io:9000".to_string()).unwrap(); + /// // Get base URL from IPv4 address + /// let base_url = BaseUrl::from_string("http://192.168.124.63:9000".to_string()).unwrap(); + /// // Get base URL from IPv6 address + /// let base_url = BaseUrl::from_string("[0:0:0:0:0:ffff:c0a8:7c3f]:9000".to_string()).unwrap(); + /// ``` pub fn from_string(s: String) -> Result { let url = s.parse::()?; diff --git a/src/s3/mod.rs b/src/s3/mod.rs index 7904da4..289187e 100644 --- a/src/s3/mod.rs +++ b/src/s3/mod.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Implementation of Simple Storage Service (aka S3) client + pub mod args; pub mod client; pub mod creds; diff --git a/src/s3/response.rs b/src/s3/response.rs index 83c98ad..124ffda 100644 --- a/src/s3/response.rs +++ b/src/s3/response.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Responses for [minio::s3::client::Client](crate::s3::client::Client) APIs + use crate::s3::error::Error; use crate::s3::types::{ parse_legal_hold, Bucket, Item, LifecycleConfig, NotificationConfig, ObjectLockConfig, @@ -28,23 +30,28 @@ use std::io::BufReader; use xmltree::Element; #[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 { pub headers: HeaderMap, pub region: String, pub bucket_name: String, } +/// Response of [make_bucket()](crate::s3::client::Client::make_bucket) API pub type MakeBucketResponse = BucketResponse; +/// Response of [remove_bucket()](crate::s3::client::Client::remove_bucket) API pub type RemoveBucketResponse = BucketResponse; #[derive(Debug)] +/// Base response for object operation pub struct ObjectResponse { pub headers: HeaderMap, pub region: String, @@ -53,9 +60,11 @@ pub struct ObjectResponse { pub version_id: Option, } +/// Response of [remove_object()](crate::s3::client::Client::remove_object) API pub type RemoveObjectResponse = ObjectResponse; #[derive(Debug)] +/// Base Upload ID response pub struct UploadIdResponse { pub headers: HeaderMap, pub region: String, @@ -64,11 +73,14 @@ pub struct UploadIdResponse { pub upload_id: String, } +/// Response of [abort_multipart_upload()](crate::s3::client::Client::abort_multipart_upload) API pub type AbortMultipartUploadResponse = UploadIdResponse; +/// Response of [create_multipart_upload()](crate::s3::client::Client::create_multipart_upload) API pub type CreateMultipartUploadResponse = UploadIdResponse; #[derive(Debug)] +/// Base response for put object pub struct PutObjectBaseResponse { pub headers: HeaderMap, pub bucket_name: String, @@ -78,23 +90,32 @@ pub struct PutObjectBaseResponse { pub version_id: Option, } +/// Response of [complete_multipart_upload()](crate::s3::client::Client::complete_multipart_upload) API pub type CompleteMultipartUploadResponse = PutObjectBaseResponse; +/// Response of [put_object_api()](crate::s3::client::Client::put_object_api) S3 API pub type PutObjectApiResponse = PutObjectBaseResponse; +/// Response of [upload_part()](crate::s3::client::Client::upload_part) S3 API pub type UploadPartResponse = PutObjectApiResponse; +/// Response of [put_object()](crate::s3::client::Client::put_object) API pub type PutObjectResponse = PutObjectApiResponse; +/// Response of [upload_part_copy()](crate::s3::client::Client::upload_part_copy) S3 API pub type UploadPartCopyResponse = PutObjectApiResponse; +/// Response of [copy_object()](crate::s3::client::Client::copy_object) API pub type CopyObjectResponse = PutObjectApiResponse; +/// Response of [compose_object()](crate::s3::client::Client::compose_object) API pub type ComposeObjectResponse = PutObjectApiResponse; +/// Response of [upload_object()](crate::s3::client::Client::upload_object) API pub type UploadObjectResponse = PutObjectApiResponse; #[derive(Debug)] +/// Response of [stat_object()](crate::s3::client::Client::stat_object) API pub struct StatObjectResponse { pub headers: HeaderMap, pub region: String, @@ -184,6 +205,7 @@ impl StatObjectResponse { } #[derive(Clone, Debug)] +/// Error defintion of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API pub struct DeleteError { pub code: String, pub message: String, @@ -192,6 +214,7 @@ pub struct DeleteError { } #[derive(Clone, Debug)] +/// Deleted object defintion of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API pub struct DeletedObject { pub name: String, pub version_id: Option, @@ -200,6 +223,7 @@ pub struct DeletedObject { } #[derive(Clone, Debug)] +/// Response of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API pub struct RemoveObjectsApiResponse { pub headers: HeaderMap, pub region: String, @@ -208,9 +232,11 @@ pub struct RemoveObjectsApiResponse { pub errors: Vec, } +/// Response of [remove_objects()](crate::s3::client::Client::remove_objects) API pub type RemoveObjectsResponse = RemoveObjectsApiResponse; #[derive(Clone, Debug)] +/// Response of [list_objects_v1()](crate::s3::client::Client::list_objects_v1) S3 API pub struct ListObjectsV1Response { pub headers: HeaderMap, pub name: String, @@ -225,6 +251,7 @@ pub struct ListObjectsV1Response { } #[derive(Clone, Debug)] +/// Response of [list_objects_v2()](crate::s3::client::Client::list_objects_v2) S3 API pub struct ListObjectsV2Response { pub headers: HeaderMap, pub name: String, @@ -241,6 +268,7 @@ pub struct ListObjectsV2Response { } #[derive(Clone, Debug)] +/// Response of [list_object_versions()](crate::s3::client::Client::list_object_versions) S3 API pub struct ListObjectVersionsResponse { pub headers: HeaderMap, pub name: String, @@ -257,6 +285,7 @@ pub struct ListObjectVersionsResponse { } #[derive(Clone, Debug)] +/// Response of [list_objects()](crate::s3::client::Client::list_objects) API pub struct ListObjectsResponse { pub headers: HeaderMap, pub name: String, @@ -284,6 +313,7 @@ pub struct ListObjectsResponse { pub next_version_id_marker: String, } +/// Response of [select_object_content()](crate::s3::client::Client::select_object_content) API pub struct SelectObjectContentResponse { pub headers: HeaderMap, pub region: String, @@ -607,6 +637,7 @@ impl SelectObjectContentResponse { } #[derive(Clone, Debug)] +/// Response of [listen_bucket_notification()](crate::s3::client::Client::listen_bucket_notification) API pub struct ListenBucketNotificationResponse { pub headers: HeaderMap, pub region: String, @@ -627,9 +658,11 @@ impl ListenBucketNotificationResponse { } } +/// 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, @@ -637,13 +670,17 @@ pub struct GetBucketEncryptionResponse { pub config: SseConfig, } +/// Response of [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API pub type SetBucketEncryptionResponse = BucketResponse; +/// Response of [enable_object_legal_hold()](crate::s3::client::Client::enable_object_legal_hold) API pub type EnableObjectLegalHoldResponse = ObjectResponse; +/// Response of [disable_object_legal_hold()](crate::s3::client::Client::disable_object_legal_hold) API pub type DisableObjectLegalHoldResponse = ObjectResponse; #[derive(Clone, Debug)] +/// Response of [is_object_legal_hold_enabled()](crate::s3::client::Client::is_object_legal_hold_enabled) API pub struct IsObjectLegalHoldEnabledResponse { pub headers: HeaderMap, pub region: String, @@ -653,9 +690,11 @@ pub struct IsObjectLegalHoldEnabledResponse { pub enabled: bool, } +/// 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, @@ -663,11 +702,14 @@ pub struct GetBucketLifecycleResponse { 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; #[derive(Clone, Debug)] +/// Response of [get_bucket_notification()](crate::s3::client::Client::get_bucket_notification) API pub struct GetBucketNotificationResponse { pub headers: HeaderMap, pub region: String, @@ -675,11 +717,14 @@ pub struct GetBucketNotificationResponse { pub config: NotificationConfig, } +/// Response of [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API pub type SetBucketNotificationResponse = BucketResponse; +/// Response of [delete_bucket_policy()](crate::s3::client::Client::delete_bucket_policy) API pub type DeleteBucketPolicyResponse = BucketResponse; #[derive(Clone, Debug)] +/// Response of [get_bucket_policy()](crate::s3::client::Client::get_bucket_policy) API pub struct GetBucketPolicyResponse { pub headers: HeaderMap, pub region: String, @@ -687,11 +732,14 @@ pub struct GetBucketPolicyResponse { pub config: String, } +/// Response of [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API pub type SetBucketPolicyResponse = BucketResponse; +/// Response of [delete_bucket_replication()](crate::s3::client::Client::delete_bucket_replication) API pub type DeleteBucketReplicationResponse = BucketResponse; #[derive(Clone, Debug)] +/// Response of [get_bucket_replication()](crate::s3::client::Client::get_bucket_replication) API pub struct GetBucketReplicationResponse { pub headers: HeaderMap, pub region: String, @@ -699,11 +747,14 @@ pub struct GetBucketReplicationResponse { pub config: ReplicationConfig, } +/// Response of [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API pub type SetBucketReplicationResponse = BucketResponse; +/// Response of [delete_bucket_tags()](crate::s3::client::Client::delete_bucket_tags) API pub type DeleteBucketTagsResponse = BucketResponse; #[derive(Clone, Debug)] +/// Response of [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API pub struct GetBucketTagsResponse { pub headers: HeaderMap, pub region: String, @@ -711,9 +762,11 @@ pub struct GetBucketTagsResponse { pub tags: std::collections::HashMap, } +/// 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, @@ -722,11 +775,14 @@ pub struct GetBucketVersioningResponse { pub mfa_delete: Option, } +/// Response of [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API pub type SetBucketVersioningResponse = BucketResponse; +/// Response of [delete_object_lock_config()](crate::s3::client::Client::delete_object_lock_config) API pub type DeleteObjectLockConfigResponse = BucketResponse; #[derive(Clone, Debug)] +/// Response of [get_object_lock_config()](crate::s3::client::Client::get_object_lock_config) API pub struct GetObjectLockConfigResponse { pub headers: HeaderMap, pub region: String, @@ -734,9 +790,11 @@ pub struct GetObjectLockConfigResponse { pub config: ObjectLockConfig, } +/// Response of [set_object_lock_config()](crate::s3::client::Client::set_object_lock_config) API pub type SetObjectLockConfigResponse = BucketResponse; #[derive(Clone, Debug)] +/// Response of [get_object_retention()](crate::s3::client::Client::get_object_retention) API pub struct GetObjectRetentionResponse { pub headers: HeaderMap, pub region: String, @@ -747,11 +805,14 @@ pub struct GetObjectRetentionResponse { pub retain_until_date: Option, } +/// Response of [set_object_retention()](crate::s3::client::Client::set_object_retention) API pub type SetObjectRetentionResponse = ObjectResponse; +/// Response of [delete_object_tags()](crate::s3::client::Client::delete_object_tags) API pub type DeleteObjectTagsResponse = ObjectResponse; #[derive(Clone, Debug)] +/// Response of [get_object_tags()](crate::s3::client::Client::get_object_tags) API pub struct GetObjectTagsResponse { pub headers: HeaderMap, pub region: String, @@ -761,9 +822,11 @@ pub struct GetObjectTagsResponse { pub tags: std::collections::HashMap, } +/// Response of [set_object_tags()](crate::s3::client::Client::set_object_tags) API pub type SetObjectTagsResponse = ObjectResponse; #[derive(Clone, Debug)] +/// Response of [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API pub struct GetPresignedObjectUrlResponse { pub region: String, pub bucket_name: String, @@ -773,6 +836,7 @@ pub struct GetPresignedObjectUrlResponse { } #[derive(Clone, Debug)] +/// Response of [download_object()](crate::s3::client::Client::download_object) API pub struct DownloadObjectResponse { pub headers: HeaderMap, pub region: String, diff --git a/src/s3/signer.rs b/src/s3/signer.rs index 6db8a9a..370bd83 100644 --- a/src/s3/signer.rs +++ b/src/s3/signer.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Signature V4 for S3 API + use crate::s3::utils::{ get_canonical_headers, get_canonical_query_string, sha256_hash, to_amz_date, to_signer_date, Multimap, UtcTime, @@ -22,16 +24,19 @@ use hmac::{Hmac, Mac}; use hyper::http::Method; use sha2::Sha256; +/// Returns HMAC hash for given key and data pub fn hmac_hash(key: &[u8], data: &[u8]) -> Vec { let mut hasher = Hmac::::new_from_slice(key).expect("HMAC can take key of any size"); hasher.update(data); hasher.finalize().into_bytes().to_vec() } +/// Returns hex encoded HMAC hash for given key and data pub fn hmac_hash_hex(key: &[u8], data: &[u8]) -> String { hexencode(hmac_hash(key, data)) } +/// Returns scope value of given date, region and service name pub fn get_scope(date: UtcTime, region: &str, service_name: &str) -> String { format!( "{}/{}/{}/aws4_request", @@ -41,6 +46,7 @@ pub fn get_scope(date: UtcTime, region: &str, service_name: &str) -> String { ) } +/// Returns hex encoded SHA256 hash of canonical request pub fn get_canonical_request_hash( method: &Method, uri: &str, @@ -63,6 +69,7 @@ pub fn get_canonical_request_hash( return sha256_hash(canonical_request.as_bytes()); } +/// Returns string-to-sign value of given date, scope and canonical request hash pub fn get_string_to_sign(date: UtcTime, scope: &str, canonical_request_hash: &str) -> String { format!( "AWS4-HMAC-SHA256\n{}\n{}\n{}", @@ -72,6 +79,7 @@ pub fn get_string_to_sign(date: UtcTime, scope: &str, canonical_request_hash: &s ) } +/// Returns signing key of given secret key, date, region and service name pub fn get_signing_key( secret_key: &str, date: UtcTime, @@ -87,10 +95,12 @@ pub fn get_signing_key( return hmac_hash(date_region_service_key.as_slice(), b"aws4_request"); } +/// Returns signature value for given signing key and string-to-sign pub fn get_signature(signing_key: &[u8], string_to_sign: &[u8]) -> String { hmac_hash_hex(signing_key, string_to_sign) } +/// Returns authorization value for given access key, scope, signed headers and signature pub fn get_authorization( access_key: &str, scope: &str, @@ -103,6 +113,7 @@ pub fn get_authorization( ) } +/// Signs and updates headers for given parameters pub fn sign_v4( service_name: &str, method: &Method, @@ -134,6 +145,7 @@ pub fn sign_v4( headers.insert("Authorization".to_string(), authorization); } +/// Signs and updates headers for given parameters for S3 request pub fn sign_v4_s3( method: &Method, uri: &str, @@ -159,6 +171,7 @@ pub fn sign_v4_s3( ) } +/// Signs and updates headers for given parameters for STS request pub fn sign_v4_sts( method: &Method, uri: &str, @@ -184,6 +197,7 @@ pub fn sign_v4_sts( ) } +/// Signs and updates headers for given parameters for pre-sign request pub fn presign_v4( method: &Method, host: &str, @@ -230,6 +244,7 @@ pub fn presign_v4( query_params.insert("X-Amz-Signature".to_string(), signature); } +/// Signs and updates headers for given parameters for pre-sign POST request pub fn post_presign_v4( string_to_sign: &str, secret_key: &str, diff --git a/src/s3/sse.rs b/src/s3/sse.rs index 913b8c3..7976ff9 100644 --- a/src/s3/sse.rs +++ b/src/s3/sse.rs @@ -13,9 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Server side encryption definitions + use crate::s3::utils; use std::any::Any; +/// Base server side encryption pub trait Sse: std::fmt::Debug { fn headers(&self) -> utils::Multimap; fn copy_headers(&self) -> utils::Multimap; @@ -24,6 +27,7 @@ pub trait Sse: std::fmt::Debug { } #[derive(Clone, Debug)] +/// Server side encryption customer key type pub struct SseCustomerKey { headers: utils::Multimap, copy_headers: utils::Multimap, @@ -88,6 +92,7 @@ impl Sse for SseCustomerKey { } #[derive(Clone, Debug)] +/// Server side encryption KMS type pub struct SseKms { headers: utils::Multimap, } @@ -133,6 +138,7 @@ impl Sse for SseKms { } #[derive(Clone, Debug)] +/// Server side encryption S3 type pub struct SseS3 { headers: utils::Multimap, } diff --git a/src/s3/types.rs b/src/s3/types.rs index 99fed2a..0c142ba 100644 --- a/src/s3/types.rs +++ b/src/s3/types.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Various types for S3 API requests and responses + use crate::s3::error::Error; use crate::s3::utils::{ from_iso8601utc, get_default_text, get_option_text, get_text, to_iso8601utc, UtcTime, @@ -23,6 +25,7 @@ use std::fmt; use xmltree::Element; #[derive(Clone, Debug, Default)] +/// Contains information of an item of [list_objects()](crate::s3::client::Client::list_objects) API pub struct Item { pub name: String, pub last_modified: Option, @@ -40,18 +43,21 @@ pub struct Item { } #[derive(Clone, Debug)] +/// Contains bucket name and creation date pub struct Bucket { pub name: String, pub creation_date: UtcTime, } #[derive(Clone, Debug)] +/// Contains part number and etag of multipart upload pub struct Part { pub number: u16, pub etag: String, } #[derive(Clone, Debug)] +/// Contains retention mode information pub enum RetentionMode { GOVERNANCE, COMPLIANCE, @@ -77,11 +83,13 @@ impl fmt::Display for RetentionMode { } #[derive(Clone, Debug)] +/// Contains retention mode and retain until date pub struct Retention { pub mode: RetentionMode, pub retain_until_date: UtcTime, } +/// Parses legal hold string value pub fn parse_legal_hold(s: &str) -> Result { match s { "ON" => Ok(true), @@ -91,12 +99,14 @@ pub fn parse_legal_hold(s: &str) -> Result { } #[derive(Clone, Debug, Copy)] +/// Contains delete object name and optional version ID pub struct DeleteObject<'a> { pub name: &'a str, pub version_id: Option<&'a str>, } #[derive(Clone, Debug)] +/// Compression types pub enum CompressionType { NONE, GZIP, @@ -114,6 +124,7 @@ impl fmt::Display for CompressionType { } #[derive(Clone, Debug)] +/// File header information types pub enum FileHeaderInfo { USE, IGNORE, @@ -131,6 +142,7 @@ impl fmt::Display for FileHeaderInfo { } #[derive(Clone, Debug)] +/// JSON document types pub enum JsonType { DOCUMENT, LINES, @@ -146,6 +158,7 @@ impl fmt::Display for JsonType { } #[derive(Clone, Debug)] +/// Quote fields types pub enum QuoteFields { ALWAYS, ASNEEDED, @@ -161,6 +174,7 @@ impl fmt::Display for QuoteFields { } #[derive(Clone, Debug, Default)] +/// CSV input serialization definitions pub struct CsvInputSerialization { pub compression_type: Option, pub allow_quoted_record_delimiter: bool, @@ -173,15 +187,18 @@ pub struct CsvInputSerialization { } #[derive(Clone, Debug, Default)] +/// JSON input serialization definitions pub struct JsonInputSerialization { pub compression_type: Option, pub json_type: Option, } #[derive(Clone, Debug, Default)] +/// Parque input serialization definitions pub struct ParquetInputSerialization; #[derive(Clone, Debug, Default)] +/// CSV output serialization definitions pub struct CsvOutputSerialization { pub field_delimiter: Option, pub quote_character: Option, @@ -191,11 +208,13 @@ pub struct CsvOutputSerialization { } #[derive(Clone, Debug, Default)] +/// JSON output serialization definitions pub struct JsonOutputSerialization { pub record_delimiter: Option, } #[derive(Clone, Debug, Default)] +/// Select request for [select_object_content()](crate::s3::client::Client::select_object_content) API pub struct SelectRequest<'a> { pub expr: &'a str, pub csv_input: Option, @@ -456,6 +475,7 @@ impl<'a> SelectRequest<'a> { } #[derive(Clone, Debug)] +/// Progress information of [select_object_content()](crate::s3::client::Client::select_object_content) API pub struct SelectProgress { pub bytes_scanned: usize, pub bytes_progressed: usize, @@ -463,14 +483,17 @@ pub struct SelectProgress { } #[derive(Debug, Deserialize, Serialize)] +/// User identity contains principal ID pub struct UserIdentity { #[serde(alias = "principalId")] pub principal_id: Option, } +/// Owner identity contains principal ID pub type OwnerIdentity = UserIdentity; #[derive(Debug, Deserialize, Serialize)] +/// Request parameters contain principal ID, region and source IP address pub struct RequestParameters { #[serde(alias = "principalId")] pub principal_id: Option, @@ -481,6 +504,7 @@ pub struct RequestParameters { } #[derive(Debug, Deserialize, Serialize)] +/// Response elements information pub struct ResponseElements { #[serde(alias = "content-length")] pub content_length: Option, @@ -493,6 +517,7 @@ pub struct ResponseElements { } #[derive(Debug, Deserialize, Serialize)] +/// S3 bucket information pub struct S3Bucket { #[serde(alias = "name")] pub name: Option, @@ -503,6 +528,7 @@ pub struct S3Bucket { } #[derive(Debug, Deserialize, Serialize)] +/// S3 object information pub struct S3Object { #[serde(alias = "key")] pub key: Option, @@ -519,6 +545,7 @@ pub struct S3Object { } #[derive(Debug, Deserialize, Serialize)] +/// S3 definitions for NotificationRecord pub struct S3 { #[serde(alias = "s3SchemaVersion")] pub s3_schema_version: Option, @@ -531,6 +558,7 @@ pub struct S3 { } #[derive(Debug, Deserialize, Serialize)] +/// Source information pub struct Source { #[serde(alias = "host")] pub host: Option, @@ -541,6 +569,7 @@ pub struct Source { } #[derive(Debug, Deserialize, Serialize)] +/// Notification record information pub struct NotificationRecord { #[serde(alias = "eventVersion")] pub event_version: Option, @@ -565,12 +594,14 @@ pub struct NotificationRecord { } #[derive(Debug, Deserialize, Serialize)] +/// Contains notification records pub struct NotificationRecords { #[serde(alias = "Records")] pub records: Vec, } #[derive(Clone, Debug)] +/// Directive types pub enum Directive { Copy, Replace, @@ -596,6 +627,7 @@ impl fmt::Display for Directive { } #[derive(Clone, Debug)] +/// Server-side information configuration pub struct SseConfig { pub sse_algorithm: String, pub kms_master_key_id: Option, @@ -636,18 +668,21 @@ impl SseConfig { } #[derive(Clone, Debug)] +/// Contains key and value pub struct Tag { pub key: String, pub value: String, } #[derive(Clone, Debug)] +/// And operator contains prefix and tags pub struct AndOperator { pub prefix: Option, pub tags: Option>, } #[derive(Clone, Debug)] +/// Filter information pub struct Filter { pub and_operator: Option, pub prefix: Option, @@ -763,6 +798,7 @@ impl Filter { } #[derive(Clone, Debug)] +/// Lifecycle rule information pub struct LifecycleRule { pub abort_incomplete_multipart_upload_days_after_initiation: Option, pub expiration_date: Option, @@ -900,6 +936,7 @@ impl LifecycleRule { } #[derive(Clone, Debug)] +/// Lifecycle configuration pub struct LifecycleConfig { pub rules: Vec, } @@ -1155,6 +1192,7 @@ fn to_xml_common_notification_config( } #[derive(Clone, Debug)] +/// Prefix filter rule pub struct PrefixFilterRule { pub value: String, } @@ -1164,6 +1202,7 @@ impl PrefixFilterRule { } #[derive(Clone, Debug)] +/// Suffix filter rule pub struct SuffixFilterRule { pub value: String, } @@ -1173,6 +1212,7 @@ impl SuffixFilterRule { } #[derive(Clone, Debug)] +/// Cloud function configuration information pub struct CloudFuncConfig { pub events: Vec, pub id: Option, @@ -1223,6 +1263,7 @@ impl CloudFuncConfig { } #[derive(Clone, Debug)] +/// Queue configuration information pub struct QueueConfig { pub events: Vec, pub id: Option, @@ -1273,6 +1314,7 @@ impl QueueConfig { } #[derive(Clone, Debug)] +/// Topic configuration information pub struct TopicConfig { pub events: Vec, pub id: Option, @@ -1323,6 +1365,7 @@ impl TopicConfig { } #[derive(Clone, Debug)] +/// Notification configuration information pub struct NotificationConfig { pub cloud_func_config_list: Option>, pub queue_config_list: Option>, @@ -1413,6 +1456,7 @@ impl NotificationConfig { } #[derive(Clone, Debug)] +/// Access control translation information pub struct AccessControlTranslation { pub owner: String, } @@ -1432,11 +1476,13 @@ impl Default for AccessControlTranslation { } #[derive(Clone, Debug)] +/// Encryption configuration information pub struct EncryptionConfig { pub replica_kms_key_id: Option, } #[derive(Clone, Debug)] +/// Metrics information pub struct Metrics { pub event_threshold_minutes: Option, pub status: bool, @@ -1452,6 +1498,7 @@ impl Metrics { } #[derive(Clone, Debug)] +/// Replication time information pub struct ReplicationTime { pub time_minutes: Option, pub status: bool, @@ -1467,6 +1514,7 @@ impl ReplicationTime { } #[derive(Clone, Debug)] +/// Destination information pub struct Destination { pub bucket_arn: String, pub access_control_translation: Option, @@ -1601,11 +1649,13 @@ impl Destination { } #[derive(Clone, Debug)] +/// Source selection criteria information pub struct SourceSelectionCriteria { pub sse_kms_encrypted_objects_status: Option, } #[derive(Clone, Debug)] +/// Replication rule information pub struct ReplicationRule { pub destination: Destination, pub delete_marker_replication_status: Option, @@ -1750,6 +1800,7 @@ impl ReplicationRule { } #[derive(Clone, Debug)] +/// Replication configuration information pub struct ReplicationConfig { pub role: Option, pub rules: Vec, @@ -1794,6 +1845,7 @@ impl ReplicationConfig { } #[derive(Clone, Debug)] +/// Object lock configuration information pub struct ObjectLockConfig { pub retention_mode: Option, pub retention_duration_days: Option, diff --git a/src/s3/utils.rs b/src/s3/utils.rs index 4e3840b..7d49093 100644 --- a/src/s3/utils.rs +++ b/src/s3/utils.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Various utility and helper functions + use crate::s3::error::Error; use base64::engine::general_purpose::STANDARD as BASE64; use base64::engine::Engine as _; @@ -29,14 +31,18 @@ pub use urlencoding::decode as urldecode; pub use urlencoding::encode as urlencode; use xmltree::Element; +/// Date and time with UTC timezone pub type UtcTime = DateTime; +/// Multimap for string key and string value pub type Multimap = MultiMap; +/// Encodes data using base64 algorithm pub fn b64encode>(input: T) -> String { BASE64.encode(input) } +/// Merges two multimaps. pub fn merge(m1: &mut Multimap, m2: &Multimap) { for (key, values) in m2.iter_all() { for value in values { @@ -45,36 +51,44 @@ pub fn merge(m1: &mut Multimap, m2: &Multimap) { } } +/// Computes CRC32 of given data. pub fn crc32(data: &[u8]) -> u32 { Crc::::new(&CRC_32_ISO_HDLC).checksum(data) } +/// Converts data array into 32 bit unsigned int pub fn uint32(mut data: &[u8]) -> Result { data.read_u32::() } +/// Gets hex encoded SHA256 hash of given data pub fn sha256_hash(data: &[u8]) -> String { let mut hasher = Sha256::new(); hasher.update(data); format!("{:x}", hasher.finalize()) } +/// Gets bas64 encoded MD5 hash of given data pub fn md5sum_hash(data: &[u8]) -> String { b64encode(md5compute(data).as_slice()) } +/// Gets current UTC time pub fn utc_now() -> UtcTime { chrono::offset::Utc::now() } +/// Gets signer date value of given time pub fn to_signer_date(time: UtcTime) -> String { time.format("%Y%m%d").to_string() } +/// Gets AMZ date value of given time pub fn to_amz_date(time: UtcTime) -> String { time.format("%Y%m%dT%H%M%SZ").to_string() } +/// Gets HTTP header value of given time pub fn to_http_header_value(time: UtcTime) -> String { format!( "{}, {} {} {} GMT", @@ -99,10 +113,12 @@ pub fn to_http_header_value(time: UtcTime) -> String { ) } +/// Gets ISO8601 UTC formatted value of given time pub fn to_iso8601utc(time: UtcTime) -> String { time.format("%Y-%m-%dT%H:%M:%S.%3fZ").to_string() } +/// Parses ISO8601 UTC formatted value to time pub fn from_iso8601utc(s: &str) -> Result { Ok(DateTime::::from_naive_utc_and_offset( match NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ") { @@ -113,6 +129,7 @@ pub fn from_iso8601utc(s: &str) -> Result { )) } +/// Parses HTTP header value to time pub fn from_http_header_value(s: &str) -> Result { Ok(DateTime::::from_naive_utc_and_offset( NaiveDateTime::parse_from_str(s, "%a, %d %b %Y %H:%M:%S GMT")?, @@ -120,6 +137,7 @@ pub fn from_http_header_value(s: &str) -> Result { )) } +/// Converts multimap to HTTP headers pub fn to_http_headers(map: &Multimap) -> Vec { let mut headers: Vec = Vec::new(); for (key, values) in map.iter_all() { @@ -134,6 +152,7 @@ pub fn to_http_headers(map: &Multimap) -> Vec { headers } +/// Converts multimap to HTTP query string pub fn to_query_string(map: &Multimap) -> String { let mut query = String::new(); for (key, values) in map.iter_all() { @@ -149,6 +168,7 @@ pub fn to_query_string(map: &Multimap) -> String { query } +/// Converts multimap to canonical query string pub fn get_canonical_query_string(map: &Multimap) -> String { let mut keys: Vec = Vec::new(); for (key, _) in map.iter() { @@ -176,6 +196,7 @@ pub fn get_canonical_query_string(map: &Multimap) -> String { query } +/// Converts multimap to signed headers and canonical headers pub fn get_canonical_headers(map: &Multimap) -> (String, String) { lazy_static! { static ref MULTI_SPACE_REGEX: Regex = Regex::new("( +)").unwrap(); @@ -223,6 +244,7 @@ pub fn get_canonical_headers(map: &Multimap) -> (String, String) { (signed_headers, canonical_headers) } +/// Validates given bucket name pub fn check_bucket_name(bucket_name: &str, strict: bool) -> Result<(), Error> { if bucket_name.trim().is_empty() { return Err(Error::InvalidBucketName(String::from( @@ -277,6 +299,7 @@ pub fn check_bucket_name(bucket_name: &str, strict: bool) -> Result<(), Error> { Ok(()) } +/// Gets text value of given XML element for given tag. pub fn get_text(element: &Element, tag: &str) -> Result { Ok(element .get_child(tag) @@ -286,6 +309,7 @@ pub fn get_text(element: &Element, tag: &str) -> Result { .to_string()) } +/// Gets optional text value of given XML element for given tag. pub fn get_option_text(element: &Element, tag: &str) -> Option { if let Some(v) = element.get_child(tag) { return Some(v.get_text().unwrap_or_default().to_string()); @@ -294,12 +318,14 @@ pub fn get_option_text(element: &Element, tag: &str) -> Option { None } +/// Gets default text value of given XML element for given tag. pub fn get_default_text(element: &Element, tag: &str) -> String { element.get_child(tag).map_or(String::new(), |v| { v.get_text().unwrap_or_default().to_string() }) } +/// Copies source byte slice into destination byte slice pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize { let mut c = 0; for (d, s) in dst.iter_mut().zip(src.iter()) { diff --git a/tests/tests.rs b/tests/tests.rs index 9166dd4..13c2f06 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -567,7 +567,7 @@ impl<'a> ClientTest<'_> { }; let spawned_task = task::spawn(listen_task()); - task::sleep(std::time::Duration::from_millis(100)).await; + task::sleep(std::time::Duration::from_millis(200)).await; let size = 16_usize; self.client @@ -1059,10 +1059,14 @@ impl<'a> ClientTest<'_> { .await .unwrap(); - let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name).unwrap(); - args.retention_mode = Some(RetentionMode::GOVERNANCE); let retain_until_date = utc_now() + Duration::days(1); - args.retain_until_date = Some(retain_until_date); + let args = SetObjectRetentionArgs::new( + &bucket_name, + &object_name, + Some(RetentionMode::GOVERNANCE), + Some(retain_until_date), + ) + .unwrap(); self.client.set_object_retention(&args).await.unwrap(); @@ -1080,7 +1084,7 @@ impl<'a> ClientTest<'_> { _ => false, },); - let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name).unwrap(); + let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name, None, None).unwrap(); args.bypass_governance_mode = true; self.client.set_object_retention(&args).await.unwrap();