use crate::env::secrets::{EnvSecret, SecretFormat};
use crate::error::{FluxError, Result};
use crate::keys::{PrivateKey, PublicKey};
use std::env;
#[derive(Debug, Default)]
pub struct EnvSecretProvider {
prefix: Option<String>,
strict_mode: bool,
}
impl EnvSecretProvider {
pub fn new() -> Self {
Self {
prefix: None,
strict_mode: false,
}
}
pub fn with_prefix(prefix: impl Into<String>) -> Self {
Self {
prefix: Some(prefix.into()),
strict_mode: false,
}
}
pub fn strict(mut self) -> Self {
self.strict_mode = true;
self
}
pub fn get_public_key(&self, var_name: &str) -> Result<PublicKey> {
let secret = self.get_secret(var_name)?;
secret.as_public_key()
}
pub fn get_private_key(&self, var_name: &str) -> Result<PrivateKey> {
let secret = self.get_secret(var_name)?;
secret.as_private_key()
}
pub fn get_string(&self, var_name: &str) -> Result<String> {
let full_name = self.build_var_name(var_name);
env::var(&full_name).map_err(|_| {
if self.strict_mode {
FluxError::env(format!(
"Required environment variable not found: {}",
full_name
))
} else {
FluxError::env(format!("Environment variable not found: {}", full_name))
}
})
}
pub fn get_secret(&self, var_name: &str) -> Result<EnvSecret> {
let value = self.get_string(var_name)?;
EnvSecret::from_string(value)
}
pub fn get_secret_with_format(
&self,
var_name: &str,
format: SecretFormat,
) -> Result<EnvSecret> {
let value = self.get_string(var_name)?;
EnvSecret::from_string_with_format(value, format)
}
pub fn has_var(&self, var_name: &str) -> bool {
let full_name = self.build_var_name(var_name);
env::var(&full_name).is_ok()
}
pub fn get_optional_secret(&self, var_name: &str) -> Option<EnvSecret> {
self.get_secret(var_name).ok()
}
pub fn get_optional_string(&self, var_name: &str) -> Option<String> {
self.get_string(var_name).ok()
}
pub fn get_multiple_secrets(&self, var_names: &[&str]) -> Vec<Result<EnvSecret>> {
var_names
.iter()
.map(|&name| self.get_secret(name))
.collect()
}
fn build_var_name(&self, var_name: &str) -> String {
match &self.prefix {
Some(prefix) => format!("{}_{}", prefix, var_name),
None => var_name.to_string(),
}
}
pub fn list_matching_vars(&self) -> Vec<String> {
let prefix = match &self.prefix {
Some(p) => format!("{}_", p),
None => String::new(),
};
env::vars()
.filter_map(|(key, _)| {
if key.starts_with(&prefix) {
Some(key)
} else {
None
}
})
.collect()
}
}
#[derive(Debug, Default)]
pub struct EnvSecretProviderBuilder {
prefix: Option<String>,
strict_mode: bool,
}
impl EnvSecretProviderBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = Some(prefix.into());
self
}
pub fn strict(mut self) -> Self {
self.strict_mode = true;
self
}
pub fn build(self) -> EnvSecretProvider {
EnvSecretProvider {
prefix: self.prefix,
strict_mode: self.strict_mode,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_provider_creation() {
let provider = EnvSecretProvider::new();
assert!(provider.prefix.is_none());
assert!(!provider.strict_mode);
let provider = EnvSecretProvider::with_prefix("FLUX");
assert_eq!(provider.prefix, Some("FLUX".to_string()));
let provider = EnvSecretProvider::new().strict();
assert!(provider.strict_mode);
}
#[test]
fn test_var_name_building() {
let provider = EnvSecretProvider::new();
assert_eq!(provider.build_var_name("TEST"), "TEST");
let provider = EnvSecretProvider::with_prefix("FLUX");
assert_eq!(provider.build_var_name("TEST"), "FLUX_TEST");
}
#[test]
fn test_has_var() {
env::set_var("TEST_VAR_EXISTS", "value");
let provider = EnvSecretProvider::new();
assert!(provider.has_var("TEST_VAR_EXISTS"));
assert!(!provider.has_var("TEST_VAR_DOES_NOT_EXIST"));
env::remove_var("TEST_VAR_EXISTS");
}
#[test]
fn test_get_string() {
env::set_var("TEST_STRING", "hello world");
let provider = EnvSecretProvider::new();
let result = provider.get_string("TEST_STRING").unwrap();
assert_eq!(result, "hello world");
let result = provider.get_string("NONEXISTENT");
assert!(result.is_err());
env::remove_var("TEST_STRING");
}
#[test]
fn test_optional_methods() {
env::set_var("TEST_OPTIONAL", "value");
let provider = EnvSecretProvider::new();
let result = provider.get_optional_string("TEST_OPTIONAL");
assert_eq!(result, Some("value".to_string()));
let result = provider.get_optional_string("NONEXISTENT");
assert_eq!(result, None);
env::remove_var("TEST_OPTIONAL");
}
#[test]
fn test_builder() {
let provider = EnvSecretProviderBuilder::new()
.prefix("FLUX")
.strict()
.build();
assert_eq!(provider.prefix, Some("FLUX".to_string()));
assert!(provider.strict_mode);
}
}