1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! Derive macros for `redactable`.
//!
//! This crate generates traversal code behind `#[derive(Sensitive)]`,
//! `#[derive(SensitiveDisplay)]`, `#[derive(NotSensitive)]`, and
//! `#[derive(NotSensitiveDisplay)]`. It:
//! - reads `#[sensitive(...)]` and `#[not_sensitive]` attributes
//! - emits trait implementations for redaction and logging integration
//!
//! It does **not** define policy markers or text policies. Those live in the main
//! `redactable` crate and are applied at runtime.
// <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
// <https://rust-lang.github.io/rust-clippy/stable>
// Allow some clippy lints
// Allow some lints while testing
extern crate proc_macro;
use quote;
use ;
pub use ;
use ;
pub use DeriveOutput;
use ;
/// Derives `redactable::RedactableWithMapper` (and related impls) for structs and enums.
///
/// # Container Attributes
///
/// These attributes are placed on the struct/enum itself:
///
/// `Sensitive` and `SensitiveDisplay` are standalone derives. Use `SensitiveDual` when a type
/// needs both structural and display redaction.
///
/// Use `#[redactable(recursive)]` on a field whose crate-qualified, aliased, or
/// mutually recursive type would otherwise create a self-referential inferred
/// bound. Unannotated fields retain their exact complete-type bounds.
/// `#[redactable(legacy_formatting)]` and `#[redactable(generated_formatting)]`
/// are display-only formatting options; standalone `Sensitive` rejects **both**.
/// Apply them on `SensitiveDisplay` or `SensitiveDual` (which is what to use when
/// a type needs structural and display redaction together); those derives
/// document what each option selects.
///
/// # Field Attributes
///
/// - **No annotation**: The field is traversed by default. Scalars pass through unchanged; nested
/// structs/enums are walked using `RedactableWithMapper` (so external types must implement it).
///
/// - `#[sensitive(Secret)]`: For scalar types (i32, bool, char, etc.), redacts to default values
/// (0, false, '*'). For string-like types, applies full redaction to `"[REDACTED]"`.
///
/// - `#[sensitive(Policy)]`: Applies the policy's redaction rules to string-like
/// values. Works for `String`, `Option<String>`, `Vec<String>`, `Box<String>`. Scalars can only
/// use `#[sensitive(Secret)]`.
///
/// - `#[not_sensitive]`: Explicit passthrough - the field is not transformed at all. Use this
/// for foreign types that don't implement `RedactableWithMapper`. This is equivalent to wrapping
/// the field type in `NotSensitiveValue<T>`, but without changing the type signature.
///
/// Unions are rejected at compile time.
///
/// # Generated Impls
///
/// - `RedactableWithMapper`: always generated.
/// - `Redactable`: always generated. Provides `.redact()` and certifies the type for the
/// redacted-output extension traits (`RedactedOutputExt`, `RedactedJsonExt`, `SlogRedactedExt`).
/// - `Debug`: redacted by default; actual values in the consumer's `cfg(test)` builds or when
/// `redactable`'s `testing` feature is enabled.
/// - `slog::Value` + `SlogRedacted` (requires `slog` feature): borrowed generated output is a
/// fixed fail-closed placeholder and never clones or serializes the raw reference. Owned values
/// can use `SlogRedactedExt::slog_redacted_json` for redact-then-serialize structured output.
/// - `TracingRedacted` (requires `tracing` feature): marker trait.
/// Derives structural and display redaction as one authenticated expansion.
///
/// Use this instead of combining `Sensitive` and `SensitiveDisplay` with the
/// legacy `#[sensitive(dual)]` coordination attribute.
/// Derives a no-op `redactable::RedactableWithMapper` implementation, along with
/// `slog::Value` / `SlogRedacted` and `TracingRedacted`.
///
/// This is useful for types that are known to be non-sensitive but still need to
/// satisfy `RedactableWithMapper` / `Redactable` bounds. Because the type has no
/// sensitive data, logging integration works without wrappers.
///
/// # Generated Impls
///
/// - `RedactableWithMapper`: no-op passthrough (the type has no sensitive data)
/// - `Redactable`: deriving `NotSensitive` is an explicit declaration, so the type is
/// certified for consuming and borrowed adapters. Generated slog serialization borrows rather
/// than clones; serde's `RefCell` implementation
/// reports an active mutable borrow as an error, which is converted to `"[REDACTED]"`.
/// - `slog::Value` and `SlogRedacted` (behind `cfg(feature = "slog")`): serializes the explicitly
/// non-sensitive value directly as structured JSON. Requires `Serialize` on the type.
/// - `TracingRedacted` (behind `cfg(feature = "tracing")`): marker trait
///
/// `NotSensitive` does **not** generate a `Debug` impl - there's nothing to redact.
/// Use `#[derive(Debug)]` when needed.
///
/// # Rejected Attributes
///
/// `#[sensitive]` and `#[not_sensitive]` attributes are rejected on both the container
/// and its fields - the former is wrong (the type is explicitly non-sensitive), the
/// latter is redundant (the entire type is already non-sensitive).
///
/// Unions are rejected at compile time.
/// Derives `redactable::RedactableWithFormatter` for types with no sensitive data.
///
/// This is the display counterpart to `NotSensitive`. Use it when you have a type
/// with no sensitive data that needs logging integration (e.g., for use with slog).
///
/// Unlike `SensitiveDisplay`, this derive does **not** require a display template.
/// Instead, it delegates directly to the type's existing `Display` implementation.
///
/// # Required Bounds
///
/// The type must implement `Display`. This is required because `RedactableWithFormatter` delegates
/// to `Display::fmt`.
///
/// # Generated Impls
///
/// - `RedactableWithMapper`: no-op passthrough (allows use inside `Sensitive` containers)
/// - `Redactable`: deriving `NotSensitiveDisplay` is an explicit declaration, so the type is
/// certified for consuming and borrowed adapters.
/// - `RedactableWithFormatter`: delegates to `Display::fmt`
/// - `ToRedactedOutput`: emits the `Display` text; certifies the type for
/// `slog_redacted_display()` and `tracing_redacted()`
/// - `slog::Value` and `SlogRedacted` (behind `cfg(feature = "slog")`): uses `RedactableWithFormatter` output
/// - `TracingRedacted` (behind `cfg(feature = "tracing")`): marker trait
///
/// # Debug
///
/// `NotSensitiveDisplay` does **not** generate a `Debug` impl - there's nothing to redact.
/// Use `#[derive(Debug)]` alongside `NotSensitiveDisplay` when needed.
///
/// # Rejected Attributes
///
/// `#[sensitive]` and `#[not_sensitive]` attributes are rejected on both the container
/// and its fields - the former is wrong (the type is explicitly non-sensitive), the
/// latter is redundant (the entire type is already non-sensitive).
///
/// # Example
///
/// ```ignore
/// use redactable::NotSensitiveDisplay;
/// use std::fmt;
///
/// #[derive(NotSensitiveDisplay)]
/// enum RetryDecision {
/// Retry,
/// Abort,
/// }
///
/// impl fmt::Display for RetryDecision {
/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
/// formatter.write_str(match self {
/// Self::Retry => "Retry",
/// Self::Abort => "Abort",
/// })
/// }
/// }
///
/// assert_eq!(RetryDecision::Retry.to_string(), "Retry");
/// ```
/// Derives `redactable::RedactableWithFormatter` using a display template.
///
/// This generates a redacted string representation without requiring `Clone`.
/// Unannotated fields use `RedactableWithFormatter` by default (passthrough for scalars,
/// redacted display for nested `SensitiveDisplay` types).
///
/// # Field Annotations
///
/// - *(none)*: Uses `RedactableWithFormatter` (requires the field type to implement it)
/// - `#[sensitive(Policy)]`: Apply the policy's redaction rules
/// - `#[not_sensitive]`: Render raw via `Display` (use for types without `RedactableWithFormatter`)
///
/// The display template is taken from `#[error("...")]` (thiserror-style) or from
/// doc comments (displaydoc-style). If neither is present, the derive fails.
///
/// Fields are redacted by reference, so field types do not need `Clone`.
/// A custom `PolicyApplicableRef` leaf nested inside a container can explicitly
/// select its ordinary borrowed projection with
/// `#[redactable(legacy_formatting)]`. The explicit route does not require the
/// direct-leaf formatting marker; it requires `PolicyApplicableRef` on the whole
/// field and the selected format capability on its output. It inherits the
/// projection's `Clone` requirements and borrow behavior; library-owned fields
/// should stay on the default conflict-safe route. It composes with
/// `#[redactable(recursive)]`, retaining the projection/output bounds while
/// suppressing the cyclic inferred field bound.
///
/// `#[redactable(generated_formatting)]` instead selects the library-owned
/// recursive formatter for an alias-hidden or otherwise ambiguous container
/// field. `legacy_formatting` and `generated_formatting` are mutually exclusive,
/// and standalone `Sensitive` rejects both (they only affect display output).
///
/// Use `SensitiveDual` instead when the same type also needs structural redaction.
///
/// # Generated Impls
///
/// - `RedactableWithFormatter`: always generated.
/// - `ToRedactedOutput`: always generated; emits the redacted display text and certifies the
/// type for `slog_redacted_display()` and `tracing_redacted()`.
/// - `Debug`: redacted by default; actual values in the consumer's `cfg(test)` builds or when
/// `redactable`'s `testing` feature is enabled.
/// - `slog::Value` + `SlogRedacted`: emits the redacted display string (requires `slog` feature).
/// - `TracingRedacted`: marker trait (requires `tracing` feature).