pub fn resolve_api_key(default_env: &str, override_env: Option<&str>) -> Option<String> {
let env_var = override_env.unwrap_or(default_env);
match std::env::var(env_var) {
Ok(key) if !key.is_empty() => Some(key),
_ => None,
}
}
pub fn resolve_provider_key(
provider: &str,
default_env: &str,
override_env: Option<&str>,
) -> Option<String> {
resolve_provider_key_in(
super::credentials::default_store(),
provider,
default_env,
override_env,
)
}
pub(crate) fn resolve_provider_key_in(
store: &dyn super::credentials::CredentialStore,
provider: &str,
default_env: &str,
override_env: Option<&str>,
) -> Option<String> {
if override_env.is_some() {
return resolve_api_key(default_env, override_env);
}
resolve_api_key(default_env, None).or_else(|| store.get(provider))
}
pub fn resolve_provider_key_with_fallback(
provider: &str,
default_env: &str,
fallback_env: &str,
override_env: Option<&str>,
) -> Option<String> {
if override_env.is_some() {
return resolve_api_key(default_env, override_env);
}
resolve_api_key_with_fallback(default_env, fallback_env, None)
.or_else(|| super::credentials::default_store().get(provider))
}
pub fn provider_key_source(
provider: &str,
default_env: &str,
override_env: Option<&str>,
) -> &'static str {
if resolve_api_key(default_env, override_env).is_some() {
return "env";
}
if override_env.is_none() && super::credentials::default_store().get(provider).is_some() {
return "keyring";
}
"none"
}
pub fn resolve_api_key_with_fallback(
default_env: &str,
fallback_env: &str,
override_env: Option<&str>,
) -> Option<String> {
if override_env.is_some() {
return resolve_api_key(default_env, override_env);
}
resolve_api_key(default_env, None).or_else(|| resolve_api_key(fallback_env, None))
}
#[cfg(test)]
mod tests {
use super::*;
fn unique_env(prefix: &str) -> String {
use std::sync::atomic::{AtomicUsize, Ordering};
static N: AtomicUsize = AtomicUsize::new(0);
format!(
"{}_{}_{}",
prefix,
std::process::id(),
N.fetch_add(1, Ordering::SeqCst)
)
}
#[test]
fn provider_key_env_beats_keyring() {
use crate::utils::credentials::test_support::FakeStore;
let store = FakeStore::default();
store
.entries
.lock()
.unwrap()
.insert("groq".to_string(), "stored-key".to_string());
let var = unique_env("MERMAID_TEST_PK_ENV");
temp_env::with_var(&var, Some("env-key"), || {
assert_eq!(
resolve_provider_key_in(&store, "groq", &var, None),
Some("env-key".to_string()),
"env must have absolute precedence"
);
});
temp_env::with_var_unset(&var, || {
assert_eq!(
resolve_provider_key_in(&store, "groq", &var, None),
Some("stored-key".to_string()),
"keyring fills the gap when env is unset"
);
});
}
#[test]
fn override_env_blocks_keyring_fallback() {
use crate::utils::credentials::test_support::FakeStore;
let store = FakeStore::default();
store
.entries
.lock()
.unwrap()
.insert("groq".to_string(), "stored-key".to_string());
let default_var = unique_env("MERMAID_TEST_PK_DEFAULT");
let override_var = unique_env("MERMAID_TEST_PK_OVERRIDE");
temp_env::with_vars(
[
(default_var.as_str(), Some("default-key")),
(override_var.as_str(), None),
],
|| {
assert_eq!(
resolve_provider_key_in(&store, "groq", &default_var, Some(&override_var)),
None
);
},
);
}
#[test]
fn returns_none_when_env_var_unset() {
let var = unique_env("MERMAID_TEST_AUTH_UNSET");
temp_env::with_var_unset(&var, || {
assert_eq!(resolve_api_key(&var, None), None);
});
}
#[test]
fn returns_value_when_env_var_set() {
let var = unique_env("MERMAID_TEST_AUTH_SET");
temp_env::with_var(&var, Some("secret-value"), || {
assert_eq!(
resolve_api_key(&var, None),
Some("secret-value".to_string())
);
});
}
#[test]
fn empty_string_treated_as_unset() {
let var = unique_env("MERMAID_TEST_AUTH_EMPTY");
temp_env::with_var(&var, Some(""), || {
assert_eq!(resolve_api_key(&var, None), None);
});
}
#[test]
fn override_env_takes_precedence_over_default() {
let default_var = unique_env("MERMAID_TEST_AUTH_DEFAULT");
let override_var = unique_env("MERMAID_TEST_AUTH_OVERRIDE");
temp_env::with_vars(
[
(default_var.as_str(), Some("default-key")),
(override_var.as_str(), Some("override-key")),
],
|| {
let resolved = resolve_api_key(&default_var, Some(&override_var));
assert_eq!(resolved, Some("override-key".to_string()));
},
);
}
#[test]
fn override_env_unset_falls_through_to_none() {
let default_var = unique_env("MERMAID_TEST_AUTH_DEFAULT2");
let override_var = unique_env("MERMAID_TEST_AUTH_OVERRIDE2");
temp_env::with_vars(
[
(default_var.as_str(), Some("default-key")),
(override_var.as_str(), None),
],
|| {
let resolved = resolve_api_key(&default_var, Some(&override_var));
assert_eq!(resolved, None);
},
);
}
#[test]
fn fallback_env_used_only_when_default_is_unset() {
let default_var = unique_env("MERMAID_TEST_AUTH_FALLBACK_DEFAULT");
let fallback_var = unique_env("MERMAID_TEST_AUTH_FALLBACK_LEGACY");
temp_env::with_vars(
[
(default_var.as_str(), None),
(fallback_var.as_str(), Some("legacy-key")),
],
|| {
let resolved = resolve_api_key_with_fallback(&default_var, &fallback_var, None);
assert_eq!(resolved, Some("legacy-key".to_string()));
},
);
temp_env::with_vars(
[
(default_var.as_str(), Some("default-key")),
(fallback_var.as_str(), Some("legacy-key")),
],
|| {
let resolved = resolve_api_key_with_fallback(&default_var, &fallback_var, None);
assert_eq!(resolved, Some("default-key".to_string()));
},
);
}
#[test]
fn fallback_env_is_ignored_when_override_is_set() {
let default_var = unique_env("MERMAID_TEST_AUTH_OVERRIDE_DEFAULT3");
let fallback_var = unique_env("MERMAID_TEST_AUTH_OVERRIDE_FALLBACK3");
let override_var = unique_env("MERMAID_TEST_AUTH_OVERRIDE3");
temp_env::with_vars(
[
(default_var.as_str(), Some("default-key")),
(fallback_var.as_str(), Some("legacy-key")),
(override_var.as_str(), None),
],
|| {
let resolved =
resolve_api_key_with_fallback(&default_var, &fallback_var, Some(&override_var));
assert_eq!(resolved, None);
},
);
}
}