Skip to main content

scv_client/
secret.rs

1//! A string that must never reach a log or an error message.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// An API key, token, or app secret.
7///
8/// It serializes as the plain string, so settings and credential files keep
9/// their format, but its `Debug` output is `<redacted>`: a struct holding one
10/// can derive `Debug` without leaking it. Read the value with
11/// [`expose`](Self::expose), or through `Deref<Target = str>`, only where it
12/// is sent to the service it belongs to.
13#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
14#[serde(transparent)]
15pub struct Secret(String);
16
17impl Secret {
18    /// Wrap a secret value.
19    #[cfg(test)]
20    pub(crate) fn new(value: impl Into<String>) -> Self {
21        Self(value.into())
22    }
23
24    /// The secret itself.
25    pub fn expose(&self) -> &str {
26        &self.0
27    }
28
29    /// The secret itself, owned.
30    pub fn into_inner(self) -> String {
31        self.0
32    }
33}
34
35impl fmt::Debug for Secret {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.write_str("<redacted>")
38    }
39}
40
41impl std::ops::Deref for Secret {
42    type Target = str;
43
44    fn deref(&self) -> &str {
45        &self.0
46    }
47}
48
49impl From<String> for Secret {
50    fn from(value: String) -> Self {
51        Self(value)
52    }
53}
54
55impl From<&str> for Secret {
56    fn from(value: &str) -> Self {
57        Self(value.to_owned())
58    }
59}
60
61#[cfg(test)]
62mod tests;