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}