1use std::collections::BTreeMap;
4use std::fmt::{Debug, Formatter};
5use std::sync::Arc;
6
7#[derive(Clone, Default, PartialEq, Eq, PartialOrd)]
9pub struct Secrets {
10 secrets: BTreeMap<String, Arc<str>>,
11}
12
13impl Debug for Secrets {
14 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15 let mut map = f.debug_map();
16 for key in self.secrets.keys() {
17 map.entry(key, &"<redacted secret>");
18 }
19 map.finish()
20 }
21}
22
23impl Secrets {
24 #[must_use]
26 pub fn new() -> Self {
27 Self {
28 secrets: BTreeMap::new(),
29 }
30 }
31
32 #[must_use]
34 pub fn get(&self, key: &str) -> Option<&Arc<str>> {
35 self.secrets.get(key)
36 }
37
38 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Arc<str>>) {
40 self.secrets.insert(key.into(), value.into());
41 }
42
43 pub fn remove(&mut self, key: &str) {
45 self.secrets.remove(key);
46 }
47
48 pub fn merge(&mut self, other: Self) {
50 for (key, value) in other.secrets {
51 self.secrets.entry(key).or_insert(value);
52 }
53 }
54}
55
56#[cfg(test)]
57mod test {
58 use super::Secrets;
59
60 #[test]
61 fn test_merge() {
62 let mut a = Secrets::new();
63 a.insert("key-a", "value-a1");
64 a.insert("key-b", "value-b1");
65
66 let mut b = Secrets::new();
67 b.insert("key-b", "value-b2");
68 b.insert("key-c", "value-c2");
69
70 a.merge(b);
71
72 assert_eq!(a.get("key-a").unwrap().as_ref(), "value-a1");
73 assert_eq!(a.get("key-b").unwrap().as_ref(), "value-b1");
74 assert_eq!(a.get("key-c").unwrap().as_ref(), "value-c2");
75 }
76}