1#[derive(Default)]
2pub struct Redactor {
3 secrets: Vec<String>,
4}
5
6impl Redactor {
7 pub fn new() -> Self {
8 Self::default()
9 }
10
11 pub fn add(&mut self, secret: &str) {
12 if secret.is_empty() {
13 return;
14 }
15
16 self.secrets.push(secret.to_owned());
17 }
18
19 pub fn redact(&self, value: &str) -> String {
20 let mut redacted = value.to_owned();
21 for secret in &self.secrets {
22 redacted = redacted.replace(secret, "[REDACTED]");
23 }
24 redacted
25 }
26}
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31
32 #[test]
33 fn redacts_registered_secret() {
34 let mut redactor = Redactor::new();
35 redactor.add("secret-token");
36
37 assert_eq!(redactor.redact("token=secret-token"), "token=[REDACTED]");
38 }
39
40 #[test]
41 fn ignores_empty_secret() {
42 let mut redactor = Redactor::new();
43 redactor.add("");
44
45 assert_eq!(redactor.redact("abc"), "abc");
46 }
47}