Repository: rusoto/rusoto (language: rust)

Issue:
Use lazy_static in rusoto_credential profile provider
TODO note here: https://github.com/rusoto/rusoto/blob/master/rusoto/credential/src/profile.rs#L259 .

Whenever we parse the credential file we create a parsing regex which is relatively expensive. We could instantiate it once so it's not recreated every time we parse the credentials file. This is important when we support https://github.com/rusoto/rusoto/issues/1303 .

See https://docs.rs/regex/1.3.1/regex/#example-avoid-compiling-the-same-regex-in-a-loop for more details on why we'd want to compile it just once.


Discussion / hints:
i'll take this one. 

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e81a1b53c3..c0ac28159e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
 ## [Unreleased]
 
 (Please put changes here)
-
+- Use static initializer for AWS profile regex
 - Add QLDB service
 - Add QLDB Session service
 - Update Skeptic tests for Rusoto v0.41
diff --git a/rusoto/credential/Cargo.toml b/rusoto/credential/Cargo.toml
index a8f97749895..d4623b1db87 100644
--- a/rusoto/credential/Cargo.toml
+++ b/rusoto/credential/Cargo.toml
@@ -31,9 +31,9 @@ serde_derive = "1.0.2"
 shlex = "0.1.1"
 tokio-process = "0.2.3"
 tokio-timer = "0.2.6"
+lazy_static = "1.4.0"
 
 [dev-dependencies]
-lazy_static = "1.0"
 quickcheck = "0.6"
 
 [dependencies.clippy]
diff --git a/rusoto/credential/src/profile.rs b/rusoto/credential/src/profile.rs
index b53ec0fd148..b35318d8afd 100644
--- a/rusoto/credential/src/profile.rs
+++ b/rusoto/credential/src/profile.rs
@@ -12,6 +12,7 @@ use futures::future::{result, FutureResult};
 use futures::{Future, Poll};
 use regex::Regex;
 use tokio_process::{CommandExt, OutputAsync};
+use lazy_static::lazy_static;
 
 use crate::{non_empty_env_var, AwsCredentials, CredentialsError, ProvideAwsCredentials};
 
@@ -21,6 +22,10 @@ const AWS_SHARED_CREDENTIALS_FILE: &str = "AWS_SHARED_CREDENTIALS_FILE";
 const DEFAULT: &str = "default";
 const REGION: &str = "region";
 
+lazy_static! {
+    static ref PROFILE_REGEX: Regex = Regex::new(r"^\[(profile )?([^\]]+)\]$").expect("Failed to compile regex");
+}
+
 /// Provides AWS credentials from a profile in a credentials file, or from a credential process.
 ///
 /// # Warning
@@ -256,11 +261,6 @@ fn parse_credential_process_output(v: &[u8]) -> Result<AwsCredentials, Credentia
     }
 }
 
-// should probably constantize with lazy_static!
-fn new_profile_regex() -> Regex {
-    Regex::new(r"^\[(profile )?([^\]]+)\]$").expect("Failed to compile regex")
-}
-
 fn parse_config_file(file_path: &Path) -> Option<HashMap<String, HashMap<String, String>>> {
     match fs::metadata(file_path) {
         Err(_) => return None,
@@ -270,7 +270,7 @@ fn parse_config_file(file_path: &Path) -> Option<HashMap<String, HashMap<String,
             }
         }
     };
-    let profile_regex = new_profile_regex();
+
     let file = File::open(file_path).expect("expected file");
     let file_lines = BufReader::new(&file);
     let result: (HashMap<String, HashMap<String, String>>, Option<String>) = file_lines
@@ -282,8 +282,8 @@ fn parse_config_file(file_path: &Path) -> Option<HashMap<String, HashMap<String,
                 .find(|l| !l.starts_with('#') && !l.is_empty())
         })
         .fold(Default::default(), |(mut result, profile), line| {
-            if profile_regex.is_match(&line) {
-                let caps = profile_regex.captures(&line).unwrap();
+            if PROFILE_REGEX.is_match(&line) {
+                let caps = PROFILE_REGEX.captures(&line).unwrap();
                 let next_profile = caps.get(2).map(|value| value.as_str().to_string());
                 (result, next_profile)
             } else {
@@ -329,7 +329,6 @@ fn parse_credentials_file(
 
     let file = File::open(file_path)?;
 
-    let profile_regex = new_profile_regex();
     let mut profiles: HashMap<String, AwsCredentials> = HashMap::new();
     let mut access_key: Option<String> = None;
     let mut secret_key: Option<String> = None;
@@ -352,7 +351,7 @@ fn parse_credentials_file(
         }
 
         // handle the opening of named profile blocks
-        if profile_regex.is_match(&unwrapped_line) {
+        if PROFILE_REGEX.is_match(&unwrapped_line) {
             if let (Some(profile), Some(key), Some(secret)) = (profile_name, access_key, secret_key)
             {
                 let creds = AwsCredentials::new(key, secret, token, None);
@@ -363,7 +362,7 @@ fn parse_credentials_file(
             secret_key = None;
             token = None;
 
-            let caps = profile_regex.captures(&unwrapped_line).unwrap();
+            let caps = PROFILE_REGEX.captures(&unwrapped_line).unwrap();
             profile_name = Some(caps.get(2).unwrap().as_str().to_string());
             continue;
         }


Test patch: