Skip to main content

rig_core/wire/
secret.rs

1//! Redacted credentials for serializable provider configuration.
2//!
3//! ```
4//! use rig_core::wire::Secret;
5//!
6//! let secret = Secret::from("api-key");
7//! assert_eq!(format!("{secret:?}"), "[redacted]");
8//! ```
9
10/// Credential whose `Debug` and serialized form contain only `[redacted]`.
11/// Equality compares the stored value; [`Self::expose`] returns it unredacted.
12/// Deserializing `[redacted]` produces an empty credential. Callers must supply
13/// credentials again after reloading serialized configuration.
14#[derive(Clone, Default, PartialEq, Eq, Hash)]
15pub struct Secret(String);
16
17/// Redaction sentinel, deserialized as an empty credential.
18const REDACTED: &str = "[redacted]";
19
20impl Secret {
21    /// The credential itself. Every call site is a place a secret can leak.
22    pub fn expose(&self) -> &str {
23        &self.0
24    }
25
26    /// Whether no credential was supplied.
27    pub fn is_empty(&self) -> bool {
28        self.0.is_empty()
29    }
30}
31
32impl<S: Into<String>> From<S> for Secret {
33    fn from(value: S) -> Self {
34        Self(value.into())
35    }
36}
37
38impl std::fmt::Debug for Secret {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        f.write_str(REDACTED)
41    }
42}
43
44impl serde::Serialize for Secret {
45    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
46        serializer.serialize_str(REDACTED)
47    }
48}
49
50impl<'de> serde::Deserialize<'de> for Secret {
51    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
52        let mut value = <String as serde::Deserialize>::deserialize(deserializer)?;
53        if value == REDACTED {
54            value.clear();
55        }
56        Ok(Self(value))
57    }
58}
59
60#[cfg(test)]
61pub(crate) mod tests;