mecha10-core 0.6.2

Mecha10 core runtime and foundational types
Documentation
//! Environment Variable Secret Backend

use super::SecretBackend;
use crate::{Mecha10Error, Result};

/// Secret backend that reads from environment variables
///
/// This is the simplest backend, reading secrets from environment variables.
/// Secret keys are automatically uppercased and prefixed.
///
/// # Example
///
/// ```rust
/// use mecha10::secrets::{EnvironmentBackend, SecretBackend};
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// // Creates backend with prefix "MECHA10_SECRET_"
/// let backend = EnvironmentBackend::new("MECHA10_SECRET");
///
/// // Will read from env var: MECHA10_SECRET_API_KEY
/// let api_key = backend.get("api_key").await?;
/// # Ok(())
/// # }
/// ```
pub struct EnvironmentBackend {
    prefix: String,
}

impl EnvironmentBackend {
    /// Create a new environment backend with a prefix
    ///
    /// The prefix will be prepended to all secret keys when reading from
    /// environment variables. Keys are automatically uppercased.
    pub fn new(prefix: &str) -> Self {
        Self {
            prefix: prefix.to_string(),
        }
    }

    /// Create backend with default prefix "MECHA10_SECRET"
    #[allow(clippy::should_implement_trait)]
    pub fn default() -> Self {
        Self::new("MECHA10_SECRET")
    }

    fn make_env_key(&self, key: &str) -> String {
        format!("{}_{}", self.prefix, key.to_uppercase())
    }
}

#[async_trait::async_trait]
impl SecretBackend for EnvironmentBackend {
    async fn get(&self, key: &str) -> Result<String> {
        let env_key = self.make_env_key(key);
        std::env::var(&env_key).map_err(|_| {
            Mecha10Error::Configuration(format!(
                "Secret '{}' not found in environment variable '{}'",
                key, env_key
            ))
        })
    }

    async fn list(&self) -> Result<Vec<String>> {
        let prefix_with_underscore = format!("{}_", self.prefix);
        let keys: Vec<String> = std::env::vars()
            .filter_map(|(k, _)| k.strip_prefix(&prefix_with_underscore).map(|s| s.to_lowercase()))
            .collect();
        Ok(keys)
    }
}