Skip to main content

sensitive_fmt/
lib.rs

1//! Derive `Debug` and `Display` for Rust structs while honoring sensitive
2//! field attributes. Designed for keeping PHI, PII, secrets, and tokens out
3//! of log lines.
4//!
5//! # Example
6//!
7//! ```
8//! use sensitive_fmt::{SensitiveDebug, SensitiveDisplay};
9//!
10//! #[derive(SensitiveDebug, SensitiveDisplay)]
11//! struct Patient {
12//!     id: u64,
13//!     #[sensitive(truncate = 4)] mrn: String,
14//!     #[sensitive(redact)]       email: String,
15//! }
16//!
17//! let p = Patient {
18//!     id: 42,
19//!     mrn: "MRN-12345-WXYZ".into(),
20//!     email: "alice@example.com".into(),
21//! };
22//! assert_eq!(
23//!     format!("{p}"),
24//!     "Patient { id: 42, mrn: ****WXYZ, email: REDACTED }",
25//! );
26//! ```
27//!
28//! See the [README](https://github.com/ceejbot/sensitive-fmt) for the full
29//! attribute reference.
30
31use proc_macro::TokenStream;
32
33mod codegen;
34mod parse;
35
36#[proc_macro_derive(SensitiveDebug, attributes(sensitive))]
37pub fn derive_sensitive_debug(input: TokenStream) -> TokenStream {
38    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
39    match parse::plan_struct(&ast, "SensitiveDebug") {
40        Ok(fields) => codegen::emit_debug_impl(&ast, &fields).into(),
41        Err(err) => err.to_compile_error().into(),
42    }
43}
44
45#[proc_macro_derive(SensitiveDisplay, attributes(sensitive))]
46pub fn derive_sensitive_display(input: TokenStream) -> TokenStream {
47    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
48    match parse::plan_struct(&ast, "SensitiveDisplay") {
49        Ok(fields) => codegen::emit_display_impl(&ast, &fields).into(),
50        Err(err) => err.to_compile_error().into(),
51    }
52}