1use std::fmt;
4
5#[derive(Clone, PartialEq, Eq, Default)]
10#[cfg_attr(feature = "microsoft", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "microsoft", serde(transparent))]
12pub struct SecretString(String);
13
14impl SecretString {
15 pub fn new(value: impl Into<String>) -> Self {
17 Self(value.into())
18 }
19
20 pub fn expose(&self) -> &str {
22 &self.0
23 }
24
25 pub fn is_empty(&self) -> bool {
27 self.0.is_empty()
28 }
29}
30
31impl From<String> for SecretString {
32 fn from(value: String) -> Self {
33 Self(value)
34 }
35}
36
37impl From<&str> for SecretString {
38 fn from(value: &str) -> Self {
39 Self(value.to_owned())
40 }
41}
42
43impl fmt::Debug for SecretString {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.write_str("[redacted]")
46 }
47}
48
49impl fmt::Display for SecretString {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.write_str("[redacted]")
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn debug_and_display_are_redacted() {
61 let s = SecretString::new("hunter2");
62 assert_eq!(format!("{s:?}"), "[redacted]");
63 assert_eq!(format!("{s}"), "[redacted]");
64 assert_eq!(s.expose(), "hunter2");
65 }
66}