Skip to main content

brainos_vault/
backend.rs

1//! Backend dispatch — one enum with cfg-gated variants.
2
3use crate::file::FileBackend;
4use crate::inject::{CredentialMetadata, CredentialValue, InjectedCredential, InjectionShape};
5use crate::vault::VaultError;
6
7#[cfg(target_os = "macos")]
8use crate::keychain::KeychainBackend;
9
10#[cfg(target_os = "linux")]
11use crate::keyring::SecretServiceBackend;
12
13/// Which backend is active. Exposed to callers for audit metadata.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum BackendKind {
16    Keychain,
17    SecretService,
18    File,
19}
20
21impl std::fmt::Display for BackendKind {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            BackendKind::Keychain => write!(f, "keychain"),
25            BackendKind::SecretService => write!(f, "secret-service"),
26            BackendKind::File => write!(f, "file"),
27        }
28    }
29}
30
31/// A runtime-selected backend. The enum avoids dyn-dispatch for this small
32/// closed set and keeps cfg gates localized.
33pub enum VaultBackend {
34    #[cfg(target_os = "macos")]
35    Keychain(KeychainBackend),
36    #[cfg(target_os = "linux")]
37    SecretService(SecretServiceBackend),
38    File(FileBackend),
39}
40
41impl VaultBackend {
42    pub fn kind(&self) -> BackendKind {
43        match self {
44            #[cfg(target_os = "macos")]
45            VaultBackend::Keychain(_) => BackendKind::Keychain,
46            #[cfg(target_os = "linux")]
47            VaultBackend::SecretService(_) => BackendKind::SecretService,
48            VaultBackend::File(_) => BackendKind::File,
49        }
50    }
51
52    pub async fn store(
53        &self,
54        tool: &str,
55        key: &str,
56        value: CredentialValue,
57        shape: InjectionShape,
58    ) -> Result<(), VaultError> {
59        match self {
60            #[cfg(target_os = "macos")]
61            VaultBackend::Keychain(b) => b.store(tool, key, value, shape).await,
62            #[cfg(target_os = "linux")]
63            VaultBackend::SecretService(b) => b.store(tool, key, value, shape).await,
64            VaultBackend::File(b) => b.store(tool, key, value, shape).await,
65        }
66    }
67
68    pub async fn get(&self, tool: &str, key: &str) -> Result<InjectedCredential, VaultError> {
69        match self {
70            #[cfg(target_os = "macos")]
71            VaultBackend::Keychain(b) => b.get(tool, key).await,
72            #[cfg(target_os = "linux")]
73            VaultBackend::SecretService(b) => b.get(tool, key).await,
74            VaultBackend::File(b) => b.get(tool, key).await,
75        }
76    }
77
78    pub async fn delete(&self, tool: &str, key: &str) -> Result<(), VaultError> {
79        match self {
80            #[cfg(target_os = "macos")]
81            VaultBackend::Keychain(b) => b.delete(tool, key).await,
82            #[cfg(target_os = "linux")]
83            VaultBackend::SecretService(b) => b.delete(tool, key).await,
84            VaultBackend::File(b) => b.delete(tool, key).await,
85        }
86    }
87
88    pub async fn list(&self, tool: Option<&str>) -> Result<Vec<CredentialMetadata>, VaultError> {
89        match self {
90            #[cfg(target_os = "macos")]
91            VaultBackend::Keychain(b) => b.list(tool).await,
92            #[cfg(target_os = "linux")]
93            VaultBackend::SecretService(b) => b.list(tool).await,
94            VaultBackend::File(b) => b.list(tool).await,
95        }
96    }
97}