From 32d04789f0057dc71f25f5c75178979a16a914cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Fri, 5 Sep 2025 11:54:02 +0200 Subject: [PATCH 1/7] fix(macros-core): more consistent env loading and reading logic While giving the new `sqlx.toml` feature a try, I discovered that its `database_url_var` setting behaved inconsistently compared to other environment variables: it wasn't loaded from `.env` files, causing it to be effectively ignored in favor of the default `DATABASE_URL` variable. This is an inconvenient outcome for the multi-database workspaces that this feature is designed to support. To address this issue, I reworked the `.env` loading logic to account for this configuration and make it simpler to use to achieve consistent behavior. The new algorithm works as follows: - First, it generates lists of environment variables that can be loaded from `.env` files and candidate `.env` file paths. When applicable, the compiler is informed to track changes to their elements. - Next, it loads the set of potentially tracked environment variables using `set_var`, similar to how `dotenvy::dotenv()` operates. When a variable is defined in both the process environment and a `.env` file, the process environment takes precedence, as before. - Macro code can now use the `env` function freely to read environment variable values, abstracting itself away from their source, which results in simpler, less error-prone code. Trivially, this rework resolves the issue I encountered because the `database_url_var` value is now part of the list of loadable environment variables. Future code can easily make such additions as necessary. --- sqlx-macros-core/src/query/mod.rs | 131 +++++++++++++++++++----------- 1 file changed, 83 insertions(+), 48 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index 060a24b847..a4bc2b3d20 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -1,4 +1,8 @@ +#[cfg(procmacro2_semver_exempt)] +use std::collections::HashSet; use std::collections::{hash_map, HashMap}; +#[cfg(procmacro2_semver_exempt)] +use std::hash::{BuildHasherDefault, DefaultHasher}; use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, Mutex}; use std::{fs, io}; @@ -115,20 +119,19 @@ static METADATA: LazyLock>> = LazyLock::new(Defa // reflect the workspace dir: https://github.com/rust-lang/cargo/issues/3946 fn init_metadata(manifest_dir: &String) -> crate::Result { let manifest_dir: PathBuf = manifest_dir.into(); + let config = Config::try_from_crate_or_default()?; - let (database_url, offline, offline_dir) = load_dot_env(&manifest_dir); + load_env(&manifest_dir, &config); let offline = env("SQLX_OFFLINE") - .ok() - .or(offline) .map(|s| s.eq_ignore_ascii_case("true") || s == "1") .unwrap_or(false); - let offline_dir = env("SQLX_OFFLINE_DIR").ok().or(offline_dir); + let offline_dir = env("SQLX_OFFLINE_DIR").ok(); - let config = Config::try_from_crate_or_default()?; - - let database_url = env(config.common.database_url_var()).ok().or(database_url); + let database_url = env(config.common.database_url_var()) + .ok() + .or_else(|| env("DATABASE_URL").ok()); Ok(Metadata { manifest_dir, @@ -415,64 +418,96 @@ where Ok(ret_tokens) } +#[cfg(procmacro2_semver_exempt)] +static TRACKED_ENV_VARS: Mutex>> = + Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); + /// Get the value of an environment variable, telling the compiler about it if applicable. fn env(name: &str) -> Result { #[cfg(procmacro2_semver_exempt)] - { + if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { + // Avoid tracking the same env var multiple times, which would undesirably modify + // build system state and thus behavior in case we change var values. proc_macro::tracked_env::var(name) + } else { + std::env::var(name) } #[cfg(not(procmacro2_semver_exempt))] - { - std::env::var(name) - } + std::env::var(name) } -/// Get `DATABASE_URL`, `SQLX_OFFLINE` and `SQLX_OFFLINE_DIR` from the `.env`. -fn load_dot_env(manifest_dir: &Path) -> (Option, Option, Option) { - let mut env_path = manifest_dir.join(".env"); - - // If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this, - // otherwise fallback to default dotenv file. - #[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))] - let env_file = if env_path.exists() { - let res = dotenvy::from_path_iter(&env_path); - match res { - Ok(iter) => Some(iter), - Err(e) => panic!("failed to load environment from {env_path:?}, {e}"), - } - } else { - #[allow(unused_assignments)] - { - env_path = PathBuf::from(".env"); +/// Load configuration environment variables from a `.env` file, without overriding existing +/// environment variables. If applicable, the compiler is informed about the loaded env vars +/// and the `.env` files they may come from. +fn load_env(manifest_dir: &Path, config: &Config) { + let loadable_vars = [ + "DATABASE_URL", + "SQLX_OFFLINE", + "SQLX_OFFLINE_DIR", + config.common.database_url_var(), + ]; + + let (found_dotenv, candidate_dotenv_paths) = find_dotenv(manifest_dir); + + // Tell the compiler to watch the candidate `.env` paths for changes. It's important to + // watch them all, because there are several possible locations where a `.env` file + // might be read, and we want to react to changes in any of them. + #[cfg(procmacro2_semver_exempt)] + for path in &candidate_dotenv_paths { + if let Some(path) = path.to_str() { + proc_macro::tracked_path::path(path); } - dotenvy::dotenv_iter().ok() - }; - - let mut offline = None; - let mut database_url = None; - let mut offline_dir = None; + } - if let Some(env_file) = env_file { - // tell the compiler to watch the `.env` for changes. - #[cfg(procmacro2_semver_exempt)] - if let Some(env_path) = env_path.to_str() { - proc_macro::tracked_path::path(env_path); + // Tell the compiler about the environment variables we care about before we load them + // from any `.env`, so the build system can react to changes in their original values, + // not the values we load from a potential `.env` file tracked above, which should not + // take precedence. + #[cfg(procmacro2_semver_exempt)] + for name in &loadable_vars { + if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { + proc_macro::tracked_env::var(name); } + } - for item in env_file { - let Ok((key, value)) = item else { + if let Some(dotenv_path) = found_dotenv + .then_some(candidate_dotenv_paths) + .iter() + .flatten() + .last() + { + for dotenv_var_result in dotenvy::from_path_iter(dotenv_path) + .ok() + .into_iter() + .flatten() + { + let Ok((key, value)) = dotenv_var_result else { continue; }; - match key.as_str() { - "DATABASE_URL" => database_url = Some(value), - "SQLX_OFFLINE" => offline = Some(value), - "SQLX_OFFLINE_DIR" => offline_dir = Some(value), - _ => {} - }; + if loadable_vars.contains(&&*key) && std::env::var(&key).is_err() { + std::env::set_var(key, value); + } } } +} + +fn find_dotenv(mut dir: &Path) -> (bool, Vec) { + let mut candidate_files = vec![]; + + loop { + candidate_files.push(dir.join(".env")); + let candidate_file = candidate_files.last().unwrap(); - (database_url, offline, offline_dir) + if candidate_file.is_file() { + return (true, candidate_files); + } + + if let Some(parent) = dir.parent() { + dir = parent; + } else { + return (false, candidate_files); + } + } } From fcd093af4f68f70b83672aa18e0223011860a779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Fri, 5 Sep 2025 13:00:59 +0200 Subject: [PATCH 2/7] tweak: use static map for loaded env vars instead of calling `set_env` --- sqlx-macros-core/src/query/mod.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index a4bc2b3d20..68e7c14caa 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -1,7 +1,6 @@ #[cfg(procmacro2_semver_exempt)] use std::collections::HashSet; use std::collections::{hash_map, HashMap}; -#[cfg(procmacro2_semver_exempt)] use std::hash::{BuildHasherDefault, DefaultHasher}; use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, Mutex}; @@ -422,19 +421,26 @@ where static TRACKED_ENV_VARS: Mutex>> = Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); +static LOADED_ENV_VARS: Mutex>> = + Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); + /// Get the value of an environment variable, telling the compiler about it if applicable. fn env(name: &str) -> Result { #[cfg(procmacro2_semver_exempt)] - if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { + let tracked_value = if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { // Avoid tracking the same env var multiple times, which would undesirably modify // build system state and thus behavior in case we change var values. proc_macro::tracked_env::var(name) } else { - std::env::var(name) - } - + None + }; #[cfg(not(procmacro2_semver_exempt))] - std::env::var(name) + let tracked_value = None; + + tracked_value + .or_else(|| std::env::var(name).ok()) + .or_else(|| LOADED_ENV_VARS.lock().unwrap().get(name).cloned()) + .ok_or(std::env::VarError::NotPresent) } /// Load configuration environment variables from a `.env` file, without overriding existing @@ -487,7 +493,7 @@ fn load_env(manifest_dir: &Path, config: &Config) { }; if loadable_vars.contains(&&*key) && std::env::var(&key).is_err() { - std::env::set_var(key, value); + LOADED_ENV_VARS.lock().unwrap().insert(key, value); } } } From 6de77e96113e7ef09f704326c596dfdb8fb1a554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Fri, 5 Sep 2025 14:35:04 +0200 Subject: [PATCH 3/7] chore: fix build when `procmacro2_semver_exempt` cfg is enabled --- sqlx-macros-core/src/query/mod.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index 68e7c14caa..0737b16895 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -1,6 +1,7 @@ #[cfg(procmacro2_semver_exempt)] use std::collections::HashSet; use std::collections::{hash_map, HashMap}; +use std::env::VarError; use std::hash::{BuildHasherDefault, DefaultHasher}; use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, Mutex}; @@ -430,17 +431,23 @@ fn env(name: &str) -> Result { let tracked_value = if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { // Avoid tracking the same env var multiple times, which would undesirably modify // build system state and thus behavior in case we change var values. - proc_macro::tracked_env::var(name) + Some(proc_macro::tracked_env::var(name)) } else { None }; #[cfg(not(procmacro2_semver_exempt))] let tracked_value = None; - tracked_value - .or_else(|| std::env::var(name).ok()) - .or_else(|| LOADED_ENV_VARS.lock().unwrap().get(name).cloned()) - .ok_or(std::env::VarError::NotPresent) + match tracked_value.map_or_else(|| std::env::var(name), |var| var) { + Ok(v) => Ok(v), + Err(VarError::NotPresent) => LOADED_ENV_VARS + .lock() + .unwrap() + .get(name) + .cloned() + .ok_or(VarError::NotPresent), + Err(e) => Err(e), + } } /// Load configuration environment variables from a `.env` file, without overriding existing From 9f232e53105fc0d6af4314eb71cf23210735d428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Tue, 23 Sep 2025 18:37:34 +0200 Subject: [PATCH 4/7] tweak: do not avoid tracking env vars repeatedly --- sqlx-macros-core/src/query/mod.rs | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index 0737b16895..d85f00846b 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -418,23 +418,13 @@ where Ok(ret_tokens) } -#[cfg(procmacro2_semver_exempt)] -static TRACKED_ENV_VARS: Mutex>> = - Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); - static LOADED_ENV_VARS: Mutex>> = Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); /// Get the value of an environment variable, telling the compiler about it if applicable. fn env(name: &str) -> Result { #[cfg(procmacro2_semver_exempt)] - let tracked_value = if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { - // Avoid tracking the same env var multiple times, which would undesirably modify - // build system state and thus behavior in case we change var values. - Some(proc_macro::tracked_env::var(name)) - } else { - None - }; + let tracked_value = Some(proc_macro::tracked_env::var(name)); #[cfg(not(procmacro2_semver_exempt))] let tracked_value = None; @@ -473,17 +463,6 @@ fn load_env(manifest_dir: &Path, config: &Config) { } } - // Tell the compiler about the environment variables we care about before we load them - // from any `.env`, so the build system can react to changes in their original values, - // not the values we load from a potential `.env` file tracked above, which should not - // take precedence. - #[cfg(procmacro2_semver_exempt)] - for name in &loadable_vars { - if TRACKED_ENV_VARS.lock().unwrap().insert(name.to_string()) { - proc_macro::tracked_env::var(name); - } - } - if let Some(dotenv_path) = found_dotenv .then_some(candidate_dotenv_paths) .iter() From 0c731ae667dd904affc7a89c3adee2ce8f967ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Thu, 25 Sep 2025 19:27:11 +0200 Subject: [PATCH 5/7] tweak: get rid of unnecessary `DATABASE_URL` fallback according to review comment --- sqlx-macros-core/src/query/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index d85f00846b..97833b3a4a 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -129,9 +129,7 @@ fn init_metadata(manifest_dir: &String) -> crate::Result { let offline_dir = env("SQLX_OFFLINE_DIR").ok(); - let database_url = env(config.common.database_url_var()) - .ok() - .or_else(|| env("DATABASE_URL").ok()); + let database_url = env(config.common.database_url_var()).ok(); Ok(Metadata { manifest_dir, From 57c1dcf45e09b725dc62931947a1c82d17aa7cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Thu, 25 Sep 2025 20:27:56 +0200 Subject: [PATCH 6/7] tweak: per-crate loaded `.env` vars storage --- sqlx-macros-core/src/query/mod.rs | 135 +++++++++++++++++------------- 1 file changed, 79 insertions(+), 56 deletions(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index 97833b3a4a..83e966fa37 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -1,8 +1,6 @@ -#[cfg(procmacro2_semver_exempt)] -use std::collections::HashSet; +use std::cell::RefCell; use std::collections::{hash_map, HashMap}; use std::env::VarError; -use std::hash::{BuildHasherDefault, DefaultHasher}; use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, Mutex}; use std::{fs, io}; @@ -113,15 +111,20 @@ impl Metadata { } } -static METADATA: LazyLock>> = LazyLock::new(Default::default); +static METADATA: LazyLock>>> = LazyLock::new(Default::default); +static CRATE_ENV_FILE_VARS: LazyLock>>> = + LazyLock::new(Default::default); + +thread_local! { + static CURRENT_CRATE_MANIFEST_DIR: RefCell = RefCell::new(PathBuf::new()); +} // If we are in a workspace, lookup `workspace_root` since `CARGO_MANIFEST_DIR` won't // reflect the workspace dir: https://github.com/rust-lang/cargo/issues/3946 -fn init_metadata(manifest_dir: &String) -> crate::Result { - let manifest_dir: PathBuf = manifest_dir.into(); +fn init_metadata(manifest_dir: &Path) -> crate::Result> { let config = Config::try_from_crate_or_default()?; - load_env(&manifest_dir, &config); + load_env(manifest_dir, &config); let offline = env("SQLX_OFFLINE") .map(|s| s.eq_ignore_ascii_case("true") || s == "1") @@ -131,40 +134,41 @@ fn init_metadata(manifest_dir: &String) -> crate::Result { let database_url = env(config.common.database_url_var()).ok(); - Ok(Metadata { - manifest_dir, + Ok(Arc::new(Metadata { + manifest_dir: manifest_dir.to_path_buf(), offline, database_url, offline_dir, config, workspace_root: Arc::new(Mutex::new(None)), - }) + })) } pub fn expand_input<'a>( input: QueryMacroInput, drivers: impl IntoIterator, ) -> crate::Result { - let manifest_dir = env("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` must be set"); + // `CARGO_MANIFEST_DIR` can only be loaded from a real environment variable due to the filtering done + // by `load_env`, so the value of `CURRENT_CRATE_MANIFEST_DIR` does not matter here. + let manifest_dir = + PathBuf::from(env("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` must be set")); + CURRENT_CRATE_MANIFEST_DIR.set(manifest_dir.clone()); - let mut metadata_lock = METADATA - .lock() - // Just reset the metadata on error - .unwrap_or_else(|poison_err| { - let mut guard = poison_err.into_inner(); - *guard = Default::default(); - guard - }); + let mut metadata_lock = METADATA.lock().unwrap(); let metadata = match metadata_lock.entry(manifest_dir) { - hash_map::Entry::Occupied(occupied) => occupied.into_mut(), + hash_map::Entry::Occupied(occupied) => Arc::clone(&occupied.get()), hash_map::Entry::Vacant(vacant) => { let metadata = init_metadata(vacant.key())?; - vacant.insert(metadata) + vacant.insert(Arc::clone(&metadata)); + metadata } }; - let data_source = match &metadata { + // Release the lock now so other expansions in other threads of this process can proceed concurrently. + drop(metadata_lock); + + let data_source = match &*metadata { Metadata { offline: false, database_url: Some(db_url), @@ -182,7 +186,7 @@ pub fn expand_input<'a>( ]; let Some(data_file_path) = dirs .iter() - .filter_map(|path| path(metadata)) + .filter_map(|path| path(&metadata)) .map(|path| path.join(&filename)) .find(|path| path.exists()) else { @@ -416,10 +420,11 @@ where Ok(ret_tokens) } -static LOADED_ENV_VARS: Mutex>> = - Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); - -/// Get the value of an environment variable, telling the compiler about it if applicable. +/// Get the value of an environment variable for the current crate, telling the compiler about it if applicable. +/// +/// The current crate is determined by the `CURRENT_CRATE_MANIFEST_DIR` thread-local variable, which is assumed +/// to be set to match the crate whose macro is being expanded before this function is called. It is also assumed +/// that the expansion of this macro happens on a single thread. fn env(name: &str) -> Result { #[cfg(procmacro2_semver_exempt)] let tracked_value = Some(proc_macro::tracked_env::var(name)); @@ -428,26 +433,37 @@ fn env(name: &str) -> Result { match tracked_value.map_or_else(|| std::env::var(name), |var| var) { Ok(v) => Ok(v), - Err(VarError::NotPresent) => LOADED_ENV_VARS - .lock() - .unwrap() - .get(name) - .cloned() + Err(VarError::NotPresent) => CURRENT_CRATE_MANIFEST_DIR + .with_borrow(|manifest_dir| { + CRATE_ENV_FILE_VARS + .lock() + .unwrap() + .get(manifest_dir) + .cloned() + }) + .and_then(|env_file_vars| env_file_vars.get(name).cloned()) .ok_or(VarError::NotPresent), Err(e) => Err(e), } } -/// Load configuration environment variables from a `.env` file, without overriding existing -/// environment variables. If applicable, the compiler is informed about the loaded env vars -/// and the `.env` files they may come from. +/// Load configuration environment variables from a `.env` file. If applicable, the compiler is +/// about the `.env` files they may come from. fn load_env(manifest_dir: &Path, config: &Config) { - let loadable_vars = [ - "DATABASE_URL", - "SQLX_OFFLINE", - "SQLX_OFFLINE_DIR", - config.common.database_url_var(), - ]; + // A whitelist of environment variables to load from a `.env` file avoids + // such files overriding internal variables they should not (e.g., `CARGO`, + // `CARGO_MANIFEST_DIR`) when using the `env` function above. + let database_url_var = config.common.database_url_var(); + let loadable_vars = if database_url_var == "DATABASE_URL" { + &["DATABASE_URL", "SQLX_OFFLINE", "SQLX_OFFLINE_DIR"][..] + } else { + &[ + "DATABASE_URL", + "SQLX_OFFLINE", + "SQLX_OFFLINE_DIR", + database_url_var, + ] + }; let (found_dotenv, candidate_dotenv_paths) = find_dotenv(manifest_dir); @@ -461,26 +477,33 @@ fn load_env(manifest_dir: &Path, config: &Config) { } } - if let Some(dotenv_path) = found_dotenv + let loaded_vars = found_dotenv .then_some(candidate_dotenv_paths) .iter() .flatten() .last() - { - for dotenv_var_result in dotenvy::from_path_iter(dotenv_path) - .ok() - .into_iter() - .flatten() - { - let Ok((key, value)) = dotenv_var_result else { - continue; - }; + .map(|dotenv_path| { + dotenvy::from_path_iter(dotenv_path) + .ok() + .into_iter() + .flatten() + .filter_map(|dotenv_var_result| match dotenv_var_result { + Ok((key, value)) + if loadable_vars.contains(&&*key) && std::env::var(&key).is_err() => + { + Some((key, value)) + } + _ => None, + }) + }) + .into_iter() + .flatten() + .collect(); - if loadable_vars.contains(&&*key) && std::env::var(&key).is_err() { - LOADED_ENV_VARS.lock().unwrap().insert(key, value); - } - } - } + CRATE_ENV_FILE_VARS + .lock() + .unwrap() + .insert(manifest_dir.to_path_buf(), loaded_vars); } fn find_dotenv(mut dir: &Path) -> (bool, Vec) { From aea4a296d4f4dcb25fde5620a579c8615625538d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Thu, 25 Sep 2025 20:34:06 +0200 Subject: [PATCH 7/7] chore: fix Clippy lint --- sqlx-macros-core/src/query/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index 83e966fa37..d94498deea 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -157,7 +157,7 @@ pub fn expand_input<'a>( let mut metadata_lock = METADATA.lock().unwrap(); let metadata = match metadata_lock.entry(manifest_dir) { - hash_map::Entry::Occupied(occupied) => Arc::clone(&occupied.get()), + hash_map::Entry::Occupied(occupied) => Arc::clone(occupied.get()), hash_map::Entry::Vacant(vacant) => { let metadata = init_metadata(vacant.key())?; vacant.insert(Arc::clone(&metadata));