1use std::fmt;
4
5pub struct Secret<T>(T);
17
18impl<T> Secret<T> {
19 #[inline]
21 pub fn new(value: T) -> Self {
22 Self(value)
23 }
24
25 #[inline]
27 pub fn expose(&self) -> &T {
28 &self.0
29 }
30
31 #[inline]
33 pub fn into_inner(self) -> T {
34 self.0
35 }
36}
37
38impl<T> fmt::Debug for Secret<T> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "<redacted>")
41 }
42}
43
44impl<T> fmt::Display for Secret<T> {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "<redacted>")
47 }
48}
49
50impl<T: Clone> Clone for Secret<T> {
51 fn clone(&self) -> Self {
52 Self(self.0.clone())
53 }
54}
55
56impl<T: PartialEq> PartialEq for Secret<T> {
57 fn eq(&self, other: &Self) -> bool {
58 self.0 == other.0
59 }
60}
61
62impl<T: Eq> Eq for Secret<T> {}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn debug_is_redacted() {
70 let s = Secret::new("hunter2".to_string());
71 assert_eq!(format!("{s:?}"), "<redacted>");
72 }
73
74 #[test]
75 fn display_is_redacted() {
76 let s = Secret::new(42u32);
77 assert_eq!(format!("{s}"), "<redacted>");
78 }
79
80 #[test]
81 fn expose_returns_inner() {
82 let s = Secret::new("value");
83 assert_eq!(*s.expose(), "value");
84 }
85
86 #[test]
87 fn into_inner_unwraps() {
88 let s = Secret::new(99u8);
89 assert_eq!(s.into_inner(), 99);
90 }
91
92 #[test]
93 fn clone_and_eq() {
94 let a = Secret::new("x".to_string());
95 let b = a.clone();
96 assert_eq!(a, b);
97 }
98}