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