Skip to main content

qubit_redact/
lib.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8#![cfg_attr(all(doctest, feature = "http", feature = "serde"), doc = include_str!("../README.md"))]
9#![cfg_attr(all(doctest, feature = "http", feature = "serde"), doc = include_str!("../README.zh_CN.md"))]
10#![cfg_attr(all(doctest, feature = "http", feature = "serde"), doc = include_str!("../doc/user_guide.md"))]
11#![cfg_attr(all(doctest, feature = "http", feature = "serde"), doc = include_str!("../doc/user_guide.zh_CN.md"))]
12//! # Qubit Redact
13//!
14//! Policy-driven, bounded redaction for fields, domain values, and diagnostic
15//! formats. [`RedactedTextComposer`] builds one ordered text result, while
16//! [`RedactionBatch`] builds independently resolvable results. Each object is
17//! single-use and publishes only through its consuming `finish` method.
18//!
19//! ```
20//! use qubit_redact::Redactor;
21//!
22//! let output = Redactor::strict()
23//!     .text_composer()
24//!     .literal("password=")
25//!     .field("password", "raw-secret")
26//!     .finish();
27//! assert!(!output.text().as_str().contains("raw-secret"));
28//! ```
29//!
30//! # Safety boundary
31//!
32//! `literal` accepts only `&'static str` program literals. Dynamic text must
33//! be passed to a redaction operation. Derived fields that lack
34//! `#[redact(...)]` are intentionally unredacted. Field sensitivity belongs to
35//! the downstream domain: the framework cannot infer it reliably, and forcing
36//! explicit "not sensitive" annotations onto the ordinary majority of fields
37//! would add noise rather than knowledge. Downstream types must explicitly mark
38//! sensitive fields and review that classification when their model changes.
39//! Fields that explicitly use `skip` are neither accessed nor emitted.
40//!
41//! With redaction enabled, `Complete`, `Truncated`, and `Exhausted` output text
42//! remains confidentiality-safe. Diagnostic formatters may publish that safe
43//! text without interpreting an incompleteness reason; callers inspect
44//! summaries only when completeness affects their own program contract.
45//!
46//! A disabled application-default policy is an intentional process-wide
47//! debugging escape hatch. It restores raw values. The framework executes the
48//! selected policy, while downstream code owns authorization, timing, and any
49//! misuse. Generated `Debug`, `Display`, and `Serialize` implementations
50//! intentionally obtain [`Redactor::application_default`] at the start of each
51//! formatting or serialization call. Replacing the application default
52//! therefore affects future generated calls, including installation of a
53//! disabled policy. Existing explicit redactors, composers, and batches retain
54//! the policy snapshots they already own.
55//!
56//! Transaction summaries are observations produced exclusively by a completed
57//! transaction; callers cannot fabricate one outside the runtime.
58//!
59//! ```compile_fail
60//! use qubit_redact::RedactionSummary;
61//!
62//! let _ = RedactionSummary::complete();
63//! ```
64//!
65//! The removed pre-0.5 transaction API cannot be imported as a public
66//! compatibility API.
67//!
68//! ```compile_fail
69//! use qubit_redact::RedactionSession;
70//! ```
71//!
72//! ```compile_fail
73//! use qubit_redact::RedactionSessionOutput;
74//! ```
75//!
76//! ```compile_fail
77//! use qubit_redact::RedactionOutput;
78//! ```
79//!
80//! ```compile_fail
81//! use qubit_redact::RedactionHandle;
82//! ```
83//!
84//! ```compile_fail
85//! use qubit_redact::RedactionHandleError;
86//! ```
87//!
88//! ```compile_fail
89//! use qubit_redact::Redactor;
90//!
91//! let _ = Redactor::strict().session();
92//! ```
93//!
94//! Composer and batch APIs deliberately do not overlap, and both publication
95//! methods consume their owner.
96//!
97//! ```compile_fail
98//! use qubit_redact::Redactor;
99//!
100//! let composer = Redactor::strict().text_composer();
101//! let _ = composer.finish();
102//! let _ = composer.literal("cannot reuse a finished composer");
103//! ```
104//!
105//! ```compile_fail
106//! use qubit_redact::Redactor;
107//!
108//! let mut batch = Redactor::strict().batch();
109//! batch.literal("batch has no aggregate text API");
110//! ```
111//!
112//! ```compile_fail
113//! use qubit_redact::Redactor;
114//!
115//! let mut batch = Redactor::strict().batch();
116//! let _ = batch.redact_field("password", "raw-secret");
117//! let _ = batch.finish_for_diagnostics("<redaction incomplete>");
118//! let _ = batch.redact_field("password", "cannot reuse a finished batch");
119//! ```
120//!
121//! ```compile_fail
122//! use qubit_redact::Redactor;
123//!
124//! let composer = Redactor::strict().text_composer();
125//! let _ = composer.redact_field("password", "batch methods are unavailable");
126//! ```
127//!
128//! ```compile_fail
129//! use qubit_redact::Redactor;
130//!
131//! let mut batch = Redactor::strict().batch();
132//! let handle = batch.redact_field("password", "raw-secret");
133//! let output = batch.finish_for_diagnostics("<redaction incomplete>");
134//! let _ = output.text(handle);
135//! let _ = handle.to_string();
136//! ```
137//!
138//! The domain-level rendering traits do not provide an alternate output path.
139//! Domain values must be written through [`Redact`] and a
140//! [`RedactedTextComposer`] or [`RedactionBatch`].
141//!
142//! ```compile_fail
143//! use qubit_redact::policy::RedactionPolicy;
144//! ```
145
146extern crate self as qubit_redact;
147
148#[cfg(feature = "derive")]
149pub use qubit_redact_derive::Redact;
150
151#[doc(hidden)]
152pub mod domain;
153mod facade;
154pub mod formats;
155mod json_feature_gate;
156mod output;
157mod policy;
158pub(crate) mod runtime;
159mod serde_feature_gate;
160
161pub use domain::Redact;
162pub use domain::RedactionWriter;
163pub use facade::DebugDisplay;
164pub use facade::RedactedText;
165pub use facade::RedactedTextComposer;
166pub use facade::RedactionBatch;
167pub use facade::RedactionBatchDiagnostics;
168pub use facade::RedactionBatchHandle;
169pub use facade::RedactionInspection;
170pub use facade::RedactionInspectionError;
171pub use facade::RedactionReason;
172pub use facade::RedactionReasons;
173pub use facade::RedactionSummary;
174pub use facade::RedactionTextOutput;
175pub use facade::RedactionUsage;
176pub use facade::Redactor;
177pub use output::RedactionCompletion;
178pub use policy::AllowRule;
179pub use policy::FieldClassification;
180pub use policy::FieldMatchKind;
181pub use policy::FieldNameMatching;
182pub use policy::FieldsBuilder;
183#[cfg(feature = "http")]
184pub use policy::HttpContextBuilderView;
185#[cfg(feature = "http")]
186pub use policy::HttpPolicyBuilderView;
187pub use policy::MaskPolicy;
188pub use policy::MaskingPolicy;
189pub use policy::MaskingPolicyBuilder;
190pub use policy::PolicyError;
191pub use policy::PolicyLocation;
192pub use policy::RedactionFloor;
193pub use policy::RedactionFloorBuilder;
194pub use policy::RedactionLimits;
195pub use policy::RedactionLimitsBuilder;
196pub use policy::RedactionPolicy;
197pub use policy::RedactionPolicyBuilder;
198pub use policy::RedactionRules;
199pub use policy::SensitiveFieldPreset;
200pub use policy::SensitiveFieldRule;
201pub use policy::Sensitivity;
202#[cfg(feature = "json")]
203pub use policy::UnkeyedJsonValuePolicy;
204pub use policy::UnknownFieldPolicy;
205#[cfg(feature = "uri")]
206pub use policy::UriPolicyBuilderView;
207pub(crate) use runtime::RedactionHandle;
208pub(crate) use runtime::RedactionHandleError;