use crate::config_value::ConfigValue;
use std::sync::{Arc, OnceLock};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ResolverError {
#[error("Environment variable '{0}' not found and no default provided")]
EnvVarNotFound(String),
#[error("Not implemented: {0}")]
NotImplemented(String),
#[error("No resolver found for reference type: {0}")]
NoResolverFound(String),
#[error("Wrong resolver type used for this reference")]
WrongResolverType,
#[error("Failed to parse value: {0}")]
ParseError(String),
}
pub trait ValueResolver: Send + Sync {
fn resolve_to_string(&self, value: &ConfigValue<String>) -> Result<String, ResolverError>;
}
pub struct EnvironmentVariableResolver;
impl ValueResolver for EnvironmentVariableResolver {
fn resolve_to_string(&self, value: &ConfigValue<String>) -> Result<String, ResolverError> {
match value {
ConfigValue::EnvironmentVariable { name, default } => {
std::env::var(name).or_else(|_| {
default
.clone()
.ok_or_else(|| ResolverError::EnvVarNotFound(name.clone()))
})
}
_ => Err(ResolverError::WrongResolverType),
}
}
}
pub struct SecretResolver;
impl ValueResolver for SecretResolver {
fn resolve_to_string(&self, value: &ConfigValue<String>) -> Result<String, ResolverError> {
match value {
ConfigValue::Secret { name } => Err(ResolverError::NotImplemented(format!(
"Secret resolution not yet implemented for '{name}'"
))),
_ => Err(ResolverError::WrongResolverType),
}
}
}
static SECRET_RESOLVER: OnceLock<Arc<dyn ValueResolver>> = OnceLock::new();
pub fn register_secret_resolver(
resolver: Arc<dyn ValueResolver>,
) -> Result<(), Arc<dyn ValueResolver>> {
SECRET_RESOLVER.set(resolver)
}
pub(crate) fn get_secret_resolver() -> Option<Arc<dyn ValueResolver>> {
SECRET_RESOLVER.get().cloned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_env_resolver_with_set_var() {
std::env::set_var("TEST_SDK_VAR_1", "test_value");
let resolver = EnvironmentVariableResolver;
let value = ConfigValue::EnvironmentVariable {
name: "TEST_SDK_VAR_1".to_string(),
default: None,
};
let result = resolver.resolve_to_string(&value).expect("resolve");
assert_eq!(result, "test_value");
std::env::remove_var("TEST_SDK_VAR_1");
}
#[test]
fn test_env_resolver_with_default() {
let resolver = EnvironmentVariableResolver;
let value = ConfigValue::EnvironmentVariable {
name: "NONEXISTENT_SDK_VAR_12345".to_string(),
default: Some("default_value".to_string()),
};
let result = resolver.resolve_to_string(&value).expect("resolve");
assert_eq!(result, "default_value");
}
#[test]
fn test_env_resolver_missing_var_no_default() {
let resolver = EnvironmentVariableResolver;
let value = ConfigValue::EnvironmentVariable {
name: "NONEXISTENT_SDK_VAR_67890".to_string(),
default: None,
};
let result = resolver.resolve_to_string(&value);
assert!(result.is_err());
assert!(matches!(
result.expect_err("should fail"),
ResolverError::EnvVarNotFound(_)
));
}
#[test]
fn test_env_resolver_wrong_variant() {
let resolver = EnvironmentVariableResolver;
let value = ConfigValue::Secret {
name: "x".to_string(),
};
assert!(matches!(
resolver.resolve_to_string(&value).expect_err("should fail"),
ResolverError::WrongResolverType
));
}
#[test]
fn test_secret_resolver_not_implemented() {
let resolver = SecretResolver;
let value = ConfigValue::Secret {
name: "my-secret".to_string(),
};
let result = resolver.resolve_to_string(&value);
assert!(result.is_err());
assert!(matches!(
result.expect_err("should fail"),
ResolverError::NotImplemented(_)
));
}
#[test]
fn test_secret_resolver_wrong_variant() {
let resolver = SecretResolver;
let value = ConfigValue::Static("x".to_string());
assert!(matches!(
resolver.resolve_to_string(&value).expect_err("should fail"),
ResolverError::WrongResolverType
));
}
}