This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new b3a99819f Simplify ObjectStore configuration pattern (#4189)
b3a99819f is described below

commit b3a99819f0d0448ddd7b311a8bc5c9625f3544bd
Author: Raphael Taylor-Davies <1781103+tustv...@users.noreply.github.com>
AuthorDate: Wed May 10 14:51:19 2023 +0100

    Simplify ObjectStore configuration pattern (#4189)
---
 object_store/src/aws/checksum.rs |  17 ++++-
 object_store/src/aws/mod.rs      | 155 +++++++++++++++------------------------
 object_store/src/azure/mod.rs    | 107 ++++++++-------------------
 object_store/src/client/retry.rs |   6 +-
 object_store/src/gcp/mod.rs      | 112 +++++++++-------------------
 5 files changed, 139 insertions(+), 258 deletions(-)

diff --git a/object_store/src/aws/checksum.rs b/object_store/src/aws/checksum.rs
index c787c28a8..57762b641 100644
--- a/object_store/src/aws/checksum.rs
+++ b/object_store/src/aws/checksum.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 use ring::digest::{self, digest as ring_digest};
+use std::str::FromStr;
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -47,13 +48,21 @@ impl std::fmt::Display for Checksum {
     }
 }
 
-impl TryFrom<&String> for Checksum {
-    type Error = ();
+impl FromStr for Checksum {
+    type Err = ();
 
-    fn try_from(value: &String) -> Result<Self, Self::Error> {
-        match value.to_lowercase().as_str() {
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_lowercase().as_str() {
             "sha256" => Ok(Self::SHA256),
             _ => Err(()),
         }
     }
 }
+
+impl TryFrom<&String> for Checksum {
+    type Error = ();
+
+    fn try_from(value: &String) -> Result<Self, Self::Error> {
+        value.parse()
+    }
+}
diff --git a/object_store/src/aws/mod.rs b/object_store/src/aws/mod.rs
index bc852ed48..5de177afa 100644
--- a/object_store/src/aws/mod.rs
+++ b/object_store/src/aws/mod.rs
@@ -467,7 +467,7 @@ pub struct AmazonS3Builder {
     /// When set to true, unsigned payload option has to be used
     unsigned_payload: bool,
     /// Checksum algorithm which has to be used for object integrity check 
during upload
-    checksum_algorithm: Option<Checksum>,
+    checksum_algorithm: Option<String>,
     /// Metadata endpoint, see 
<https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html>
     metadata_endpoint: Option<String>,
     /// Profile name, see 
<https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html>
@@ -478,30 +478,17 @@ pub struct AmazonS3Builder {
 
 /// Configuration keys for [`AmazonS3Builder`]
 ///
-/// Configuration via keys can be dome via the 
[`try_with_option`](AmazonS3Builder::try_with_option)
-/// or [`with_options`](AmazonS3Builder::try_with_options) methods on the 
builder.
+/// Configuration via keys can be done via [`AmazonS3Builder::with_config`]
 ///
 /// # Example
 /// ```
-/// use std::collections::HashMap;
-/// use object_store::aws::{AmazonS3Builder, AmazonS3ConfigKey};
-///
-/// let options = HashMap::from([
-///     ("aws_access_key_id", "my-access-key-id"),
-///     ("aws_secret_access_key", "my-secret-access-key"),
-/// ]);
-/// let typed_options = vec![
-///     (AmazonS3ConfigKey::DefaultRegion, "my-default-region"),
-/// ];
-/// let aws = AmazonS3Builder::new()
-///     .try_with_options(options)
-///     .unwrap()
-///     .try_with_options(typed_options)
-///     .unwrap()
-///     .try_with_option(AmazonS3ConfigKey::Region, "my-region")
-///     .unwrap();
+/// # use object_store::aws::{AmazonS3Builder, AmazonS3ConfigKey};
+/// let builder = AmazonS3Builder::new()
+///     .with_config("aws_access_key_id".parse().unwrap(), "my-access-key-id")
+///     .with_config(AmazonS3ConfigKey::DefaultRegion, "my-default-region");
 /// ```
 #[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, Serialize, Deserialize)]
+#[non_exhaustive]
 pub enum AmazonS3ConfigKey {
     /// AWS Access Key
     ///
@@ -706,7 +693,7 @@ impl AmazonS3Builder {
                     if let Ok(config_key) =
                         AmazonS3ConfigKey::from_str(&key.to_ascii_lowercase())
                     {
-                        builder = builder.try_with_option(config_key, 
value).unwrap();
+                        builder = builder.with_config(config_key, value);
                     }
                 }
             }
@@ -754,14 +741,12 @@ impl AmazonS3Builder {
     }
 
     /// Set an option on the builder via a key - value pair.
-    ///
-    /// This method will return an `UnknownConfigKey` error if key cannot be 
parsed into [`AmazonS3ConfigKey`].
-    pub fn try_with_option(
+    pub fn with_config(
         mut self,
-        key: impl AsRef<str>,
+        key: AmazonS3ConfigKey,
         value: impl Into<String>,
-    ) -> Result<Self> {
-        match AmazonS3ConfigKey::from_str(key.as_ref())? {
+    ) -> Self {
+        match key {
             AmazonS3ConfigKey::AccessKeyId => self.access_key_id = 
Some(value.into()),
             AmazonS3ConfigKey::SecretAccessKey => {
                 self.secret_access_key = Some(value.into())
@@ -786,18 +771,28 @@ impl AmazonS3Builder {
             AmazonS3ConfigKey::UnsignedPayload => {
                 self.unsigned_payload = str_is_truthy(&value.into())
             }
-            AmazonS3ConfigKey::Checksum => {
-                let algorithm = Checksum::try_from(&value.into())
-                    .map_err(|_| Error::InvalidChecksumAlgorithm)?;
-                self.checksum_algorithm = Some(algorithm)
-            }
+            AmazonS3ConfigKey::Checksum => self.checksum_algorithm = 
Some(value.into()),
         };
-        Ok(self)
+        self
+    }
+
+    /// Set an option on the builder via a key - value pair.
+    ///
+    /// This method will return an `UnknownConfigKey` error if key cannot be 
parsed into [`AmazonS3ConfigKey`].
+    #[deprecated(note = "Use with_config")]
+    pub fn try_with_option(
+        self,
+        key: impl AsRef<str>,
+        value: impl Into<String>,
+    ) -> Result<Self> {
+        Ok(self.with_config(key.as_ref().parse()?, value))
     }
 
     /// Hydrate builder from key value pairs
     ///
     /// This method will return an `UnknownConfigKey` error if any key cannot 
be parsed into [`AmazonS3ConfigKey`].
+    #[deprecated(note = "Use with_config")]
+    #[allow(deprecated)]
     pub fn try_with_options<
         I: IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
     >(
@@ -838,7 +833,7 @@ impl AmazonS3Builder {
             AmazonS3ConfigKey::MetadataEndpoint => 
self.metadata_endpoint.clone(),
             AmazonS3ConfigKey::Profile => self.profile.clone(),
             AmazonS3ConfigKey::UnsignedPayload => 
Some(self.unsigned_payload.to_string()),
-            AmazonS3ConfigKey::Checksum => self.checksum_algorithm.map(|v| 
v.to_string()),
+            AmazonS3ConfigKey::Checksum => self.checksum_algorithm.clone(),
         }
     }
 
@@ -979,7 +974,8 @@ impl AmazonS3Builder {
     ///
     /// [checksum algorithm]: 
https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html
     pub fn with_checksum_algorithm(mut self, checksum_algorithm: Checksum) -> 
Self {
-        self.checksum_algorithm = Some(checksum_algorithm);
+        // Convert to String to enable deferred parsing of config
+        self.checksum_algorithm = Some(checksum_algorithm.to_string());
         self
     }
 
@@ -1032,6 +1028,11 @@ impl AmazonS3Builder {
 
         let bucket = self.bucket_name.context(MissingBucketNameSnafu)?;
         let region = self.region.context(MissingRegionSnafu)?;
+        let checksum = self
+            .checksum_algorithm
+            .map(|c| c.parse())
+            .transpose()
+            .map_err(|_| Error::InvalidChecksumAlgorithm)?;
 
         let credentials = match (self.access_key_id, self.secret_access_key, 
self.token) {
             (Some(key_id), Some(secret_key), token) => {
@@ -1129,7 +1130,7 @@ impl AmazonS3Builder {
             retry_config: self.retry_config,
             client_options: self.client_options,
             sign_payload: !self.unsigned_payload,
-            checksum: self.checksum_algorithm,
+            checksum,
         };
 
         let client = Arc::new(S3Client::new(config)?);
@@ -1303,7 +1304,10 @@ mod tests {
         assert_eq!(builder.token.unwrap(), aws_session_token);
         let metadata_uri = 
format!("{METADATA_ENDPOINT}{container_creds_relative_uri}");
         assert_eq!(builder.metadata_endpoint.unwrap(), metadata_uri);
-        assert_eq!(builder.checksum_algorithm.unwrap(), Checksum::SHA256);
+        assert_eq!(
+            builder.checksum_algorithm.unwrap(),
+            Checksum::SHA256.to_string()
+        );
         assert!(builder.unsigned_payload);
     }
 
@@ -1324,46 +1328,22 @@ mod tests {
             ("aws_checksum_algorithm", "sha256".to_string()),
         ]);
 
-        let builder = AmazonS3Builder::new()
-            .try_with_options(&options)
-            .unwrap()
-            .try_with_option("aws_secret_access_key", "new-secret-key")
-            .unwrap();
-        assert_eq!(builder.access_key_id.unwrap(), aws_access_key_id.as_str());
-        assert_eq!(builder.secret_access_key.unwrap(), "new-secret-key");
-        assert_eq!(builder.region.unwrap(), aws_default_region);
-        assert_eq!(builder.endpoint.unwrap(), aws_endpoint);
-        assert_eq!(builder.token.unwrap(), aws_session_token);
-        assert_eq!(builder.checksum_algorithm.unwrap(), Checksum::SHA256);
-        assert!(builder.unsigned_payload);
-    }
-
-    #[test]
-    fn s3_test_config_from_typed_map() {
-        let aws_access_key_id = "object_store:fake_access_key_id".to_string();
-        let aws_secret_access_key = "object_store:fake_secret_key".to_string();
-        let aws_default_region = 
"object_store:fake_default_region".to_string();
-        let aws_endpoint = "object_store:fake_endpoint".to_string();
-        let aws_session_token = "object_store:fake_session_token".to_string();
-        let options = HashMap::from([
-            (AmazonS3ConfigKey::AccessKeyId, aws_access_key_id.clone()),
-            (AmazonS3ConfigKey::SecretAccessKey, aws_secret_access_key),
-            (AmazonS3ConfigKey::DefaultRegion, aws_default_region.clone()),
-            (AmazonS3ConfigKey::Endpoint, aws_endpoint.clone()),
-            (AmazonS3ConfigKey::Token, aws_session_token.clone()),
-            (AmazonS3ConfigKey::UnsignedPayload, "true".to_string()),
-        ]);
+        let builder = options
+            .into_iter()
+            .fold(AmazonS3Builder::new(), |builder, (key, value)| {
+                builder.with_config(key.parse().unwrap(), value)
+            })
+            .with_config(AmazonS3ConfigKey::SecretAccessKey, "new-secret-key");
 
-        let builder = AmazonS3Builder::new()
-            .try_with_options(&options)
-            .unwrap()
-            .try_with_option(AmazonS3ConfigKey::SecretAccessKey, 
"new-secret-key")
-            .unwrap();
         assert_eq!(builder.access_key_id.unwrap(), aws_access_key_id.as_str());
         assert_eq!(builder.secret_access_key.unwrap(), "new-secret-key");
         assert_eq!(builder.region.unwrap(), aws_default_region);
         assert_eq!(builder.endpoint.unwrap(), aws_endpoint);
         assert_eq!(builder.token.unwrap(), aws_session_token);
+        assert_eq!(
+            builder.checksum_algorithm.unwrap(),
+            Checksum::SHA256.to_string()
+        );
         assert!(builder.unsigned_payload);
     }
 
@@ -1374,19 +1354,15 @@ mod tests {
         let aws_default_region = 
"object_store:fake_default_region".to_string();
         let aws_endpoint = "object_store:fake_endpoint".to_string();
         let aws_session_token = "object_store:fake_session_token".to_string();
-        let options = HashMap::from([
-            (AmazonS3ConfigKey::AccessKeyId, aws_access_key_id.clone()),
-            (
-                AmazonS3ConfigKey::SecretAccessKey,
-                aws_secret_access_key.clone(),
-            ),
-            (AmazonS3ConfigKey::DefaultRegion, aws_default_region.clone()),
-            (AmazonS3ConfigKey::Endpoint, aws_endpoint.clone()),
-            (AmazonS3ConfigKey::Token, aws_session_token.clone()),
-            (AmazonS3ConfigKey::UnsignedPayload, "true".to_string()),
-        ]);
 
-        let builder = 
AmazonS3Builder::new().try_with_options(&options).unwrap();
+        let builder = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::AccessKeyId, &aws_access_key_id)
+            .with_config(AmazonS3ConfigKey::SecretAccessKey, 
&aws_secret_access_key)
+            .with_config(AmazonS3ConfigKey::DefaultRegion, &aws_default_region)
+            .with_config(AmazonS3ConfigKey::Endpoint, &aws_endpoint)
+            .with_config(AmazonS3ConfigKey::Token, &aws_session_token)
+            .with_config(AmazonS3ConfigKey::UnsignedPayload, "true");
+
         assert_eq!(
             builder
                 .get_config_value(&AmazonS3ConfigKey::AccessKeyId)
@@ -1423,19 +1399,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn s3_test_config_fallible_options() {
-        let aws_access_key_id = "object_store:fake_access_key_id".to_string();
-        let aws_secret_access_key = "object_store:fake_secret_key".to_string();
-        let options = HashMap::from([
-            ("aws_access_key_id", aws_access_key_id),
-            ("invalid-key", aws_secret_access_key),
-        ]);
-
-        let builder = AmazonS3Builder::new().try_with_options(&options);
-        assert!(builder.is_err());
-    }
-
     #[tokio::test]
     async fn s3_test() {
         let config = maybe_skip_integration!();
diff --git a/object_store/src/azure/mod.rs b/object_store/src/azure/mod.rs
index ddfd02820..15033dca7 100644
--- a/object_store/src/azure/mod.rs
+++ b/object_store/src/azure/mod.rs
@@ -436,30 +436,17 @@ pub struct MicrosoftAzureBuilder {
 
 /// Configuration keys for [`MicrosoftAzureBuilder`]
 ///
-/// Configuration via keys can be dome via the 
[`try_with_option`](MicrosoftAzureBuilder::try_with_option)
-/// or [`with_options`](MicrosoftAzureBuilder::try_with_options) methods on 
the builder.
+/// Configuration via keys can be done via 
[`MicrosoftAzureBuilder::with_config`]
 ///
 /// # Example
 /// ```
-/// use std::collections::HashMap;
-/// use object_store::azure::{MicrosoftAzureBuilder, AzureConfigKey};
-///
-/// let options = HashMap::from([
-///     ("azure_client_id", "my-client-id"),
-///     ("azure_client_secret", "my-account-name"),
-/// ]);
-/// let typed_options = vec![
-///     (AzureConfigKey::AccountName, "my-account-name"),
-/// ];
-/// let azure = MicrosoftAzureBuilder::new()
-///     .try_with_options(options)
-///     .unwrap()
-///     .try_with_options(typed_options)
-///     .unwrap()
-///     .try_with_option(AzureConfigKey::AuthorityId, "my-tenant-id")
-///     .unwrap();
+/// # use object_store::azure::{MicrosoftAzureBuilder, AzureConfigKey};
+/// let builder = MicrosoftAzureBuilder::new()
+///     .with_config("azure_client_id".parse().unwrap(), "my-client-id")
+///     .with_config(AzureConfigKey::AuthorityId, "my-tenant-id");
 /// ```
 #[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, Deserialize, Serialize)]
+#[non_exhaustive]
 pub enum AzureConfigKey {
     /// The name of the azure storage account
     ///
@@ -678,7 +665,7 @@ impl MicrosoftAzureBuilder {
                     if let Ok(config_key) =
                         AzureConfigKey::from_str(&key.to_ascii_lowercase())
                     {
-                        builder = builder.try_with_option(config_key, 
value).unwrap();
+                        builder = builder.with_config(config_key, value);
                     }
                 }
             }
@@ -724,12 +711,8 @@ impl MicrosoftAzureBuilder {
     }
 
     /// Set an option on the builder via a key - value pair.
-    pub fn try_with_option(
-        mut self,
-        key: impl AsRef<str>,
-        value: impl Into<String>,
-    ) -> Result<Self> {
-        match AzureConfigKey::from_str(key.as_ref())? {
+    pub fn with_config(mut self, key: AzureConfigKey, value: impl 
Into<String>) -> Self {
+        match key {
             AzureConfigKey::AccessKey => self.access_key = Some(value.into()),
             AzureConfigKey::AccountName => self.account_name = 
Some(value.into()),
             AzureConfigKey::ClientId => self.client_id = Some(value.into()),
@@ -750,10 +733,22 @@ impl MicrosoftAzureBuilder {
                 self.use_emulator = str_is_truthy(&value.into())
             }
         };
-        Ok(self)
+        self
+    }
+
+    /// Set an option on the builder via a key - value pair.
+    #[deprecated(note = "Use with_config")]
+    pub fn try_with_option(
+        self,
+        key: impl AsRef<str>,
+        value: impl Into<String>,
+    ) -> Result<Self> {
+        Ok(self.with_config(key.as_ref().parse()?, value))
     }
 
     /// Hydrate builder from key value pairs
+    #[deprecated(note = "Use with_config")]
+    #[allow(deprecated)]
     pub fn try_with_options<
         I: IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
     >(
@@ -1270,31 +1265,11 @@ mod tests {
             ("azure_storage_token", azure_storage_token),
         ]);
 
-        let builder = MicrosoftAzureBuilder::new()
-            .try_with_options(options)
-            .unwrap();
-        assert_eq!(builder.client_id.unwrap(), azure_client_id);
-        assert_eq!(builder.account_name.unwrap(), azure_storage_account_name);
-        assert_eq!(builder.bearer_token.unwrap(), azure_storage_token);
-    }
-
-    #[test]
-    fn azure_test_config_from_typed_map() {
-        let azure_client_id = "object_store:fake_access_key_id".to_string();
-        let azure_storage_account_name = 
"object_store:fake_secret_key".to_string();
-        let azure_storage_token = 
"object_store:fake_default_region".to_string();
-        let options = HashMap::from([
-            (AzureConfigKey::ClientId, azure_client_id.clone()),
-            (
-                AzureConfigKey::AccountName,
-                azure_storage_account_name.clone(),
-            ),
-            (AzureConfigKey::Token, azure_storage_token.clone()),
-        ]);
-
-        let builder = MicrosoftAzureBuilder::new()
-            .try_with_options(&options)
-            .unwrap();
+        let builder = options
+            .into_iter()
+            .fold(MicrosoftAzureBuilder::new(), |builder, (key, value)| {
+                builder.with_config(key.parse().unwrap(), value)
+            });
         assert_eq!(builder.client_id.unwrap(), azure_client_id);
         assert_eq!(builder.account_name.unwrap(), azure_storage_account_name);
         assert_eq!(builder.bearer_token.unwrap(), azure_storage_token);
@@ -1305,18 +1280,11 @@ mod tests {
         let azure_client_id = "object_store:fake_access_key_id".to_string();
         let azure_storage_account_name = 
"object_store:fake_secret_key".to_string();
         let azure_storage_token = 
"object_store:fake_default_region".to_string();
-        let options = HashMap::from([
-            (AzureConfigKey::ClientId, azure_client_id.clone()),
-            (
-                AzureConfigKey::AccountName,
-                azure_storage_account_name.clone(),
-            ),
-            (AzureConfigKey::Token, azure_storage_token.clone()),
-        ]);
-
         let builder = MicrosoftAzureBuilder::new()
-            .try_with_options(&options)
-            .unwrap();
+            .with_config(AzureConfigKey::ClientId, &azure_client_id)
+            .with_config(AzureConfigKey::AccountName, 
&azure_storage_account_name)
+            .with_config(AzureConfigKey::Token, &azure_storage_token);
+
         assert_eq!(
             builder.get_config_value(&AzureConfigKey::ClientId).unwrap(),
             azure_client_id
@@ -1333,19 +1301,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn azure_test_config_fallible_options() {
-        let azure_client_id = "object_store:fake_access_key_id".to_string();
-        let azure_storage_token = 
"object_store:fake_default_region".to_string();
-        let options = HashMap::from([
-            ("azure_client_id", azure_client_id),
-            ("invalid-key", azure_storage_token),
-        ]);
-
-        let builder = MicrosoftAzureBuilder::new().try_with_options(&options);
-        assert!(builder.is_err());
-    }
-
     #[test]
     fn azure_test_split_sas() {
         let raw_sas = 
"?sv=2021-10-04&st=2023-01-04T17%3A48%3A57Z&se=2023-01-04T18%3A15%3A00Z&sr=c&sp=rcwl&sig=C7%2BZeEOWbrxPA3R0Cw%2Fw1EZz0%2B4KBvQexeKZKe%2BB6h0%3D";
diff --git a/object_store/src/client/retry.rs b/object_store/src/client/retry.rs
index e6e92f086..f9c2dd300 100644
--- a/object_store/src/client/retry.rs
+++ b/object_store/src/client/retry.rs
@@ -22,9 +22,9 @@ use futures::future::BoxFuture;
 use futures::FutureExt;
 use reqwest::header::LOCATION;
 use reqwest::{Response, StatusCode};
+use snafu::Error as SnafuError;
 use std::time::{Duration, Instant};
 use tracing::info;
-use snafu::Error as SnafuError;
 
 /// Retry request error
 #[derive(Debug)]
@@ -365,13 +365,13 @@ mod tests {
         assert_eq!(e.message, "502 Bad Gateway");
 
         // Panic results in an incomplete message error in the client
-        mock.push_fn(|_| {panic!()});
+        mock.push_fn(|_| panic!());
         let r = do_request().await.unwrap();
         assert_eq!(r.status(), StatusCode::OK);
 
         // Gives up after retrying mulitiple panics
         for _ in 0..=retry.max_retries {
-            mock.push_fn(|_| {panic!()});
+            mock.push_fn(|_| panic!());
         }
         let e = do_request().await.unwrap_err();
         assert_eq!(e.retries, retry.max_retries);
diff --git a/object_store/src/gcp/mod.rs b/object_store/src/gcp/mod.rs
index a6cf66022..6f3d53d42 100644
--- a/object_store/src/gcp/mod.rs
+++ b/object_store/src/gcp/mod.rs
@@ -786,29 +786,17 @@ pub struct GoogleCloudStorageBuilder {
 
 /// Configuration keys for [`GoogleCloudStorageBuilder`]
 ///
-/// Configuration via keys can be done via the 
[`try_with_option`](GoogleCloudStorageBuilder::try_with_option)
-/// or [`try_with_options`](GoogleCloudStorageBuilder::try_with_options) 
methods on the builder.
+/// Configuration via keys can be done via 
[`GoogleCloudStorageBuilder::with_config`]
 ///
 /// # Example
 /// ```
-/// use std::collections::HashMap;
-/// use object_store::gcp::{GoogleCloudStorageBuilder, GoogleConfigKey};
-///
-/// let options = HashMap::from([
-///     ("google_service_account", "my-service-account"),
-/// ]);
-/// let typed_options = vec![
-///     (GoogleConfigKey::Bucket, "my-bucket"),
-/// ];
-/// let azure = GoogleCloudStorageBuilder::new()
-///     .try_with_options(options)
-///     .unwrap()
-///     .try_with_options(typed_options)
-///     .unwrap()
-///     .try_with_option(GoogleConfigKey::Bucket, "my-new-bucket")
-///     .unwrap();
+/// # use object_store::gcp::{GoogleCloudStorageBuilder, GoogleConfigKey};
+/// let builder = GoogleCloudStorageBuilder::new()
+///     .with_config("google_service_account".parse().unwrap(), 
"my-service-account")
+///     .with_config(GoogleConfigKey::Bucket, "my-bucket");
 /// ```
 #[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, Serialize, Deserialize)]
+#[non_exhaustive]
 pub enum GoogleConfigKey {
     /// Path to the service account file
     ///
@@ -926,7 +914,7 @@ impl GoogleCloudStorageBuilder {
                     if let Ok(config_key) =
                         GoogleConfigKey::from_str(&key.to_ascii_lowercase())
                     {
-                        builder = builder.try_with_option(config_key, 
value).unwrap();
+                        builder = builder.with_config(config_key, value);
                     }
                 }
             }
@@ -957,12 +945,8 @@ impl GoogleCloudStorageBuilder {
     }
 
     /// Set an option on the builder via a key - value pair.
-    pub fn try_with_option(
-        mut self,
-        key: impl AsRef<str>,
-        value: impl Into<String>,
-    ) -> Result<Self> {
-        match GoogleConfigKey::from_str(key.as_ref())? {
+    pub fn with_config(mut self, key: GoogleConfigKey, value: impl 
Into<String>) -> Self {
+        match key {
             GoogleConfigKey::ServiceAccount => {
                 self.service_account_path = Some(value.into())
             }
@@ -974,10 +958,22 @@ impl GoogleCloudStorageBuilder {
                 self.application_credentials_path = Some(value.into())
             }
         };
-        Ok(self)
+        self
+    }
+
+    /// Set an option on the builder via a key - value pair.
+    #[deprecated(note = "Use with_config")]
+    pub fn try_with_option(
+        self,
+        key: impl AsRef<str>,
+        value: impl Into<String>,
+    ) -> Result<Self> {
+        Ok(self.with_config(key.as_ref().parse()?, value))
     }
 
     /// Hydrate builder from key value pairs
+    #[deprecated(note = "Use with_config")]
+    #[allow(deprecated)]
     pub fn try_with_options<
         I: IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
     >(
@@ -1449,31 +1445,12 @@ mod test {
             ("google_bucket_name", google_bucket_name.clone()),
         ]);
 
-        let builder = GoogleCloudStorageBuilder::new()
-            .try_with_options(&options)
-            .unwrap();
-        assert_eq!(
-            builder.service_account_path.unwrap(),
-            google_service_account.as_str()
-        );
-        assert_eq!(builder.bucket_name.unwrap(), google_bucket_name.as_str());
-    }
+        let builder = options
+            .iter()
+            .fold(GoogleCloudStorageBuilder::new(), |builder, (key, value)| {
+                builder.with_config(key.parse().unwrap(), value)
+            });
 
-    #[test]
-    fn gcs_test_config_from_typed_map() {
-        let google_service_account = 
"object_store:fake_service_account".to_string();
-        let google_bucket_name = "object_store:fake_bucket".to_string();
-        let options = HashMap::from([
-            (
-                GoogleConfigKey::ServiceAccount,
-                google_service_account.clone(),
-            ),
-            (GoogleConfigKey::Bucket, google_bucket_name.clone()),
-        ]);
-
-        let builder = GoogleCloudStorageBuilder::new()
-            .try_with_options(&options)
-            .unwrap();
         assert_eq!(
             builder.service_account_path.unwrap(),
             google_service_account.as_str()
@@ -1485,17 +1462,10 @@ mod test {
     fn gcs_test_config_get_value() {
         let google_service_account = 
"object_store:fake_service_account".to_string();
         let google_bucket_name = "object_store:fake_bucket".to_string();
-        let options = HashMap::from([
-            (
-                GoogleConfigKey::ServiceAccount,
-                google_service_account.clone(),
-            ),
-            (GoogleConfigKey::Bucket, google_bucket_name.clone()),
-        ]);
-
         let builder = GoogleCloudStorageBuilder::new()
-            .try_with_options(&options)
-            .unwrap();
+            .with_config(GoogleConfigKey::ServiceAccount, 
&google_service_account)
+            .with_config(GoogleConfigKey::Bucket, &google_bucket_name);
+
         assert_eq!(
             builder
                 .get_config_value(&GoogleConfigKey::ServiceAccount)
@@ -1508,19 +1478,6 @@ mod test {
         );
     }
 
-    #[test]
-    fn gcs_test_config_fallible_options() {
-        let google_service_account = 
"object_store:fake_service_account".to_string();
-        let google_bucket_name = "object_store:fake_bucket".to_string();
-        let options = HashMap::from([
-            ("google_service_account", google_service_account),
-            ("invalid-key", google_bucket_name),
-        ]);
-
-        let builder = 
GoogleCloudStorageBuilder::new().try_with_options(&options);
-        assert!(builder.is_err());
-    }
-
     #[test]
     fn gcs_test_config_aliases() {
         // Service account path
@@ -1531,16 +1488,14 @@ mod test {
             "service_account_path",
         ] {
             let builder = GoogleCloudStorageBuilder::new()
-                .try_with_options([(alias, "/fake/path.json")])
-                .unwrap();
+                .with_config(alias.parse().unwrap(), "/fake/path.json");
             assert_eq!("/fake/path.json", 
builder.service_account_path.unwrap());
         }
 
         // Service account key
         for alias in ["google_service_account_key", "service_account_key"] {
             let builder = GoogleCloudStorageBuilder::new()
-                .try_with_options([(alias, FAKE_KEY)])
-                .unwrap();
+                .with_config(alias.parse().unwrap(), FAKE_KEY);
             assert_eq!(FAKE_KEY, builder.service_account_key.unwrap());
         }
 
@@ -1552,8 +1507,7 @@ mod test {
             "bucket_name",
         ] {
             let builder = GoogleCloudStorageBuilder::new()
-                .try_with_options([(alias, "fake_bucket")])
-                .unwrap();
+                .with_config(alias.parse().unwrap(), "fake_bucket");
             assert_eq!("fake_bucket", builder.bucket_name.unwrap());
         }
     }

Reply via email to