use std::collections::BTreeMap;
use crate::config::{Config, ProviderConfig};
use crate::error::ViaError;
use crate::secrets::SecretValue;
mod onepassword;
pub trait SecretProvider {
fn resolve(&self, reference: &str) -> Result<SecretValue, ViaError>;
}
pub struct ProviderRegistry {
providers: BTreeMap<String, Box<dyn SecretProvider>>,
}
impl ProviderRegistry {
pub fn from_config(config: &Config) -> Result<Self, ViaError> {
let mut providers: BTreeMap<String, Box<dyn SecretProvider>> = BTreeMap::new();
for (name, provider) in &config.providers {
match provider {
ProviderConfig::OnePassword { account } => {
providers.insert(
name.clone(),
Box::new(onepassword::OnePasswordCliProvider::new(account.clone())),
);
}
}
}
Ok(Self { providers })
}
pub fn get(&self, name: &str) -> Result<&dyn SecretProvider, ViaError> {
self.providers
.get(name)
.map(|provider| provider.as_ref())
.ok_or_else(|| ViaError::InvalidConfig(format!("unknown provider `{name}`")))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn config() -> Config {
Config::from_toml_str(
r#"
version = 1
[providers.onepassword]
type = "1password"
"#,
)
.unwrap()
}
#[test]
fn builds_registry_from_config() {
let registry = ProviderRegistry::from_config(&config()).unwrap();
assert!(registry.get("onepassword").is_ok());
}
#[test]
fn reports_missing_provider() {
let registry = ProviderRegistry::from_config(&config()).unwrap();
let error = match registry.get("missing") {
Ok(_) => panic!("expected missing provider error"),
Err(error) => error,
};
assert!(
matches!(error, ViaError::InvalidConfig(message) if message.contains("unknown provider"))
);
}
}