1use std::fmt;
2
3#[derive(Clone, PartialEq, Eq)]
9pub struct Secret(String);
10
11impl Secret {
12 pub fn new(value: impl Into<String>) -> Self {
13 Self(value.into())
14 }
15
16 pub fn expose(&self) -> &str {
18 &self.0
19 }
20
21 pub fn into_inner(self) -> String {
22 self.0
23 }
24}
25
26impl fmt::Debug for Secret {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.write_str("Secret(***)")
29 }
30}
31
32impl fmt::Display for Secret {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.write_str("***")
35 }
36}
37
38impl From<&str> for Secret {
39 fn from(value: &str) -> Self {
40 Self::new(value)
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn debug_output_never_contains_the_value() {
50 let secret = Secret::new("ghp_supersecret");
51 assert_eq!(format!("{secret:?}"), "Secret(***)");
52 assert_eq!(format!("{secret}"), "***");
53 assert_eq!(secret.expose(), "ghp_supersecret");
54 }
55}