cargo_credential/secret.rs
1use std::fmt;
2use std::ops::Deref;
3
4use serde::{Deserialize, Serialize};
5
6/// A wrapper for values that should not be printed.
7///
8/// This type does not implement `Display`, and has a `Debug` impl that hides
9/// the contained value.
10///
11/// ```
12/// # use cargo_credential::Secret;
13/// let token = Secret::from("super secret string");
14/// assert_eq!(format!("{:?}", token), "Secret { inner: \"REDACTED\" }");
15/// ```
16///
17/// Currently, we write a borrowed `Secret<T>` as `Secret<&T>`.
18/// The [`as_deref`](Secret::as_deref) and [`to_owned`](Secret::to_owned) methods can
19/// be used to convert back and forth between `Secret<String>` and `Secret<&str>`.
20#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(transparent)]
22pub struct Secret<T> {
23 inner: T,
24}
25
26impl<T> Secret<T> {
27 /// Unwraps the contained value.
28 ///
29 /// Use of this method marks the boundary of where the contained value is
30 /// hidden.
31 pub fn expose(self) -> T {
32 self.inner
33 }
34
35 /// Converts a `Secret<T>` to a `Secret<&T::Target>`.
36 /// ```
37 /// # use cargo_credential::Secret;
38 /// let owned: Secret<String> = Secret::from(String::from("token"));
39 /// let borrowed: Secret<&str> = owned.as_deref();
40 /// ```
41 pub fn as_deref(&self) -> Secret<&<T as Deref>::Target>
42 where
43 T: Deref,
44 {
45 Secret::from(self.inner.deref())
46 }
47
48 /// Converts a `Secret<T>` to a `Secret<&T>`.
49 pub fn as_ref(&self) -> Secret<&T> {
50 Secret::from(&self.inner)
51 }
52
53 /// Converts a `Secret<T>` to a `Secret<U>` by applying `f` to the contained value.
54 pub fn map<U, F>(self, f: F) -> Secret<U>
55 where
56 F: FnOnce(T) -> U,
57 {
58 Secret::from(f(self.inner))
59 }
60}
61
62impl<T: ToOwned + ?Sized> Secret<&T> {
63 /// Converts a `Secret` containing a borrowed type to a `Secret` containing the
64 /// corresponding owned type.
65 /// ```
66 /// # use cargo_credential::Secret;
67 /// let borrowed: Secret<&str> = Secret::from("token");
68 /// let owned: Secret<String> = borrowed.to_owned();
69 /// ```
70 pub fn to_owned(&self) -> Secret<<T as ToOwned>::Owned> {
71 Secret::from(self.inner.to_owned())
72 }
73}
74
75impl<T, E> Secret<Result<T, E>> {
76 /// Converts a `Secret<Result<T, E>>` to a `Result<Secret<T>, E>`.
77 pub fn transpose(self) -> Result<Secret<T>, E> {
78 self.inner.map(|v| Secret::from(v))
79 }
80}
81
82impl<T: AsRef<str>> Secret<T> {
83 /// Checks if the contained value is empty.
84 pub fn is_empty(&self) -> bool {
85 self.inner.as_ref().is_empty()
86 }
87}
88
89impl<T> From<T> for Secret<T> {
90 fn from(inner: T) -> Self {
91 Self { inner }
92 }
93}
94
95impl<T> fmt::Debug for Secret<T> {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.debug_struct("Secret")
98 .field("inner", &"REDACTED")
99 .finish()
100 }
101}