diff --git a/README.md b/README.md index 121eed8..0fe4a08 100644 --- a/README.md +++ b/README.md @@ -5,64 +5,9 @@ MinIO Rust SDK is Simple Storage Service (aka S3) client to perform bucket and o For a complete list of APIs and examples, please take a look at the [MinIO Rust Client API Reference](https://minio-rs.min.io/) ## Example:: file-uploader.rs -```rust -use minio::s3::args::{BucketExistsArgs, MakeBucketArgs, UploadObjectArgs}; -use minio::s3::client::Client; -use minio::s3::creds::StaticProvider; -use minio::s3::http::BaseUrl; -#[tokio::main] -async fn main() -> Result<(), Box> { - let base_url = BaseUrl::from_string("https://play.min.io".to_string()).unwrap(); +[Upload a file to MinIO](examples/file-uploader.rs) - let static_provider = StaticProvider::new( - "Q3AM3UQ867SPQQA43P2F", - "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", - None, - ); - - let client = Client::new( - base_url.clone(), - Some(Box::new(static_provider)), - None, - None, - ) - .unwrap(); - - let bucket_name = "asiatrip"; - - // Check 'asiatrip' bucket exist or not. - let exists = client - .bucket_exists(&BucketExistsArgs::new(&bucket_name).unwrap()) - .await - .unwrap(); - - // Make 'asiatrip' bucket if not exist. - if !exists { - client - .make_bucket(&MakeBucketArgs::new(&bucket_name).unwrap()) - .await - .unwrap(); - } - - // Upload '/home/user/Photos/asiaphotos.zip' as object name - // 'asiaphotos-2015.zip' to bucket 'asiatrip'. - client - .upload_object( - &mut UploadObjectArgs::new( - &bucket_name, - "asiaphotos-2015.zip", - "/home/user/Photos/asiaphotos.zip", - ) - .unwrap(), - ) - .await - .unwrap(); - - println!("'/home/user/Photos/asiaphotos.zip' is successfully uploaded as object 'asiaphotos-2015.zip' to bucket 'asiatrip'."); - Ok(()) -} -``` ## License This SDK is distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), see [LICENSE](https://github.com/minio/minio-rs/blob/master/LICENSE) for more information. diff --git a/examples/file-uploader.rs b/examples/file-uploader.rs index dd2cf78..fe1b519 100644 --- a/examples/file-uploader.rs +++ b/examples/file-uploader.rs @@ -5,7 +5,7 @@ use minio::s3::http::BaseUrl; #[tokio::main] async fn main() -> Result<(), Box> { - let base_url = BaseUrl::from_string("https://play.min.io".to_string()).unwrap(); + let base_url = "https://play.min.io".parse::()?; let static_provider = StaticProvider::new( "Q3AM3UQ867SPQQA43P2F", diff --git a/src/s3/client.rs b/src/s3/client.rs index 1ff76c5..4219dc1 100644 --- a/src/s3/client.rs +++ b/src/s3/client.rs @@ -224,7 +224,7 @@ impl Client { /// 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 mut base_url: BaseUrl = "play.min.io".parse().unwrap(); /// let static_provider = StaticProvider::new( /// "Q3AM3UQ867SPQQA43P2F", /// "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", diff --git a/src/s3/http.rs b/src/s3/http.rs index 56567b5..008c9bf 100644 --- a/src/s3/http.rs +++ b/src/s3/http.rs @@ -24,6 +24,7 @@ use hyper::Uri; use lazy_static::lazy_static; use regex::Regex; use std::fmt; +use std::str::FromStr; const AWS_S3_PREFIX: &str = r"^(((bucket\.|accesspoint\.)vpce(-[a-z_\d]+)+\.s3\.)|([a-z_\d-]{1,63}\.)s3-control(-[a-z_\d]+)*\.|(s3(-[a-z_\d]+)*\.))"; @@ -217,6 +218,107 @@ pub struct BaseUrl { pub virtual_style: bool, } +impl FromStr for BaseUrl { + type Err = Error; + + /// Convert a string to a BaseUrl. + /// + /// Enables use of [`str`]'s [`parse`] method to create a [`BaseUrl`]. + /// + /// # Examples + /// + /// ``` + /// use minio::s3::http::BaseUrl; + /// use std::str::FromStr; + /// + /// // Get base URL from host name + /// let base_url = "play.min.io".parse::().unwrap(); + /// let base_url = BaseUrl::from_str("play.min.io").unwrap(); + /// // Get base URL from host:port + /// let base_url: BaseUrl = "play.minio.io:9000".parse().unwrap(); + /// // Get base URL from IPv4 address + /// let base_url: BaseUrl = "http://192.168.124.63:9000".parse().unwrap(); + /// // Get base URL from IPv6 address + /// let base_url: BaseUrl = "[0:0:0:0:0:ffff:c0a8:7c3f]:9000".parse().unwrap(); + /// ``` + fn from_str(s: &str) -> Result { + let url = s.parse::()?; + + let https = match url.scheme() { + None => true, + Some(scheme) => match scheme.as_str() { + "http" => false, + "https" => true, + _ => { + return Err(Error::InvalidBaseUrl(String::from( + "scheme must be http or https", + ))) + } + }, + }; + + let mut host = match url.host() { + Some(h) => h, + _ => { + return Err(Error::InvalidBaseUrl(String::from( + "valid host must be provided", + ))) + } + }; + + let ipv6host = "[".to_string() + host + "]"; + if host.parse::().is_ok() { + host = &ipv6host; + } + + let mut port = match url.port() { + Some(p) => p.as_u16(), + _ => 0u16, + }; + + if (https && port == 443) || (!https && port == 80) { + port = 0u16; + } + + if url.path() != "/" && url.path() != "" { + return Err(Error::InvalidBaseUrl(String::from( + "path must be empty for base URL", + ))); + } + + if url.query().is_some() { + return Err(Error::InvalidBaseUrl(String::from( + "query must be none for base URL", + ))); + } + + let mut region = String::new(); + let mut aws_s3_prefix = String::new(); + let mut aws_domain_suffix = String::new(); + let mut dualstack: bool = false; + get_aws_info( + &host.to_string(), + https, + &mut region, + &mut aws_s3_prefix, + &mut aws_domain_suffix, + &mut dualstack, + )?; + let virtual_style = !aws_domain_suffix.is_empty() || host.ends_with("aliyuncs.com"); + + Ok(BaseUrl { + https, + host: host.to_string(), + port, + region, + aws_s3_prefix, + aws_domain_suffix, + dualstack, + virtual_style, + }) + } +} + impl BaseUrl { /// Checks base URL is AWS host pub fn is_aws_host(&self) -> bool { @@ -363,96 +465,4 @@ 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::()?; - - let https = match url.scheme() { - None => true, - Some(scheme) => match scheme.as_str() { - "http" => false, - "https" => true, - _ => { - return Err(Error::InvalidBaseUrl(String::from( - "scheme must be http or https", - ))) - } - }, - }; - - let mut host = match url.host() { - Some(h) => h, - _ => { - return Err(Error::InvalidBaseUrl(String::from( - "valid host must be provided", - ))) - } - }; - - let ipv6host = "[".to_string() + host + "]"; - if host.parse::().is_ok() { - host = &ipv6host; - } - - let mut port = match url.port() { - Some(p) => p.as_u16(), - _ => 0u16, - }; - - if (https && port == 443) || (!https && port == 80) { - port = 0u16; - } - - if url.path() != "/" && url.path() != "" { - return Err(Error::InvalidBaseUrl(String::from( - "path must be empty for base URL", - ))); - } - - if url.query().is_some() { - return Err(Error::InvalidBaseUrl(String::from( - "query must be none for base URL", - ))); - } - - let mut region = String::new(); - let mut aws_s3_prefix = String::new(); - let mut aws_domain_suffix = String::new(); - let mut dualstack: bool = false; - get_aws_info( - &host.to_string(), - https, - &mut region, - &mut aws_s3_prefix, - &mut aws_domain_suffix, - &mut dualstack, - )?; - let virtual_style = !aws_domain_suffix.is_empty() || host.ends_with("aliyuncs.com"); - - Ok(BaseUrl { - https, - host: host.to_string(), - port, - region, - aws_s3_prefix, - aws_domain_suffix, - dualstack, - virtual_style, - }) - } } diff --git a/tests/tests.rs b/tests/tests.rs index a3a0db2..25945b0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1155,7 +1155,7 @@ async fn s3_tests() -> Result<(), Box> { let ignore_cert_check = std::env::var("IGNORE_CERT_CHECK").is_ok(); let region = std::env::var("SERVER_REGION").ok(); - let mut base_url = BaseUrl::from_string(host).unwrap(); + let mut base_url: BaseUrl = host.parse().unwrap(); base_url.https = secure; if let Some(v) = region { base_url.region = v;