SafeDebug

Derive Macro SafeDebug 

Source
#[derive(SafeDebug)]
Expand description

Derives std::fmt::Debug with automatic redaction for sensitive fields.

§Supported Types

  • Named structs
  • Tuple structs
  • Unit structs
  • Enums (all variant types: unit, tuple, struct)
  • Generic types (with lifetimes and type parameters)
  • Nested structures

§Requirements

  • Must also derive or implement Facet
  • Sensitive fields marked with #[facet(sensitive)]
  • For generic types, type parameters must implement Debug

§Performance

There should be minimal runtime overhead:

  • One Self::SHAPE const access per Debug::fmt call
  • One field.is_sensitive() check per field (O(1) bitflag check)
  • No heap allocations beyond standard Debug formatting
  • Zero cost for non-sensitive fields (formatted normally)

Benchmarks show <1% overhead compared to hand-written Debug implementations, and of course a lot less tedium writing code.

§Security

Fails safe when metadata is unavailable:

  • Structs: Redacts all fields with "[REDACTED:NO_METADATA]"
  • Enums: Outputs only the type name, no variant or field data

§Varied examples

Basic struct with mixed sensitive/non-sensitive fields:

use facet::Facet;
use safe_debug::SafeDebug;

#[derive(Facet, SafeDebug)]
struct PatientRecord {
    id: String,              // public field
    #[facet(sensitive)]
    ssn: String,             // redacted as [REDACTED]
    #[facet(sensitive)]
    medical_history: String, // redacted as [REDACTED]
}

Enums with sensitive fields in specific variants:

#[derive(Facet, SafeDebug)]
enum ApiResponse {
    Success { code: u16, data: String },
    Error {
        code: u16,
        #[facet(sensitive)]
        error_details: String,  // only redacted in Error variant
    },
    Pending(u64),
}

Generic types (automatically adds required trait bounds):

#[derive(Facet, SafeDebug)]
struct Container<T> {
    id: u32,
    #[facet(sensitive)]
    secret: T,  // T will be redacted
}
// Expands to: impl<T: Debug + Facet> Debug for Container<T>

Types with lifetimes:

#[derive(Facet, SafeDebug)]
struct BorrowedData<'a> {
    public: &'a str,
    #[facet(sensitive)]
    token: &'a str,
}