Skip to main content

synwire_core/credentials/
env.rs

1//! Environment variable credential provider.
2
3use crate::BoxFuture;
4use crate::credentials::SecretValue;
5use crate::credentials::traits::CredentialProvider;
6use crate::error::SynwireError;
7
8/// Reads a credential from an environment variable on each call.
9///
10/// # Example
11///
12/// ```
13/// use synwire_core::credentials::EnvCredentialProvider;
14///
15/// let provider = EnvCredentialProvider::new("OPENAI_API_KEY");
16/// ```
17#[derive(Debug, Clone)]
18pub struct EnvCredentialProvider {
19    env_var: String,
20}
21
22impl EnvCredentialProvider {
23    /// Creates a new provider that reads from the given environment variable.
24    pub fn new(env_var: impl Into<String>) -> Self {
25        Self {
26            env_var: env_var.into(),
27        }
28    }
29
30    /// Returns the environment variable name this provider reads from.
31    pub fn env_var(&self) -> &str {
32        &self.env_var
33    }
34}
35
36impl CredentialProvider for EnvCredentialProvider {
37    fn get_credential(&self) -> BoxFuture<'_, Result<SecretValue, SynwireError>> {
38        Box::pin(async {
39            let value = std::env::var(&self.env_var).map_err(|_| SynwireError::Credential {
40                message: format!("environment variable {} not set", self.env_var),
41            })?;
42            Ok(SecretValue::new(value))
43        })
44    }
45}
46
47#[cfg(test)]
48#[allow(clippy::unwrap_used)]
49mod tests {
50    use super::*;
51
52    #[tokio::test]
53    async fn missing_env_var_returns_error() {
54        // Use a variable name that will not exist in the environment.
55        let provider = EnvCredentialProvider::new("SYNWIRE_NONEXISTENT_VAR_82a9f3c1d4e6b7");
56        let result = provider.get_credential().await;
57        assert!(result.is_err());
58        let err_msg = result.unwrap_err().to_string();
59        assert!(err_msg.contains("not set"));
60    }
61
62    #[tokio::test]
63    async fn get_credential_from_existing_env() {
64        // PATH is always set on Linux, so use it as a safe read-only test.
65        let provider = EnvCredentialProvider::new("PATH");
66        let result = provider.get_credential().await;
67        assert!(result.is_ok());
68        assert!(!result.unwrap().expose().is_empty());
69    }
70
71    #[test]
72    fn env_var_accessor() {
73        let provider = EnvCredentialProvider::new("MY_KEY");
74        assert_eq!(provider.env_var(), "MY_KEY");
75    }
76}