Skip to main content

SecretVo

Trait SecretVo 

Source
pub trait SecretVo: ValueObject {
    type Inner;
    type Error: Error + Send + Sync + 'static;

    // Required methods
    fn try_new(inner: Self::Inner) -> Result<Self, Self::Error>;
    fn expose_secret(&self) -> &Self::Inner;
}
Expand description

A value object that wraps a secret (API key, password, token, …) which must never appear in logs or error messages by accident.

This trait cannot force a redacting Debug impl: ValueObject requires Debug to exist, but nothing in the type system can require what it prints. A hand-written impl SecretVo (as in this crate’s own test fixtures) is only as safe as its own Debug/Display impls - it is the implementor’s responsibility to redact. #[derive(SecretVo)] in ddd-toolkit-macro closes this gap for derived types: it always generates a Debug impl that prints TypeName(***) and never the wrapped value, so prefer the derive over a manual impl wherever possible.

§Examples

use ddd_toolkit_core::domain::{SecretVo, ValidationError, ValueObject};
use std::fmt::{Debug, Display};

#[derive(Clone, PartialEq)]
struct ApiKey(String);

impl Display for ApiKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[REDACTED]")
    }
}

impl Debug for ApiKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "ApiKey([REDACTED])")
    }
}

impl ValueObject for ApiKey {}

impl SecretVo for ApiKey {
    type Inner = String;
    type Error = ValidationError;

    fn try_new(inner: Self::Inner) -> Result<Self, Self::Error> {
        if inner.trim().is_empty() {
            return Err(ValidationError::new("ApiKey", "empty"));
        }
        Ok(Self(inner))
    }

    fn expose_secret(&self) -> &Self::Inner {
        &self.0
    }
}

let key = ApiKey::try_new("sk-live-abc123".to_string())?;

// the raw secret is only reachable through the explicit accessor...
assert_eq!(key.expose_secret(), "sk-live-abc123");

// ...never through Display/Debug, so it can't leak into a log by accident.
assert_eq!(key.to_string(), "[REDACTED]");
assert_eq!(format!("{key:?}"), "ApiKey([REDACTED])");

Required Associated Types§

Source

type Inner

The wrapped secret’s raw type.

Source

type Error: Error + Send + Sync + 'static

The error produced when a value fails validation.

Required Methods§

Source

fn try_new(inner: Self::Inner) -> Result<Self, Self::Error>

Validates and wraps inner.

§Errors

Returns SecretVo::Error if inner fails the implementation’s validation.

Source

fn expose_secret(&self) -> &Self::Inner

Returns the wrapped secret. Named to make call sites (key.expose_secret() rather than key.0) an explicit, greppable signal that a secret is about to be handled directly.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§