o402 0.1.4

OpenAI-compatible gateway, paid with x402.
//! Secret wrapper that redacts `Debug` / `Display`.

use std::fmt;

use serde::Deserialize;

/// Wrapper that never prints the inner value.
#[derive(Clone, Deserialize, Eq, PartialEq)]
#[serde(transparent)]
pub(crate) struct Secret<T>(T);

#[cfg_attr(
    not(test),
    allow(dead_code, reason = "constructed via serde; expose is the read path")
)]
impl<T> Secret<T> {
    /// Wrap `inner` so logs cannot print it by accident.
    #[must_use]
    pub(crate) const fn new(inner: T) -> Self {
        Self(inner)
    }

    /// Borrow the inner value. Do not log the result.
    #[must_use]
    pub(crate) const fn expose(&self) -> &T {
        &self.0
    }
}

impl<T> fmt::Debug for Secret<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("***")
    }
}

impl<T> fmt::Display for Secret<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("***")
    }
}

#[cfg(test)]
mod tests {
    use super::Secret;

    #[test]
    fn debug_and_display_redact() {
        let secret = Secret::new("super-secret");
        assert_eq!(format!("{secret:?}"), "***", "debug");
        assert_eq!(format!("{secret}"), "***", "display");
        assert_eq!(secret.expose(), &"super-secret", "expose");
    }
}