siphon_secrets/
resolver.rs

1//! Secret resolution dispatcher
2
3use crate::error::SecretError;
4use crate::uri::SecretUri;
5
6/// Resolves secrets from various backends based on URI scheme
7#[derive(Debug, Default)]
8pub struct SecretResolver {
9    _private: (), // Prevent construction without ::new()
10}
11
12impl SecretResolver {
13    /// Create a new secret resolver
14    pub fn new() -> Self {
15        Self { _private: () }
16    }
17
18    /// Resolve a SecretUri to its actual value
19    pub fn resolve(&self, uri: &SecretUri) -> Result<String, SecretError> {
20        tracing::debug!(backend = uri.backend_name(), "Resolving secret");
21
22        match uri {
23            SecretUri::Plain(value) => Ok(value.clone()),
24
25            #[cfg(feature = "env")]
26            SecretUri::Env { var_name } => crate::backends::env::resolve(var_name),
27
28            #[cfg(not(feature = "env"))]
29            SecretUri::Env { .. } => Err(SecretError::disabled("env")),
30
31            #[cfg(feature = "file")]
32            SecretUri::File { path } => crate::backends::file::resolve(path),
33
34            #[cfg(not(feature = "file"))]
35            SecretUri::File { .. } => Err(SecretError::disabled("file")),
36
37            #[cfg(feature = "keychain")]
38            SecretUri::Keychain { service, key } => {
39                crate::backends::keychain::resolve(service, key)
40            }
41
42            #[cfg(not(feature = "keychain"))]
43            SecretUri::Keychain { .. } => Err(SecretError::disabled("keychain")),
44
45            #[cfg(feature = "onepassword")]
46            SecretUri::OnePassword { vault, item, field } => {
47                crate::backends::onepassword::resolve(vault, item, field)
48            }
49
50            #[cfg(not(feature = "onepassword"))]
51            SecretUri::OnePassword { .. } => Err(SecretError::disabled("1password")),
52
53            #[cfg(feature = "base64")]
54            SecretUri::Base64 { data } => crate::backends::base64::resolve(data),
55
56            #[cfg(not(feature = "base64"))]
57            SecretUri::Base64 { .. } => Err(SecretError::disabled("base64")),
58        }
59    }
60
61    /// Resolve a SecretUri, trimming whitespace from the result
62    pub fn resolve_trimmed(&self, uri: &SecretUri) -> Result<String, SecretError> {
63        self.resolve(uri).map(|s| s.trim().to_string())
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_resolve_plain() {
73        let resolver = SecretResolver::new();
74        let uri = SecretUri::Plain("my-secret".to_string());
75        let result = resolver.resolve(&uri).unwrap();
76        assert_eq!(result, "my-secret");
77    }
78
79    #[test]
80    #[cfg(feature = "env")]
81    fn test_resolve_env() {
82        std::env::set_var("TEST_RESOLVER_SECRET", "env-secret-value");
83        let resolver = SecretResolver::new();
84        let uri = SecretUri::Env {
85            var_name: "TEST_RESOLVER_SECRET".to_string(),
86        };
87        let result = resolver.resolve(&uri).unwrap();
88        assert_eq!(result, "env-secret-value");
89        std::env::remove_var("TEST_RESOLVER_SECRET");
90    }
91}