Skip to main content

kasapay_core/
secret.rs

1//! A string that does not print itself.
2
3use std::fmt;
4
5/// Holds a credential and keeps it out of `Debug` output and logs.
6///
7/// The value is still readable through [`Secret::expose`]; this guards against
8/// a stray `{:?}` on a config struct, not against a determined caller.
9#[derive(Clone, PartialEq, Eq)]
10pub struct Secret(String);
11
12impl Secret {
13    /// Wraps a credential.
14    pub fn new(value: impl Into<String>) -> Self {
15        Self(value.into())
16    }
17
18    /// Reads the credential, for the one place that has to send it.
19    #[must_use]
20    pub fn expose(&self) -> &str {
21        &self.0
22    }
23}
24
25impl fmt::Debug for Secret {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        f.write_str("Secret(***)")
28    }
29}
30
31impl From<String> for Secret {
32    fn from(value: String) -> Self {
33        Self(value)
34    }
35}
36
37impl From<&str> for Secret {
38    fn from(value: &str) -> Self {
39        Self(value.to_owned())
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::Secret;
46
47    #[test]
48    fn debug_output_omits_the_value() {
49        let secret = Secret::new("sandbox-key-value");
50        assert!(!format!("{secret:?}").contains("sandbox-key-value"));
51        assert_eq!(secret.expose(), "sandbox-key-value");
52    }
53}