1use std::collections::HashMap;
9use std::fmt;
10use std::sync::Mutex;
11
12#[derive(Clone, PartialEq, Eq)]
14pub struct Secret(String);
15
16impl Secret {
17 pub fn new(value: impl Into<String>) -> Self {
18 Secret(value.into())
19 }
20 pub fn expose(&self) -> &str {
22 &self.0
23 }
24 pub fn is_empty(&self) -> bool {
25 self.0.is_empty()
26 }
27}
28
29impl fmt::Debug for Secret {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.write_str("Secret([redacted])")
32 }
33}
34
35impl fmt::Display for Secret {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.write_str("[redacted]")
38 }
39}
40
41impl From<String> for Secret {
42 fn from(v: String) -> Self {
43 Secret(v)
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct SecretError(pub String);
49
50impl fmt::Display for SecretError {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "secret store: {}", self.0)
53 }
54}
55impl std::error::Error for SecretError {}
56
57pub trait SecretStore: Send + Sync {
60 fn get(&self, service: &str, account: &str) -> Result<Option<Secret>, SecretError>;
61 fn set(&self, service: &str, account: &str, value: &Secret) -> Result<(), SecretError>;
62 fn delete(&self, service: &str, account: &str) -> Result<(), SecretError>;
63}
64
65#[derive(Default)]
67pub struct MemorySecretStore {
68 inner: Mutex<HashMap<(String, String), Secret>>,
69}
70
71impl MemorySecretStore {
72 pub fn new() -> Self {
73 Self::default()
74 }
75}
76
77impl SecretStore for MemorySecretStore {
78 fn get(&self, service: &str, account: &str) -> Result<Option<Secret>, SecretError> {
79 Ok(self
80 .inner
81 .lock()
82 .unwrap()
83 .get(&(service.to_string(), account.to_string()))
84 .cloned())
85 }
86 fn set(&self, service: &str, account: &str, value: &Secret) -> Result<(), SecretError> {
87 self.inner
88 .lock()
89 .unwrap()
90 .insert((service.to_string(), account.to_string()), value.clone());
91 Ok(())
92 }
93 fn delete(&self, service: &str, account: &str) -> Result<(), SecretError> {
94 self.inner
95 .lock()
96 .unwrap()
97 .remove(&(service.to_string(), account.to_string()));
98 Ok(())
99 }
100}