1use std::fs;
2use std::path::Path;
3
4pub trait Storage {
5 fn exists(&self, path: &str) -> bool;
6 fn read_to_string(&self, path: &str) -> anyhow::Result<String>;
7 fn write_string(&self, path: &str, data: &str) -> anyhow::Result<()>;
8}
9
10pub trait SecretStore: Send + Sync {
11 fn set_secret(&self, key: &str, secret: &str) -> anyhow::Result<()>;
12 fn get_secret(&self, key: &str) -> anyhow::Result<Option<String>>;
13}
14
15#[derive(Clone, Default)]
16pub struct FsStorage;
17
18impl Storage for FsStorage {
19 fn exists(&self, path: &str) -> bool {
20 Path::new(path).exists()
21 }
22 fn read_to_string(&self, path: &str) -> anyhow::Result<String> {
23 Ok(fs::read_to_string(path)?)
24 }
25 fn write_string(&self, path: &str, data: &str) -> anyhow::Result<()> {
26 if let Some(parent) = Path::new(path).parent()
27 && !parent.as_os_str().is_empty()
28 {
29 fs::create_dir_all(parent)?;
30 }
31 Ok(fs::write(path, data)?)
32 }
33}
34
35#[derive(Default)]
36pub struct InMemorySecretStore(std::sync::Mutex<std::collections::HashMap<String, String>>);
37
38impl InMemorySecretStore {
39 pub fn new() -> Self {
40 Self::default()
41 }
42}
43
44impl SecretStore for InMemorySecretStore {
45 fn set_secret(&self, key: &str, secret: &str) -> anyhow::Result<()> {
46 let mut g = self
47 .0
48 .lock()
49 .map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?;
50 g.insert(key.to_string(), secret.to_string());
51 Ok(())
52 }
53 fn get_secret(&self, key: &str) -> anyhow::Result<Option<String>> {
54 let g = self
55 .0
56 .lock()
57 .map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?;
58 Ok(g.get(key).cloned())
59 }
60}