diff --git a/Cargo.lock b/Cargo.lock index 9ec799500..326ca8015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -846,10 +846,12 @@ dependencies = [ [[package]] name = "data_privacy" -version = "0.11.0" +version = "0.12.0" dependencies = [ + "data_privacy_core", "data_privacy_macros", "derive_more", + "insta", "mutants", "once_cell", "rapidhash", @@ -860,9 +862,20 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "data_privacy_core" +version = "0.1.0" +dependencies = [ + "insta", + "mutants", + "serde", + "serde_core", + "serde_json", +] + [[package]] name = "data_privacy_macros" -version = "0.9.0" +version = "0.10.0" dependencies = [ "data_privacy_macros_impl", "mutants", @@ -870,7 +883,7 @@ dependencies = [ [[package]] name = "data_privacy_macros_impl" -version = "0.9.0" +version = "0.10.0" dependencies = [ "insta", "mutants", diff --git a/Cargo.toml b/Cargo.toml index 86504f015..8c1931adc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,10 @@ cachet = { path = "crates/cachet", default-features = false, version = "0.5.1" } cachet_memory = { path = "crates/cachet_memory", default-features = false, version = "0.2.1" } cachet_service = { path = "crates/cachet_service", default-features = false, version = "0.1.0" } cachet_tier = { path = "crates/cachet_tier", default-features = false, version = "0.1.0" } -data_privacy = { path = "crates/data_privacy", default-features = false, version = "0.11.0" } -data_privacy_macros = { path = "crates/data_privacy_macros", default-features = false, version = "0.9.0" } -data_privacy_macros_impl = { path = "crates/data_privacy_macros_impl", default-features = false, version = "0.9.0" } +data_privacy = { path = "crates/data_privacy", default-features = false, version = "0.12.0" } +data_privacy_core = { path = "crates/data_privacy_core", default-features = false, version = "0.1.0" } +data_privacy_macros = { path = "crates/data_privacy_macros", default-features = false, version = "0.10.0" } +data_privacy_macros_impl = { path = "crates/data_privacy_macros_impl", default-features = false, version = "0.10.0" } fetch_hyper = { path = "crates/fetch_hyper", default-features = false, version = "0.1.1" } fundle = { path = "crates/fundle", default-features = false, version = "0.3.0" } fundle_macros = { path = "crates/fundle_macros", default-features = false, version = "0.3.0" } diff --git a/crates/anyspawn/README.md b/crates/anyspawn/README.md index 0f1959b2b..797df1987 100644 --- a/crates/anyspawn/README.md +++ b/crates/anyspawn/README.md @@ -54,7 +54,7 @@ contention-free, NUMA-friendly task dispatch. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG5RxOwZfGEjeG2_e5NBDlvq5G2UGvhnaHUvnGwpHmIxmYGfgYWSCgmhhbnlzcGF3bmUwLjUuMIJsdGhyZWFkX2F3YXJlZTAuNy4w + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQblHE7Bl8YSN4bb97k0EOW-rkbZQa-GdodS-cbCkeYjGZgZ-BhZIKCaGFueXNwYXduZTAuNS4wgmx0aHJlYWRfYXdhcmVlMC43LjA [__link0]: https://docs.rs/anyspawn/0.5.0/anyspawn/?search=Spawner [__link1]: https://docs.rs/anyspawn/0.5.0/anyspawn/?search=SpawnCustom [__link2]: https://docs.rs/anyspawn/0.5.0/anyspawn/?search=CustomSpawnerBuilder diff --git a/crates/bytesbuf/README.md b/crates/bytesbuf/README.md index e3548c0fc..a021c1078 100644 --- a/crates/bytesbuf/README.md +++ b/crates/bytesbuf/README.md @@ -471,7 +471,7 @@ See the `mem::testing` module for details (requires `test-util` Cargo feature). This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG91Jt_IDJ8uaGzw7UvcHuNDdG2jPnTC62v9nG8FGesia3fwjYWSBgmhieXRlc2J1ZmUwLjUuMA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQb3Um38gMny5obPDtS9we40N0baM-dMLra_2cbwUZ6yJrd_CNhZIGCaGJ5dGVzYnVmZTAuNS4w [__link0]: https://docs.rs/bytesbuf/0.5.0/bytesbuf/?search=BytesBuf [__link1]: https://docs.rs/bytesbuf/0.5.0/bytesbuf/?search=BytesView [__link10]: https://docs.rs/bytesbuf/0.5.0/bytesbuf/?search=BytesView diff --git a/crates/bytesbuf_io/README.md b/crates/bytesbuf_io/README.md index b91e72d82..02ba12248 100644 --- a/crates/bytesbuf_io/README.md +++ b/crates/bytesbuf_io/README.md @@ -35,7 +35,7 @@ types that produce or consume streams of bytes. These are in the `testing` modul This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG8wC2qKB5vPMG5c8vZ8ulsvSGwSlXV9Pnjr5GyAAXY8thjNKYWSBgmtieXRlc2J1Zl9pb2UwLjUuMA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbzALaooHm88wblzy9ny6Wy9IbBKVdX0-eOvkbIABdjy2GM0phZIGCa2J5dGVzYnVmX2lvZTAuNS4w [__link0]: https://docs.io/bytesbuf [__link1]: https://docs.rs/bytesbuf_io/0.5.0/bytesbuf_io/?search=Read [__link2]: https://docs.rs/bytesbuf_io/0.5.0/bytesbuf_io/?search=Write diff --git a/crates/data_privacy/CHANGELOG.md b/crates/data_privacy/CHANGELOG.md index a438ef7b7..db9f7893a 100644 --- a/crates/data_privacy/CHANGELOG.md +++ b/crates/data_privacy/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [0.12.0] - 2026-05-28 + +- ✨ Features + + - Introduce the `Redactor` trait as the primary interface for redaction, replacing direct use of `RedactionEngine` in trait signatures. + - `RedactionEngine` now implements the `Redactor` trait. + - `RedactedDebug::fmt` and `RedactedDisplay::fmt` now accept `&dyn Redactor` instead of `&RedactionEngine`. + - `RedactedToString::to_redacted_string` now accepts `&dyn Redactor` instead of `&RedactionEngine`. + - Rename `would_redact` into `redacts` and implement it for all redactors. + - Move core types and traits (`Classified`, `DataClass`, `IntoDataClass`, `RedactedDebug`, `RedactedDisplay`, `RedactedToString`, `Redactor`) to the new `data_privacy_core` crate, re-exported from `data_privacy`. + ## [0.11.0] - 2026-03-20 - ✨ Features diff --git a/crates/data_privacy/Cargo.toml b/crates/data_privacy/Cargo.toml index 1bf2d7172..3ba260431 100644 --- a/crates/data_privacy/Cargo.toml +++ b/crates/data_privacy/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "data_privacy" description = "Data annotation and redaction system providing a robust way to manipulate sensitive information." -version = "0.11.0" +version = "0.12.0" readme = "README.md" keywords = ["oxidizer", "compliance", "privacy", "redaction", "scrubbing"] categories = ["data-structures"] @@ -23,6 +23,7 @@ all-features = true allowed_external_types = [ "serde_core::de::*", "serde_core::ser::*", + "data_privacy_core::*", "data_privacy_macros::*", "rapidhash::v3::seed::RapidSecrets", ] @@ -30,10 +31,11 @@ allowed_external_types = [ [features] default = ["serde"] rapidhash = ["dep:rapidhash"] -serde = ["dep:serde_core"] +serde = ["dep:serde_core", "data_privacy_core/serde"] xxh3 = ["dep:xxhash-rust"] [dependencies] +data_privacy_core.workspace = true data_privacy_macros.workspace = true rapidhash = { workspace = true, optional = true } rustc-hash = { workspace = true, features = ["std"] } @@ -42,6 +44,7 @@ xxhash-rust = { workspace = true, optional = true, features = ["xxh3"] } [dev-dependencies] derive_more = { workspace = true, features = ["constructor", "from"] } +insta.workspace = true mutants.workspace = true once_cell = { workspace = true, features = ["std"] } serde = { workspace = true, features = ["std", "derive"] } diff --git a/crates/data_privacy/README.md b/crates/data_privacy/README.md index 04793af30..6bccea3f7 100644 --- a/crates/data_privacy/README.md +++ b/crates/data_privacy/README.md @@ -55,9 +55,10 @@ This crate is built around two primary traits: * The [`Classified`][__link0] trait is used to mark types that hold sensitive data. -* The [`Redactor`][__link1] trait defines the logic needed by an individual redactor. This crate provides a - few implementations of this trait, such as [`SimpleRedactor`][__link2], but others can - be implemented and used by applications as well. +* The [`Redactor`][__link1] trait defines the interface for applying redaction. Both + [`RedactionEngine`][__link2] (the high-level engine that routes data classes to + strategies) and individual redaction strategies (e.g. hash-based or + replacement-based redactors) implement this trait. This crate also exposes additional traits which are usually, but not necessarily, implemented by types that implement the [`Classified`][__link3] trait: @@ -191,19 +192,19 @@ assert_eq!(output_buffer, "********"); This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG3M_vhUtB5gFG-D1ZeP7gWyXGzaYsyQD3GO7GwPXvYMSh3Z2YWSBgmxkYXRhX3ByaXZhY3lmMC4xMS4w - [__link0]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=Classified - [__link1]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=Redactor - [__link10]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=classified - [__link11]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=taxonomy - [__link12]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=classified - [__link13]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=RedactionEngine - [__link14]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=RedactionEngine::builder - [__link2]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=simple_redactor::SimpleRedactor - [__link3]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=Classified - [__link4]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=RedactedDebug - [__link5]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=RedactedDisplay - [__link6]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=RedactedToString - [__link7]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=taxonomy - [__link8]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=DataClass - [__link9]: https://docs.rs/data_privacy/0.11.0/data_privacy/?search=Classified + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbfPAU5OoNavUbRy0yQBG8se0b5QdS3UiPpR4bCQr6vjqou8phZIOCbGRhdGFfcHJpdmFjeWYwLjEyLjCCcWRhdGFfcHJpdmFjeV9jb3JlZTAuMS4wgnNkYXRhX3ByaXZhY3lfbWFjcm9zZjAuMTAuMA + [__link0]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Classified + [__link1]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Redactor + [__link10]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=classified + [__link11]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=taxonomy + [__link12]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=classified + [__link13]: https://docs.rs/data_privacy/0.12.0/data_privacy/?search=RedactionEngine + [__link14]: https://docs.rs/data_privacy/0.12.0/data_privacy/?search=RedactionEngine::builder + [__link2]: https://docs.rs/data_privacy/0.12.0/data_privacy/?search=RedactionEngine + [__link3]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Classified + [__link4]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=RedactedDebug + [__link5]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=RedactedDisplay + [__link6]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=RedactedToString + [__link7]: https://docs.rs/data_privacy_macros/0.10.0/data_privacy_macros/?search=taxonomy + [__link8]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=DataClass + [__link9]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Classified diff --git a/crates/data_privacy/src/lib.rs b/crates/data_privacy/src/lib.rs index f78b1f9a5..5e1e72bf8 100644 --- a/crates/data_privacy/src/lib.rs +++ b/crates/data_privacy/src/lib.rs @@ -45,9 +45,10 @@ //! //! * The [`Classified`] trait is used to mark types that hold sensitive data. //! -//! * The [`Redactor`] trait defines the logic needed by an individual redactor. This crate provides a -//! few implementations of this trait, such as [`SimpleRedactor`](simple_redactor::SimpleRedactor), but others can -//! be implemented and used by applications as well. +//! * The [`Redactor`] trait defines the interface for applying redaction. Both +//! [`RedactionEngine`] (the high-level engine that routes data classes to +//! strategies) and individual redaction strategies (e.g. hash-based or +//! replacement-based redactors) implement this trait. //! //! This crate also exposes additional traits which are usually, but not necessarily, implemented by types that implement the //! [`Classified`] trait: @@ -177,29 +178,27 @@ #![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/data_privacy/logo.png")] #![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/data_privacy/favicon.ico")] -// Needed for the `taxonomy` macro to be able to use `data_privacy` instead of `crate` in examples -// Workaround for https://github.com/bkchr/proc-macro-crate/issues/14 +// Needed for the `taxonomy` macro to be able to use `data_privacy` instead of `crate` in examples. extern crate self as data_privacy; -mod classified; -mod data_class; -mod macros; -mod redacted; +// Re-export types and traits from data_privacy_core. +#[doc(inline)] +pub use data_privacy_core::{Classified, DataClass, IntoDataClass, RedactedDebug, RedactedDisplay, RedactedToString, Redactor}; +// Re-export derive macros and attribute macros. +#[doc(inline)] +pub use data_privacy_macros::{RedactedDebug, RedactedDisplay, classified, taxonomy}; + mod redaction_engine; mod redaction_engine_builder; mod redaction_engine_inner; mod redactors; mod sensitive; -pub use classified::Classified; -pub use data_class::{DataClass, IntoDataClass}; -pub use macros::{RedactedDebug, RedactedDisplay, classified, taxonomy}; -pub use redacted::{RedactedDebug, RedactedDisplay, RedactedToString}; pub use redaction_engine::RedactionEngine; pub use redaction_engine_builder::RedactionEngineBuilder; #[cfg(feature = "rapidhash")] pub use redactors::rapidhash_redactor; +pub use redactors::simple_redactor; #[cfg(feature = "xxh3")] pub use redactors::xxh3_redactor; -pub use redactors::{Redactor, simple_redactor}; pub use sensitive::Sensitive; diff --git a/crates/data_privacy/src/macros.rs b/crates/data_privacy/src/macros.rs deleted file mode 100644 index c002d9a12..000000000 --- a/crates/data_privacy/src/macros.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/// Derives the [`RedactedDebug`](crate::RedactedDebug) trait for a struct. -/// -/// This macro generates an implementation that formats the struct similarly to the standard -/// library's [`Debug`](std::fmt::Debug) trait, but produces redacted output based on the provided [`RedactionEngine`](crate::RedactionEngine). -/// All fields implementing [`Classified`](crate::Classified) will be automatically redacted according to the engine's policy. -/// -/// Fields can be marked with `#[unredacted]` to exclude them from redaction. -/// -/// # Example -/// -/// ``` -/// use data_privacy::{RedactedDebug, classified, taxonomy}; -/// -/// #[taxonomy(example)] -/// enum ExampleTaxonomy { -/// Sensitive, -/// } -/// -/// #[classified(ExampleTaxonomy::Sensitive)] -/// struct UserId(String); -/// -/// #[derive(RedactedDebug)] -/// struct User { -/// id: UserId, -/// #[unredacted] -/// age: u32, -/// } -/// ``` -pub use data_privacy_macros::RedactedDebug; -/// Derives the [`RedactedDisplay`](crate::RedactedDisplay) trait for a struct. -/// -/// This macro generates an implementation that formats the struct similarly to the standard -/// library's [`Display`](std::fmt::Display) trait, but produces redacted output based on the provided [`RedactionEngine`](crate::RedactionEngine). -/// All fields implementing [`Classified`](crate::Classified) will be automatically redacted according to the engine's policy. -/// -/// Fields can be marked with `#[unredacted]` to exclude them from redaction. -/// -/// # Example -/// -/// ``` -/// use data_privacy::{RedactedDisplay, classified, taxonomy}; -/// -/// #[taxonomy(example)] -/// enum ExampleTaxonomy { -/// Sensitive, -/// } -/// -/// #[classified(ExampleTaxonomy::Sensitive)] -/// struct UserId(String); -/// -/// #[derive(RedactedDisplay)] -/// struct User { -/// id: UserId, -/// #[unredacted] -/// age: u32, -/// } -/// ``` -pub use data_privacy_macros::RedactedDisplay; -/// Implements the [`Classified`](crate::Classified) trait on a newtype or single-field struct. -/// -/// This macro is applied to a newtype struct or single-field struct declaration. The struct -/// wraps an inner type that holds sensitive data. The macro generates -/// an implementation of the [`Classified`](crate::Classified), [`Debug`], [`Deref`](core::ops::Deref), -/// [`DerefMut`](core::ops::DerefMut), [`RedactedDebug`](crate::RedactedDebug), -/// and [`RedactedDisplay`](crate::RedactedDisplay) traits. -/// -/// # Example -/// -/// ``` -/// use data_privacy::{classified, taxonomy}; -/// -/// // Declare a taxonomy -/// #[taxonomy(contoso)] -/// enum ContosoTaxonomy { -/// CustomerContent, -/// CustomerIdentifier, -/// } -/// -/// // Declare a classified container -/// #[classified(ContosoTaxonomy::CustomerIdentifier)] -/// struct CustomerId(String); -pub use data_privacy_macros::classified; -/// Generates implementation logic and types to expose a data taxonomy. -/// -/// This macro is applied to an enum declaration. Each variant of the enum -/// represents a data class within the taxonomy. -/// -/// You provide a taxonomy name as first argument, followed by an optional `serde = false` or `serde = true` -/// argument to control whether serde support is included in the generated taxonomy code. -/// The default value for `serde` is `true`, meaning that serde support is included by default. -/// -/// This attribute produces an implementation block for the enum which includes one method for -/// each variant of the enum. These methods each return a [`DataClass`](crate::DataClass) instance representing that data class. -/// In addition, classified data container types are generated for each data class. -/// -/// ## Example -/// -/// ```ignore -/// use data_privacy::taxonomy; -/// -/// #[taxonomy(contoso, serde = false)] -/// enum ContosoTaxonomy { -/// CustomerContent, -/// CustomerIdentifier, -/// OrganizationIdentifier, -/// } -/// ``` -pub use data_privacy_macros::taxonomy; diff --git a/crates/data_privacy/src/redaction_engine.rs b/crates/data_privacy/src/redaction_engine.rs index 83c85d557..f8a0f2189 100644 --- a/crates/data_privacy/src/redaction_engine.rs +++ b/crates/data_privacy/src/redaction_engine.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use crate::redaction_engine_builder::RedactionEngineBuilder; use crate::redaction_engine_inner::RedactionEngineInner; -use crate::{DataClass, RedactedDebug, RedactedDisplay, RedactedToString}; +use crate::{DataClass, RedactedDebug, RedactedDisplay, RedactedToString, Redactor}; /// Lets you apply redaction to classified data. /// @@ -75,16 +75,6 @@ impl RedactionEngine { Self { inner: Arc::new(inner) } } - /// Returns whether redaction would take place for the given data class. - /// - /// Returns `false` only when redaction has been explicitly suppressed for this data class - /// via [`RedactionEngineBuilder::suppress_redaction`]. Returns `true` in all other cases, - /// including when no specific redactor is registered (since the fallback redactor applies). - #[must_use] - pub fn would_redact(&self, data_class: &DataClass) -> bool { - self.inner.would_redact(data_class) - } - /// Redacts a value implementing [`RedactedDebug`], sending the results to the output sink. /// /// # Errors @@ -147,24 +137,20 @@ impl RedactionEngine { pub fn redacted_to_string(&self, value: &impl RedactedToString) -> String { value.to_redacted_string(self) } +} - /// Redacts a string with an explicit data classification, sending the results to the output sink. - /// - /// # Errors - /// - /// This function returns [`Err`] if, and only if, writing to the provided output sink (which implements [`Write`]) returns [`Err`]. String redaction is considered an infallible operation; - /// this function only returns a [`std::fmt::Result`] because writing to the underlying sink might fail and it must provide a way to propagate the fact that an error - /// has occurred (as a [`std::fmt::Error`]) back up the stack. - pub fn redact(&self, data_class: impl AsRef, value: impl AsRef, output: &mut impl Write) -> core::fmt::Result { - let data_class_ref = data_class.as_ref(); - let value_str = value.as_ref(); +impl Redactor for RedactionEngine { + fn redacts(&self, data_class: &DataClass) -> bool { + self.inner.redacts(data_class) + } - if let Some(redactor) = self.inner.resolve(data_class_ref) { - redactor.redact(data_class_ref, value_str, output) + fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { + if let Some(strategy) = self.inner.resolve(data_class) { + strategy.redact(data_class, value, output) } else { // Redaction has been explicitly suppressed for this data class; pass through unmodified, // bypassing both class-specific and fallback redactors. - output.write_str(value_str) + output.write_str(value) } } } diff --git a/crates/data_privacy/src/redaction_engine_inner.rs b/crates/data_privacy/src/redaction_engine_inner.rs index 4e36c1095..23ad2e280 100644 --- a/crates/data_privacy/src/redaction_engine_inner.rs +++ b/crates/data_privacy/src/redaction_engine_inner.rs @@ -56,8 +56,12 @@ impl RedactionEngineInner { } #[must_use] - pub fn would_redact(&self, data_class: &DataClass) -> bool { - !matches!(self.redactors.get(data_class), Some(RedactionPolicy::Suppressed)) + pub fn redacts(&self, data_class: &DataClass) -> bool { + match self.redactors.get(data_class) { + Some(RedactionPolicy::Suppressed) => false, + Some(RedactionPolicy::Redact(redactor)) => redactor.redacts(data_class), + None => self.fallback.redacts(data_class), + } } pub fn set_fallback(&mut self, redactor: impl Redactor + Send + Sync + 'static) { @@ -81,6 +85,7 @@ impl Debug for RedactionEngineInner { } #[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] mod tests { use data_privacy_macros::taxonomy; use rustc_hash::FxBuildHasher; @@ -138,22 +143,36 @@ mod tests { } #[test] - fn test_would_redact_returns_true_for_unknown_class() { + fn test_redacts_returns_true_for_unknown_class() { let inner = RedactionEngineInner::default(); - assert!(inner.would_redact(&TestTaxonomy::Sensitive.data_class())); + assert!(inner.redacts(&TestTaxonomy::Sensitive.data_class())); } #[test] - fn test_would_redact_returns_true_for_registered_redactor() { + fn test_redacts_returns_true_for_registered_redactor() { let mut inner = RedactionEngineInner::default(); inner.insert(TestTaxonomy::Sensitive, SimpleRedactor::new()); - assert!(inner.would_redact(&TestTaxonomy::Sensitive.data_class())); + assert!(inner.redacts(&TestTaxonomy::Sensitive.data_class())); } #[test] - fn test_would_redact_returns_false_for_suppressed_class() { + fn test_redacts_returns_false_for_suppressed_class() { let mut inner = RedactionEngineInner::default(); inner.suppress(TestTaxonomy::Sensitive); - assert!(!inner.would_redact(&TestTaxonomy::Sensitive.data_class())); + assert!(!inner.redacts(&TestTaxonomy::Sensitive.data_class())); + } + + #[test] + fn test_redacts_returns_false_for_passthrough_redactor() { + let mut inner = RedactionEngineInner::default(); + inner.insert(TestTaxonomy::Sensitive, SimpleRedactor::with_mode(SimpleRedactorMode::Passthrough)); + assert!(!inner.redacts(&TestTaxonomy::Sensitive.data_class())); + } + + #[test] + fn test_redacts_returns_false_for_passthrough_fallback() { + let mut inner = RedactionEngineInner::default(); + inner.set_fallback(SimpleRedactor::with_mode(SimpleRedactorMode::Passthrough)); + assert!(!inner.redacts(&TestTaxonomy::Sensitive.data_class())); } } diff --git a/crates/data_privacy/src/redactors/mod.rs b/crates/data_privacy/src/redactors/mod.rs index 6e66838e2..29895b2e8 100644 --- a/crates/data_privacy/src/redactors/mod.rs +++ b/crates/data_privacy/src/redactors/mod.rs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -mod redactor; - pub mod simple_redactor; #[cfg(feature = "xxh3")] @@ -11,8 +9,6 @@ pub mod xxh3_redactor; #[cfg(feature = "rapidhash")] pub mod rapidhash_redactor; -pub use redactor::Redactor; - #[cfg(any(feature = "xxh3", feature = "rapidhash"))] #[inline] pub fn u64_to_hex_array(mut value: u64) -> [u8; N] { @@ -28,16 +24,19 @@ pub fn u64_to_hex_array(mut value: u64) -> [u8; N] { } #[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] mod tests { use std::fmt::Write; use data_privacy_macros::taxonomy; - use super::*; + use crate::Redactor; #[cfg(any(feature = "xxh3", feature = "rapidhash"))] #[test] fn test_u64_to_hex_array() { + use super::u64_to_hex_array; + let result = u64_to_hex_array(0x1234_5678_9abc_def0); let expected = b"123456789abcdef0"; assert_eq!(result, *expected); @@ -53,7 +52,11 @@ mod tests { struct TestRedactor; - impl Redactor for TestRedactor { + impl crate::Redactor for TestRedactor { + fn redacts(&self, _data_class: &crate::DataClass) -> bool { + true + } + fn redact(&self, _data_class: &crate::DataClass, value: &str, output: &mut dyn Write) -> std::fmt::Result { write!(output, "{value}tomato") } diff --git a/crates/data_privacy/src/redactors/rapidhash_redactor.rs b/crates/data_privacy/src/redactors/rapidhash_redactor.rs index a6764dd74..be0388d2d 100644 --- a/crates/data_privacy/src/redactors/rapidhash_redactor.rs +++ b/crates/data_privacy/src/redactors/rapidhash_redactor.rs @@ -27,6 +27,10 @@ impl RapidHashRedactor { } impl Redactor for RapidHashRedactor { + fn redacts(&self, _data_class: &DataClass) -> bool { + true + } + fn redact(&self, _: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { let hash = rapidhash_v3_seeded(value.as_bytes(), &self.secrets); let buffer = crate::redactors::u64_to_hex_array::(hash); diff --git a/crates/data_privacy/src/redactors/redactor.rs b/crates/data_privacy/src/redactors/redactor.rs deleted file mode 100644 index b8eeee30d..000000000 --- a/crates/data_privacy/src/redactors/redactor.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use std::fmt::Write; - -use crate::DataClass; - -/// Represents types that can redact data. -pub trait Redactor { - /// Redacts the given value and writes the results to the given output sink. - /// - /// # Errors - /// - /// This function returns [`Err`] if, and only if, writing to the provided `output` sink (which implements [`Write`]) returns [`Err`]. String redaction is considered - /// an infallible operation; this function only returns a [`std::fmt::Result`] because writing to the underlying output sink might fail and it must provide a way to propagate - /// the fact that an error has occurred back up the stack. - fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> std::fmt::Result; -} diff --git a/crates/data_privacy/src/redactors/simple_redactor.rs b/crates/data_privacy/src/redactors/simple_redactor.rs index 102d8453f..3ec4fd174 100644 --- a/crates/data_privacy/src/redactors/simple_redactor.rs +++ b/crates/data_privacy/src/redactors/simple_redactor.rs @@ -6,8 +6,7 @@ use std::borrow::Cow; use std::fmt::Write; -use crate::DataClass; -use crate::redactors::Redactor; +use crate::{DataClass, Redactor}; /// Mode of operation for the `SimpleRedactor`. #[derive(Clone, Debug, PartialEq, Eq)] @@ -66,6 +65,10 @@ impl SimpleRedactor { } impl Redactor for SimpleRedactor { + fn redacts(&self, _data_class: &DataClass) -> bool { + !matches!(self.mode, SimpleRedactorMode::Passthrough) + } + #[cfg_attr(test, mutants::skip)] fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { const ASTERISKS: &str = diff --git a/crates/data_privacy/src/redactors/xxh3_redactor.rs b/crates/data_privacy/src/redactors/xxh3_redactor.rs index a1483cef3..057433221 100644 --- a/crates/data_privacy/src/redactors/xxh3_redactor.rs +++ b/crates/data_privacy/src/redactors/xxh3_redactor.rs @@ -48,6 +48,10 @@ impl xxH3Redactor { } impl Redactor for xxH3Redactor { + fn redacts(&self, _data_class: &DataClass) -> bool { + true + } + fn redact(&self, _: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { let hash = xxh3_64_with_secret(value.as_bytes(), &self.secret); let buffer = crate::redactors::u64_to_hex_array::(hash); @@ -58,6 +62,7 @@ impl Redactor for xxH3Redactor { } #[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] mod tests { #[test] fn test_with_secret_creates_redactor_with_custom_secret() { diff --git a/crates/data_privacy/src/sensitive.rs b/crates/data_privacy/src/sensitive.rs index e9ad5837b..fd05ac74a 100644 --- a/crates/data_privacy/src/sensitive.rs +++ b/crates/data_privacy/src/sensitive.rs @@ -3,9 +3,7 @@ use core::fmt::{Debug, Display, Formatter}; -use data_privacy::IntoDataClass; - -use crate::{Classified, DataClass, RedactedDebug, RedactedDisplay, RedactionEngine}; +use data_privacy_core::{Classified, DataClass, IntoDataClass, RedactedDebug, RedactedDisplay, Redactor}; /// Size of the stack-allocated buffer used for formatting before falling back to heap allocation. const STACK_BUFFER_SIZE: usize = 128; @@ -80,7 +78,7 @@ where clippy::cast_possible_truncation, reason = "Converting from u64 to usize, value is known to be <= STACK_BUFFER_SIZE" )] - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> std::fmt::Result { let v = &self.value; let mut local_buf = [0u8; STACK_BUFFER_SIZE]; @@ -97,10 +95,10 @@ where // SAFETY: We know the buffer contains valid UTF-8 because the Debug impl can only write valid UTF-8. let s = unsafe { core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(self.data_class(), s, f) + redactor.redact(self.data_class(), s, f) } else { // If the value is too large to fit in the buffer, we fall back to using the Debug format directly. - engine.redact(self.data_class(), format!("{v:?}"), f) + redactor.redact(self.data_class(), &format!("{v:?}"), f) } } } @@ -113,7 +111,7 @@ where clippy::cast_possible_truncation, reason = "Converting from u64 to usize, value is known to be <= STACK_BUFFER_SIZE" )] - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> std::fmt::Result { let v = &self.value; let mut local_buf = [0u8; STACK_BUFFER_SIZE]; @@ -127,13 +125,129 @@ where }; if amount <= local_buf.len() { - // SAFETY: We know the buffer contains valid UTF-8 because the Debug impl can only write valid UTF-8. + // SAFETY: We know the buffer contains valid UTF-8 because the Display impl can only write valid UTF-8. let s = unsafe { core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(self.data_class(), s, f) + redactor.redact(self.data_class(), s, f) } else { - // If the value is too large to fit in the buffer, we fall back to using the Debug format directly. - engine.redact(self.data_class(), format!("{v}"), f) + // If the value is too large to fit in the buffer, we fall back to using the Display format directly. + redactor.redact(self.data_class(), &format!("{v}"), f) + } + } +} + +#[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] +mod tests { + use core::fmt::Write; + + use insta::assert_snapshot; + + use crate::{DataClass, RedactedDebug, RedactedDisplay, RedactedToString, Redactor, Sensitive}; + + /// A test redactor that wraps values in brackets with the data class. + struct TagRedactor; + + impl Redactor for TagRedactor { + fn redacts(&self, _data_class: &DataClass) -> bool { + true + } + + fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { + write!(output, "<{}/{}:{}>", data_class.taxonomy(), data_class.name(), value) + } + } + + #[test] + #[cfg_attr(miri, ignore)] + fn redacted_debug_produces_nonempty_output() { + struct Adapter<'a, T: ?Sized> { + inner: &'a T, + redactor: &'a dyn Redactor, + } + impl std::fmt::Display for Adapter<'_, T> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + ::fmt(self.inner, self.redactor, f) + } + } + let sensitive = Sensitive::new("secret", DataClass::new("test", "pii")); + let result = Adapter { + inner: &sensitive, + redactor: &TagRedactor as &dyn Redactor, } + .to_string(); + assert_snapshot!(result, @r#""#); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn redacted_display_produces_nonempty_output() { + struct Adapter<'a, T: ?Sized> { + inner: &'a T, + redactor: &'a dyn Redactor, + } + impl std::fmt::Display for Adapter<'_, T> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + ::fmt(self.inner, self.redactor, f) + } + } + let sensitive = Sensitive::new("secret", DataClass::new("test", "pii")); + let result = Adapter { + inner: &sensitive, + redactor: &TagRedactor as &dyn Redactor, + } + .to_string(); + assert_snapshot!(result, @""); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn redacted_to_string_produces_nonempty_output() { + let sensitive = Sensitive::new("secret", DataClass::new("test", "pii")); + let result = sensitive.to_redacted_string(&TagRedactor); + assert_snapshot!(result, @""); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn redacted_display_at_exact_buffer_boundary() { + // STACK_BUFFER_SIZE is 128, so a Display output of exactly 128 bytes tests the `<=` boundary + let value = "x".repeat(128); + let sensitive = Sensitive::new(value, DataClass::new("test", "pii")); + let result = sensitive.to_redacted_string(&TagRedactor); + assert_snapshot!(result, @""); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn redacted_debug_at_exact_buffer_boundary() { + // Debug of a String adds quotes: "xxx" -> 126 chars + 2 quotes = 128 bytes + struct Adapter<'a, T: ?Sized> { + inner: &'a T, + redactor: &'a dyn Redactor, + } + impl std::fmt::Display for Adapter<'_, T> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + ::fmt(self.inner, self.redactor, f) + } + } + let value = "x".repeat(126); + let sensitive = Sensitive::new(value, DataClass::new("test", "pii")); + let result = Adapter { + inner: &sensitive, + redactor: &TagRedactor as &dyn Redactor, + } + .to_string(); + assert_snapshot!(result, @r#""#); + } + + #[test] + fn reclassify_changes_data_class() { + use crate::Classified; + + let sensitive = Sensitive::new("secret", DataClass::new("original", "pii")); + let reclassified = sensitive.reclassify(DataClass::new("updated", "eupi")); + assert_eq!(reclassified.data_class().taxonomy(), "updated"); + assert_eq!(reclassified.data_class().name(), "eupi"); } } diff --git a/crates/data_privacy/tests/rapidhash_redactor.rs b/crates/data_privacy/tests/rapidhash_redactor.rs index 18edb80d5..bcb714d24 100644 --- a/crates/data_privacy/tests/rapidhash_redactor.rs +++ b/crates/data_privacy/tests/rapidhash_redactor.rs @@ -119,6 +119,13 @@ fn test_clone_produces_identical_redactor() { assert_eq!(output1, output2); } +#[test] +fn test_redacts_returns_true() { + let redactor = get_test_redactor(); + let data_class = DataClass::new("test_taxonomy", "test_class"); + assert!(redactor.redacts(&data_class)); +} + #[test] fn test_data_class_does_not_affect_output() { let redactor = get_test_redactor(); diff --git a/crates/data_privacy/tests/redaction_engine.rs b/crates/data_privacy/tests/redaction_engine.rs index 2ac653595..24179ba50 100644 --- a/crates/data_privacy/tests/redaction_engine.rs +++ b/crates/data_privacy/tests/redaction_engine.rs @@ -4,7 +4,7 @@ #![expect(missing_docs, reason = "Test code")] use data_privacy::simple_redactor::{SimpleRedactor, SimpleRedactorMode}; -use data_privacy::{DataClass, IntoDataClass, RedactedDisplay, RedactionEngine}; +use data_privacy::{DataClass, IntoDataClass, RedactedDisplay, RedactionEngine, Redactor}; use data_privacy_macros::{classified, taxonomy}; #[taxonomy(test)] @@ -65,7 +65,7 @@ where fn collect_output_as_class(engine: &RedactionEngine, data_class: impl IntoDataClass, value: &str) -> String { let mut output = String::new(); engine - .redact(data_class.into_data_class(), value, &mut output) + .redact(&data_class.into_data_class(), value, &mut output) .expect("redact should succeed in tests"); output } @@ -429,8 +429,8 @@ fn suppress_redaction_prevents_redaction_for_class() { .suppress_redaction(TestTaxonomy::Insensitive) .build(); - assert!(engine.would_redact(&TestTaxonomy::Sensitive.data_class())); - assert!(!engine.would_redact(&TestTaxonomy::Insensitive.data_class())); + assert!(engine.redacts(&TestTaxonomy::Sensitive.data_class())); + assert!(!engine.redacts(&TestTaxonomy::Insensitive.data_class())); } #[test] @@ -448,10 +448,10 @@ fn suppress_redaction_results_in_passthrough_output() { } #[test] -fn would_redact_returns_true_for_unregistered_class() { +fn redacts_returns_true_for_unregistered_class() { let engine = RedactionEngine::builder().build(); - assert!(engine.would_redact(&TestTaxonomy::Sensitive.data_class())); + assert!(engine.redacts(&TestTaxonomy::Sensitive.data_class())); } #[test] @@ -461,5 +461,5 @@ fn suppress_redaction_overrides_previously_added_redactor() { .suppress_redaction(TestTaxonomy::Sensitive) .build(); - assert!(!engine.would_redact(&TestTaxonomy::Sensitive.data_class())); + assert!(!engine.redacts(&TestTaxonomy::Sensitive.data_class())); } diff --git a/crates/data_privacy/tests/sensitive.rs b/crates/data_privacy/tests/sensitive.rs index e1933e29e..232d18ff4 100644 --- a/crates/data_privacy/tests/sensitive.rs +++ b/crates/data_privacy/tests/sensitive.rs @@ -9,6 +9,7 @@ use std::hash::{Hash, Hasher}; use data_privacy::simple_redactor::{SimpleRedactor, SimpleRedactorMode}; use data_privacy::{Classified, RedactionEngine, Sensitive}; use data_privacy_macros::taxonomy; +use insta::assert_snapshot; #[taxonomy(test)] #[derive(Debug)] @@ -184,3 +185,76 @@ fn test_redacted_long_value_fallback_path() { assert_eq!(to_string_out.len(), long_plain.len()); assert!(to_string_out.chars().all(|c| c == '*')); } + +#[test] +#[cfg_attr(miri, ignore)] +fn test_redacted_debug_output_is_nonempty() { + let engine = RedactionEngine::builder() + .add_class_redactor(TestTaxonomy::PII, SimpleRedactor::with_mode(SimpleRedactorMode::Replace('*'))) + .build(); + let wrapper = Sensitive::new("hello", TestTaxonomy::PII); + + let mut debug_out = String::new(); + engine.redacted_debug(&wrapper, &mut debug_out).unwrap(); + assert_snapshot!(debug_out, @"*******"); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn test_redacted_display_output_is_nonempty() { + let engine = RedactionEngine::builder() + .add_class_redactor(TestTaxonomy::PII, SimpleRedactor::with_mode(SimpleRedactorMode::Replace('*'))) + .build(); + let wrapper = Sensitive::new("hello", TestTaxonomy::PII); + + let mut display_out = String::new(); + engine.redacted_display(&wrapper, &mut display_out).unwrap(); + assert_snapshot!(display_out, @"*****"); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn test_redacted_to_string_is_nonempty() { + let engine = RedactionEngine::builder() + .add_class_redactor(TestTaxonomy::PII, SimpleRedactor::with_mode(SimpleRedactorMode::Replace('*'))) + .build(); + let wrapper = Sensitive::new("hello", TestTaxonomy::PII); + + let result = engine.redacted_to_string(&wrapper); + assert_snapshot!(result, @"*****"); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn test_redacted_display_at_stack_buffer_boundary() { + // Value with Display output of exactly 128 bytes (STACK_BUFFER_SIZE) exercises the <= boundary. + let engine = RedactionEngine::builder() + .add_class_redactor(TestTaxonomy::PII, SimpleRedactor::with_mode(SimpleRedactorMode::PassthroughAndTag)) + .build(); + let value_at_boundary = "x".repeat(128); + let wrapper = Sensitive::new(value_at_boundary, TestTaxonomy::PII); + + let mut display_out = String::new(); + engine.redacted_display(&wrapper, &mut display_out).unwrap(); + assert_snapshot!(display_out, @""); + + // Verify the result is the same as to_redacted_string + let to_string_out = engine.redacted_to_string(&wrapper); + assert_eq!(display_out, to_string_out); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn test_redacted_debug_at_stack_buffer_boundary() { + // Value with Debug output of exactly 128 bytes exercises the <= boundary. + // For a String, Debug adds quotes: `"xxx..."` so we need 126 chars to get 128 bytes of debug output. + let engine = RedactionEngine::builder() + .add_class_redactor(TestTaxonomy::PII, SimpleRedactor::with_mode(SimpleRedactorMode::PassthroughAndTag)) + .build(); + let value_at_boundary = "x".repeat(126); + let wrapper = Sensitive::new(value_at_boundary, TestTaxonomy::PII); + + let mut debug_out = String::new(); + engine.redacted_debug(&wrapper, &mut debug_out).unwrap(); + assert_snapshot!(debug_out, @r#""#); +} diff --git a/crates/data_privacy/tests/xxh3_redactor.rs b/crates/data_privacy/tests/xxh3_redactor.rs index fe493665a..3fcb480fb 100644 --- a/crates/data_privacy/tests/xxh3_redactor.rs +++ b/crates/data_privacy/tests/xxh3_redactor.rs @@ -123,6 +123,13 @@ fn test_clone_produces_identical_redactor() { assert_eq!(output1, output2); } +#[test] +fn test_redacts_returns_true() { + let redactor = get_test_redactor(); + let data_class = DataClass::new("test_taxonomy", "test_class"); + assert!(redactor.redacts(&data_class)); +} + #[test] fn test_data_class_does_not_affect_output() { let redactor = get_test_redactor(); diff --git a/crates/data_privacy_core/CHANGELOG.md b/crates/data_privacy_core/CHANGELOG.md new file mode 100644 index 000000000..d0cead33b --- /dev/null +++ b/crates/data_privacy_core/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## [0.1.0] - 2026-05-28 + +- ✨ Features + + - Initial release. + - Core data classification types and traits extracted from `data_privacy`: `Classified`, `DataClass`, `IntoDataClass`, `RedactedDebug`, `RedactedDisplay`, `RedactedToString`, `Redactor`. diff --git a/crates/data_privacy_core/Cargo.toml b/crates/data_privacy_core/Cargo.toml new file mode 100644 index 000000000..f2a98a7c3 --- /dev/null +++ b/crates/data_privacy_core/Cargo.toml @@ -0,0 +1,39 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[package] +name = "data_privacy_core" +description = "Core data classification types and traits for the data_privacy ecosystem." +version = "0.1.0" +readme = "README.md" +keywords = ["oxidizer", "compliance", "privacy", "classification"] +categories = ["data-structures"] + +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository = "https://github.com/microsoft/oxidizer/tree/main/crates/data_privacy_core" + +[package.metadata.docs.rs] +all-features = true + +[package.metadata.cargo_check_external_types] +allowed_external_types = ["serde_core::de::*", "serde_core::ser::*"] + +[features] +default = ["serde"] +serde = ["dep:serde_core"] + +[dependencies] +serde_core = { workspace = true, optional = true, default-features = false, features = ["std"] } + +[dev-dependencies] +insta.workspace = true +mutants.workspace = true +serde = { workspace = true, features = ["std", "derive"] } +serde_json = { workspace = true, features = ["std"] } + +[lints] +workspace = true diff --git a/crates/data_privacy_core/README.md b/crates/data_privacy_core/README.md new file mode 100644 index 000000000..6eaa7185c --- /dev/null +++ b/crates/data_privacy_core/README.md @@ -0,0 +1,51 @@ +
+ Data Privacy Core Logo + +# Data Privacy Core + +[![crate.io](https://img.shields.io/crates/v/data_privacy_core.svg)](https://crates.io/crates/data_privacy_core) +[![docs.rs](https://docs.rs/data_privacy_core/badge.svg)](https://docs.rs/data_privacy_core) +[![MSRV](https://img.shields.io/crates/msrv/data_privacy_core)](https://crates.io/crates/data_privacy_core) +[![CI](https://github.com/microsoft/oxidizer/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/microsoft/oxidizer/actions/workflows/main.yml) +[![Coverage](https://codecov.io/gh/microsoft/oxidizer/graph/badge.svg?token=FCUG0EL5TI)](https://codecov.io/gh/microsoft/oxidizer) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) +This crate was developed as part of the Oxidizer project + +
+ +Core data classification types and traits. + +The `data_privacy_core` crate contains the trait definitions and the [`DataClass`][__link0] type +with no support for `#[derive()]` or attribute macros. + +In crates that use `#[taxonomy]`, `#[classified]`, `#[derive(RedactedDebug)]`, or +`#[derive(RedactedDisplay)]`, you must depend on the **[`data_privacy`][__link1]** +crate, not `data_privacy_core`. + +In crates that hand-write implementations of data privacy traits, or only use them as trait +bounds, depending on `data_privacy_core` is permitted. But `data_privacy` re-exports all of +these traits and can be used for this use case too. **If in doubt, disregard `data_privacy_core` +and always use `data_privacy`.** + +## Contents + +* [`DataClass`][__link2] - identifies a data class within a taxonomy +* [`Classified`][__link3] - trait for types that hold classified data +* [`Redactor`][__link4] - trait for types that can apply redaction +* [`RedactedDebug`][__link5] / [`RedactedDisplay`][__link6] / [`RedactedToString`][__link7] - redaction-aware formatting traits + + +
+ +This crate was developed as part of The Oxidizer Project. Browse this crate's source code. + + + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbJMSGY2z7YbEblsBSe-58K48b62Bomn7PG1Ebw8HBurz5KcZhZIGCcWRhdGFfcHJpdmFjeV9jb3JlZTAuMS4w + [__link0]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=DataClass + [__link1]: https://docs.rs/data_privacy + [__link2]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=DataClass + [__link3]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Classified + [__link4]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=Redactor + [__link5]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=RedactedDebug + [__link6]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=RedactedDisplay + [__link7]: https://docs.rs/data_privacy_core/0.1.0/data_privacy_core/?search=RedactedToString diff --git a/crates/data_privacy/src/classified.rs b/crates/data_privacy_core/src/classified.rs similarity index 85% rename from crates/data_privacy/src/classified.rs rename to crates/data_privacy_core/src/classified.rs index ca7c2383c..648326d1b 100644 --- a/crates/data_privacy/src/classified.rs +++ b/crates/data_privacy_core/src/classified.rs @@ -10,8 +10,9 @@ use crate::DataClass; /// Although instances are encapsulated, it's possible to extract the instances when /// classification is no longer needed. /// -/// You rarely implement this trait by hand, instead use the [`classified`](data_privacy_macros::classified) macro to generate -/// classified types automatically. +/// You rarely implement this trait by hand, instead use the +/// [`classified`](https://docs.rs/data_privacy/latest/data_privacy/attr.classified.html) macro from the +/// [`data_privacy`](https://docs.rs/data_privacy) crate to generate classified types automatically. /// /// # Ancillary Traits /// @@ -26,7 +27,7 @@ use crate::DataClass; /// # Example /// /// ```rust -/// use data_privacy::{Classified, DataClass}; +/// use data_privacy_core::{Classified, DataClass}; /// /// #[derive(Debug)] /// struct Person { @@ -63,7 +64,6 @@ use crate::DataClass; /// assert_eq!(classified.data_class().name(), "classified_person"); /// ``` pub trait Classified { - /// Returns the data class of the classified data. - #[must_use] + /// Returns the data class assigned to this instance. fn data_class(&self) -> &DataClass; } diff --git a/crates/data_privacy/src/data_class.rs b/crates/data_privacy_core/src/data_class.rs similarity index 72% rename from crates/data_privacy/src/data_class.rs rename to crates/data_privacy_core/src/data_class.rs index f0df83749..db9c6e9ce 100644 --- a/crates/data_privacy/src/data_class.rs +++ b/crates/data_privacy_core/src/data_class.rs @@ -145,12 +145,10 @@ const fn is_valid_identifier(s: &str) -> bool { return false; } - // Validate first character if !is_valid_ascii_ident_start(bytes[0]) { return false; } - // Validate remaining characters let mut i = 1; while i < bytes.len() { if !is_valid_ascii_ident_continue(bytes[i]) { @@ -162,141 +160,138 @@ const fn is_valid_identifier(s: &str) -> bool { true } -#[cfg(all(test, feature = "serde"))] +#[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] mod tests { use super::*; #[test] - fn test_serialize() { - let dc = DataClass::new("contoso", "customer_identifier"); - let serialized = serde_json::to_string(&dc).expect("failed to serialize"); - assert_eq!(serialized, "\"contoso/customer_identifier\""); - } - - #[test] - fn test_deserialize_valid() { - let serialized = "\"contoso/customer_identifier\""; - let dc: DataClass = serde_json::from_str(serialized).expect("failed to deserialize"); + fn new_stores_taxonomy_and_name() { + let dc = DataClass::new("contoso", "customer_id"); assert_eq!(dc.taxonomy(), "contoso"); - assert_eq!(dc.name(), "customer_identifier"); + assert_eq!(dc.name(), "customer_id"); } #[test] - fn test_deserialize_invalid_format_no_slash() { - let serialized = "\"contoso_customer_identifier\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("expecting taxonomy/name format")); + fn display_uses_slash_separator() { + let dc = DataClass::new("contoso", "customer_id"); + assert_eq!(dc.to_string(), "contoso/customer_id"); } #[test] - fn test_deserialize_invalid_format_empty_taxonomy() { - let serialized = "\"/customer_identifier\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid taxonomy identifier")); + fn as_ref_returns_self() { + let dc = DataClass::new("contoso", "customer_id"); + let dc_ref: &DataClass = dc.as_ref(); + assert_eq!(dc_ref, &dc); } #[test] - fn test_deserialize_invalid_format_empty_name() { - let serialized = "\"contoso/\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid name identifier")); + fn into_data_class_identity() { + let dc = DataClass::new("contoso", "customer_id"); + let dc2 = dc.clone().into_data_class(); + assert_eq!(dc, dc2); } #[test] - fn test_deserialize_invalid_taxonomy() { - let serialized = "\"a-b/c\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid taxonomy identifier")); + fn valid_identifiers() { + assert!(is_valid_identifier("foo")); + assert!(is_valid_identifier("_bar")); + assert!(is_valid_identifier("Baz123")); + assert!(is_valid_identifier("_")); + assert!(is_valid_identifier("a")); } #[test] - fn test_deserialize_invalid_name() { - let serialized = "\"a/b-c\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid name identifier")); + fn invalid_identifiers() { + assert!(!is_valid_identifier("")); + assert!(!is_valid_identifier("1abc")); + assert!(!is_valid_identifier("a-b")); + assert!(!is_valid_identifier("a b")); } #[test] - fn test_deserialize_invalid_type() { - let serialized = "123"; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.is_data()); + fn data_class_equality_and_ordering() { + let a = DataClass::new("a", "x"); + let b = DataClass::new("b", "x"); + assert!(a < b); + assert_eq!(a, a.clone()); } #[test] - fn test_deserialize_expecting_message() { - let serialized = "false"; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("a string in taxonomy/name format")); + fn data_class_debug() { + let dc = DataClass::new("t", "n"); + let dbg = format!("{dc:?}"); + assert!(dbg.contains("DataClass")); } #[test] - fn test_is_valid_identifier_valid() { - let _ = DataClass::new("a", "a"); - let _ = DataClass::new("a1", "a"); - let _ = DataClass::new("a_b", "a"); - let _ = DataClass::new("_a", "a"); - let _ = DataClass::new("A", "a"); - let _ = DataClass::new("A1", "a"); - let _ = DataClass::new("A_B", "a"); - let _ = DataClass::new("_A", "a"); + fn data_class_hash() { + use std::collections::HashSet; + let mut set = HashSet::new(); + set.insert(DataClass::new("t", "n")); + assert!(set.contains(&DataClass::new("t", "n"))); } +} + +#[cfg(all(test, feature = "serde"))] +#[cfg_attr(coverage_nightly, coverage(off))] +mod serde_tests { + use super::*; #[test] - #[should_panic] - fn test_is_valid_identifier_invalid_empty() { - let _ = DataClass::new("", "a"); + fn test_serialize() { + let dc = DataClass::new("contoso", "customer_identifier"); + let serialized = serde_json::to_string(&dc).expect("failed to serialize"); + assert_eq!(serialized, "\"contoso/customer_identifier\""); } #[test] - #[should_panic] - fn test_is_valid_identifier_invalid_starts_with_number() { - let _ = DataClass::new("1a", "a"); + fn test_deserialize_valid() { + let serialized = "\"contoso/customer_identifier\""; + let dc: DataClass = serde_json::from_str(serialized).expect("failed to deserialize"); + assert_eq!(dc.taxonomy(), "contoso"); + assert_eq!(dc.name(), "customer_identifier"); } #[test] - #[should_panic] - fn test_is_valid_identifier_invalid_contains_invalid_char() { - let _ = DataClass::new("a-b", "a"); + fn test_deserialize_invalid_format_no_slash() { + let serialized = "\"contoso_customer_identifier\""; + let err = serde_json::from_str::(serialized).unwrap_err(); + assert!(err.to_string().contains("expecting taxonomy/name format")); } #[test] - fn test_is_valid_identifier_invalid_hash() { - // Hash character is not valid in identifiers - let serialized = "\"r#type/data\""; + fn test_deserialize_invalid_format_empty_taxonomy() { + let serialized = "\"/customer_identifier\""; let err = serde_json::from_str::(serialized).unwrap_err(); assert!(err.to_string().contains("invalid taxonomy identifier")); } #[test] - fn test_unicode_identifiers_emoji_invalid() { - // Emoji are not XID_Start or XID_Continue, should be invalid - let serialized = "\"test/🦀data\""; + fn test_deserialize_invalid_format_empty_name() { + let serialized = "\"contoso/\""; let err = serde_json::from_str::(serialized).unwrap_err(); assert!(err.to_string().contains("invalid name identifier")); } #[test] - fn test_unicode_invalid_start_with_digit() { - // Even with Unicode, can't start with a digit - let serialized = "\"1μετρο/data\""; + fn test_deserialize_invalid_taxonomy() { + let serialized = "\"a-b/c\""; let err = serde_json::from_str::(serialized).unwrap_err(); assert!(err.to_string().contains("invalid taxonomy identifier")); } #[test] - fn test_unicode_invalid_whitespace() { - // Whitespace is not XID_Continue - let serialized = "\"hello world/data\""; + fn test_deserialize_invalid_name() { + let serialized = "\"a/b-c\""; let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid taxonomy identifier")); + assert!(err.to_string().contains("invalid name identifier")); } #[test] - fn test_unicode_invalid_punctuation() { - // Most punctuation is not XID_Continue (except underscore) - let serialized = "\"hello-world/data\""; - let err = serde_json::from_str::(serialized).unwrap_err(); - assert!(err.to_string().contains("invalid taxonomy identifier")); + fn test_deserialize_wrong_type_triggers_expecting() { + // Passing a number instead of a string triggers Visitor::expecting + let err = serde_json::from_str::("42").unwrap_err(); + assert!(err.to_string().contains("a string in taxonomy/name format")); } } diff --git a/crates/data_privacy_core/src/lib.rs b/crates/data_privacy_core/src/lib.rs new file mode 100644 index 000000000..72d21d6e0 --- /dev/null +++ b/crates/data_privacy_core/src/lib.rs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +#![cfg_attr(docsrs, feature(doc_cfg))] + +//! Core data classification types and traits. +//! +//! The `data_privacy_core` crate contains the trait definitions and the [`DataClass`] type +//! with no support for `#[derive()]` or attribute macros. +//! +//! In crates that use `#[taxonomy]`, `#[classified]`, `#[derive(RedactedDebug)]`, or +//! `#[derive(RedactedDisplay)]`, you must depend on the **[`data_privacy`](https://docs.rs/data_privacy)** +//! crate, not `data_privacy_core`. +//! +//! In crates that hand-write implementations of data privacy traits, or only use them as trait +//! bounds, depending on `data_privacy_core` is permitted. But `data_privacy` re-exports all of +//! these traits and can be used for this use case too. **If in doubt, disregard `data_privacy_core` +//! and always use `data_privacy`.** +//! +//! # Contents +//! +//! - [`DataClass`] - identifies a data class within a taxonomy +//! - [`Classified`] - trait for types that hold classified data +//! - [`Redactor`] - trait for types that can apply redaction +//! - [`RedactedDebug`] / [`RedactedDisplay`] / [`RedactedToString`] - redaction-aware formatting traits + +mod classified; +mod data_class; +mod redacted; +mod redactor; + +pub use classified::Classified; +pub use data_class::{DataClass, IntoDataClass}; +pub use redacted::{RedactedDebug, RedactedDisplay, RedactedToString}; +pub use redactor::Redactor; diff --git a/crates/data_privacy/src/redacted.rs b/crates/data_privacy_core/src/redacted.rs similarity index 51% rename from crates/data_privacy/src/redacted.rs rename to crates/data_privacy_core/src/redacted.rs index df2b330ce..365aaf7fb 100644 --- a/crates/data_privacy/src/redacted.rs +++ b/crates/data_privacy_core/src/redacted.rs @@ -3,12 +3,12 @@ use core::fmt::{Formatter, Result}; -use crate::RedactionEngine; +use crate::Redactor; /// Formats the redacted value using the given formatter. /// /// This trait behaves similarly to the standard library's [`Debug`](core::fmt::Debug) trait, but it produces a redacted -/// representation of the value based on the provided [`RedactionEngine`]. +/// representation of the value based on the provided [`Redactor`]. /// /// Types implementing [`Classified`](crate::Classified) usually implement [`RedactedDebug`] as well. /// Generally speaking, you should just derive an implementation of this trait. @@ -20,13 +20,13 @@ pub trait RedactedDebug { /// This function returns [`Err`] if, and only if, the provided [`Formatter`] returns [`Err`]. String redaction is considered an infallible operation; /// this function only returns a [`Result`] because writing to the underlying stream might fail and it must provide a way to propagate the fact that an error /// has occurred back up the stack. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> Result; + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> Result; } /// Formats the redacted value using the given formatter. /// /// This trait behaves similarly to the standard library's [`Display`](std::fmt::Display) trait, but it produces a redacted -/// representation of the value based on the provided [`RedactionEngine`]. +/// representation of the value based on the provided [`Redactor`]. /// /// Types implementing [`Classified`](crate::Classified) usually implement [`RedactedDisplay`] as well. /// Generally speaking, you should just derive an implementation of this trait. @@ -38,28 +38,78 @@ pub trait RedactedDisplay { /// This function returns [`Err`] if, and only if, the provided [`Formatter`] returns [`Err`]. String redaction is considered an infallible operation; /// this function only returns a [`Result`] because writing to the underlying stream might fail and it must provide a way to propagate the fact that an error /// has occurred back up the stack. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> Result; + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> Result; } /// Converts a type implementing [`RedactedDisplay`] to a redacted string representation. pub trait RedactedToString { /// Converts the value to a redacted string representation. - fn to_redacted_string(&self, engine: &RedactionEngine) -> String; + fn to_redacted_string(&self, redactor: &dyn Redactor) -> String; } impl RedactedToString for T { - fn to_redacted_string(&self, engine: &RedactionEngine) -> String { + fn to_redacted_string(&self, redactor: &dyn Redactor) -> String { struct Adapter<'a, T: ?Sized> { inner: &'a T, - engine: &'a RedactionEngine, + redactor: &'a dyn Redactor, } impl std::fmt::Display for Adapter<'_, T> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - ::fmt(self.inner, self.engine, f) + ::fmt(self.inner, self.redactor, f) } } - Adapter { inner: self, engine }.to_string() + Adapter { inner: self, redactor }.to_string() + } +} + +#[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] +mod tests { + use core::fmt::Write; + + use insta::assert_snapshot; + + use crate::{DataClass, RedactedDisplay, RedactedToString, Redactor}; + + /// A simple test redactor that passes through values with a tag. + struct TagRedactor; + + impl Redactor for TagRedactor { + fn redacts(&self, _data_class: &DataClass) -> bool { + true + } + + fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> core::fmt::Result { + write!(output, "[{}/{}:{}]", data_class.taxonomy(), data_class.name(), value) + } + } + + struct TestValue { + text: &'static str, + } + + impl RedactedDisplay for TestValue { + fn fmt(&self, redactor: &dyn Redactor, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let data_class = DataClass::new("test", "pii"); + redactor.redact(&data_class, self.text, f) + } + } + + #[test] + #[cfg_attr(miri, ignore)] + fn to_redacted_string_produces_correct_output() { + let value = TestValue { text: "secret" }; + let result = value.to_redacted_string(&TagRedactor); + assert_snapshot!(result, @"[test/pii:secret]"); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn to_redacted_string_is_nonempty_for_empty_input() { + let value = TestValue { text: "" }; + let result = value.to_redacted_string(&TagRedactor); + assert_snapshot!(result, @"[test/pii:]"); } } diff --git a/crates/data_privacy_core/src/redactor.rs b/crates/data_privacy_core/src/redactor.rs new file mode 100644 index 000000000..9bddc331d --- /dev/null +++ b/crates/data_privacy_core/src/redactor.rs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fmt::Write; + +use crate::DataClass; + +/// Represents types that can apply redaction to classified data. +/// +/// This is the primary interface for redaction. Types implementing this trait +/// decide how to handle sensitive data based on its classification. +/// +/// Both high-level redaction engines (such as +/// [`RedactionEngine`](https://docs.rs/data_privacy)) and individual redaction +/// strategies (e.g. hash-based or replacement-based redactors) implement this +/// trait. Custom implementations are possible for testing or specialized +/// scenarios. +pub trait Redactor { + /// Returns whether this redactor would modify output for the given data class. + /// + /// Implementations may return `false` when no transformation is applied — for + /// example, when a strategy operates in pass-through mode, or when redaction has + /// been explicitly suppressed for the given class. + fn redacts(&self, data_class: &DataClass) -> bool; + + /// Redacts a string value with an explicit data classification, sending the results to the output sink. + /// + /// # Errors + /// + /// This function returns [`Err`] if, and only if, writing to the provided output sink + /// (which implements [`Write`]) returns [`Err`]. String redaction is considered an infallible + /// operation; this function only returns a [`std::fmt::Result`] because writing to the underlying + /// sink might fail. + fn redact(&self, data_class: &DataClass, value: &str, output: &mut dyn Write) -> std::fmt::Result; +} diff --git a/crates/data_privacy_macros/CHANGELOG.md b/crates/data_privacy_macros/CHANGELOG.md index 4c01c9e4b..311cdac72 100644 --- a/crates/data_privacy_macros/CHANGELOG.md +++ b/crates/data_privacy_macros/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.10.0] - 2026-05-28 + +- ✨ Features + + - Generated code now uses `&dyn Redactor` instead of `&RedactionEngine` to match updated trait signatures in `data_privacy`. + ## [0.9.0] - 2025-12-16 - ✨ Features diff --git a/crates/data_privacy_macros/Cargo.toml b/crates/data_privacy_macros/Cargo.toml index 112f68229..f66946495 100644 --- a/crates/data_privacy_macros/Cargo.toml +++ b/crates/data_privacy_macros/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "data_privacy_macros" description = "Macros for the data_privacy crate." -version = "0.9.0" +version = "0.10.0" readme = "README.md" keywords = ["oxidizer", "compliance", "privacy", "redaction", "scrubbing"] categories = ["data-structures"] diff --git a/crates/data_privacy_macros_impl/CHANGELOG.md b/crates/data_privacy_macros_impl/CHANGELOG.md index 29c4dcae4..3e4ff6852 100644 --- a/crates/data_privacy_macros_impl/CHANGELOG.md +++ b/crates/data_privacy_macros_impl/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.10.0] - 2026-05-28 + +- ✨ Features + + - Generated code now uses `&dyn Redactor` instead of `&RedactionEngine` to match updated trait signatures in `data_privacy`. + ## [0.9.0] - 2025-12-16 - ✨ Features diff --git a/crates/data_privacy_macros_impl/Cargo.toml b/crates/data_privacy_macros_impl/Cargo.toml index b834387bd..98b5dbc52 100644 --- a/crates/data_privacy_macros_impl/Cargo.toml +++ b/crates/data_privacy_macros_impl/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "data_privacy_macros_impl" description = "Macros for the data_privacy crate." -version = "0.9.0" +version = "0.10.0" readme = "README.md" keywords = ["oxidizer", "compliance", "privacy", "redaction", "scrubbing"] categories = ["data-structures"] diff --git a/crates/data_privacy_macros_impl/src/classified.rs b/crates/data_privacy_macros_impl/src/classified.rs index a03f3fc65..dfd4c573e 100644 --- a/crates/data_privacy_macros_impl/src/classified.rs +++ b/crates/data_privacy_macros_impl/src/classified.rs @@ -106,7 +106,7 @@ pub fn classified(attr_args: TokenStream, item: TokenStream) -> SynResult) -> ::core::fmt::Result { + fn fmt(&self, redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter<'_>) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; let v = &#field_access; let dc = ::data_class(self); @@ -121,9 +121,9 @@ pub fn classified(attr_args: TokenStream, item: TokenStream) -> SynResult SynResult ::core::fmt::Result { + fn fmt(&self, redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; let v = &#field_access; let dc = ::data_class(self); @@ -148,9 +148,9 @@ pub fn classified(attr_args: TokenStream, item: TokenStream) -> SynResult Result { } else { quote! { ::std::write!(f, "{}{}: ", #separator, #field_name_str)?; - <#field_type as ::data_privacy::RedactedDebug>::fmt(&self.#field_name, engine, f)?; + <#field_type as ::data_privacy::RedactedDebug>::fmt(&self.#field_name, redactor, f)?; } } }); @@ -64,7 +64,7 @@ pub fn redacted_debug(input: TokenStream) -> Result { } } else { quote! { - <#field_type as ::data_privacy::RedactedDebug>::fmt(&self.#index, engine, f)?; + <#field_type as ::data_privacy::RedactedDebug>::fmt(&self.#index, redactor, f)?; } }; @@ -91,7 +91,7 @@ pub fn redacted_debug(input: TokenStream) -> Result { impl #impl_generics ::data_privacy::RedactedDebug for #name #ty_generics #where_clause { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, #opening)?; @@ -131,7 +131,7 @@ pub fn redacted_display(input: TokenStream) -> Result { } else { quote! { ::std::write!(f, "{}{}: ", #separator, #field_name_str)?; - <#field_type as ::data_privacy::RedactedDisplay>::fmt(&self.#field_name, engine, f)?; + <#field_type as ::data_privacy::RedactedDisplay>::fmt(&self.#field_name, redactor, f)?; } } }); @@ -155,7 +155,7 @@ pub fn redacted_display(input: TokenStream) -> Result { } } else { quote! { - <#field_type as ::data_privacy::RedactedDisplay>::fmt(&self.#index, engine, f)?; + <#field_type as ::data_privacy::RedactedDisplay>::fmt(&self.#index, redactor, f)?; } }; @@ -182,7 +182,7 @@ pub fn redacted_display(input: TokenStream) -> Result { impl #impl_generics ::data_privacy::RedactedDisplay for #name #ty_generics #where_clause { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, #opening)?; diff --git a/crates/data_privacy_macros_impl/tests/snapshots/classified__success.snap b/crates/data_privacy_macros_impl/tests/snapshots/classified__success.snap index 3a34f5b8b..3078a79e8 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/classified__success.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/classified__success.snap @@ -30,7 +30,7 @@ where )] fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter<'_>, ) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; @@ -47,9 +47,9 @@ where }; if amount <= local_buf.len() { let s = unsafe { ::core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(dc, s, output) + redactor.redact(dc, s, output) } else { - engine.redact(dc, format!("{v:?}"), output) + redactor.redact(dc, &format!("{v:?}"), output) } } } @@ -63,7 +63,7 @@ where )] fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter, ) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; @@ -80,9 +80,9 @@ where }; if amount <= local_buf.len() { let s = unsafe { ::core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(dc, s, output) + redactor.redact(dc, s, output) } else { - engine.redact(dc, format!("{v}"), output) + redactor.redact(dc, &format!("{v}"), output) } } } diff --git a/crates/data_privacy_macros_impl/tests/snapshots/classified__success_named_field.snap b/crates/data_privacy_macros_impl/tests/snapshots/classified__success_named_field.snap index d5210a1b8..1af3a4057 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/classified__success_named_field.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/classified__success_named_field.snap @@ -32,7 +32,7 @@ where )] fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter<'_>, ) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; @@ -49,9 +49,9 @@ where }; if amount <= local_buf.len() { let s = unsafe { ::core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(dc, s, output) + redactor.redact(dc, s, output) } else { - engine.redact(dc, format!("{v:?}"), output) + redactor.redact(dc, &format!("{v:?}"), output) } } } @@ -65,7 +65,7 @@ where )] fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, output: &mut ::std::fmt::Formatter, ) -> ::core::fmt::Result { const STACK_BUFFER_SIZE: usize = 128; @@ -82,9 +82,9 @@ where }; if amount <= local_buf.len() { let s = unsafe { ::core::str::from_utf8_unchecked(&local_buf[..amount]) }; - engine.redact(dc, s, output) + redactor.redact(dc, s, output) } else { - engine.redact(dc, format!("{v}"), output) + redactor.redact(dc, &format!("{v}"), output) } } } diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple.snap index 6145b738a..2e6dfcdf9 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple.snap @@ -1,16 +1,15 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 34 expression: pretty --- impl ::data_privacy::RedactedDebug for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress(")?; - ::fmt(&self.0, engine, f)?; + ::fmt(&self.0, redactor, f)?; ::std::write!(f, ", ")?; ::std::write!(f, "{:?}", & self.1)?; ::std::write!(f, ")")?; diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple_named.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple_named.snap index 362b37c41..badb2f295 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple_named.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_multiple_named.snap @@ -1,17 +1,16 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 48 expression: pretty --- impl ::data_privacy::RedactedDebug for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress {{")?; ::std::write!(f, "{}{}: ", " ", "x")?; - ::fmt(&self.x, engine, f)?; + ::fmt(&self.x, redactor, f)?; ::std::write!(f, "{}{}: {:?}", ", ", "y", & self.y)?; ::std::write!(f, " }}")?; ::std::result::Result::Ok(()) diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_single.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_single.snap index 43650ce0d..ec15132fe 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_single.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_single.snap @@ -1,16 +1,15 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 23 expression: pretty --- impl ::data_privacy::RedactedDebug for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress(")?; - ::fmt(&self.0, engine, f)?; + ::fmt(&self.0, redactor, f)?; ::std::write!(f, ")")?; ::std::result::Result::Ok(()) } diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_unit.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_unit.snap index 32ea29abd..2262f8e42 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_unit.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_debug_unit.snap @@ -1,12 +1,11 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 53 expression: pretty --- impl ::data_privacy::RedactedDebug for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress")?; diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple.snap index ef9dcbde5..4fa563277 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple.snap @@ -1,16 +1,15 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 90 expression: pretty --- impl ::data_privacy::RedactedDisplay for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress(")?; - ::fmt(&self.0, engine, f)?; + ::fmt(&self.0, redactor, f)?; ::std::write!(f, ", ")?; ::std::write!(f, "{}", & self.1)?; ::std::write!(f, ")")?; diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple_named.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple_named.snap index e692a4a86..1cc341e72 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple_named.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_multiple_named.snap @@ -1,17 +1,16 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 104 expression: pretty --- impl ::data_privacy::RedactedDisplay for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress {{")?; ::std::write!(f, "{}{}: ", " ", "x")?; - ::fmt(&self.x, engine, f)?; + ::fmt(&self.x, redactor, f)?; ::std::write!(f, "{}{}: {}", ", ", "y", & self.y)?; ::std::write!(f, " }}")?; ::std::result::Result::Ok(()) diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_single.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_single.snap index a75caa780..43b096493 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_single.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_single.snap @@ -1,16 +1,15 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 63 expression: pretty --- impl ::data_privacy::RedactedDisplay for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress(")?; - ::fmt(&self.0, engine, f)?; + ::fmt(&self.0, redactor, f)?; ::std::write!(f, ")")?; ::std::result::Result::Ok(()) } diff --git a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_unit.snap b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_unit.snap index 5f253e2cd..d675c6f42 100644 --- a/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_unit.snap +++ b/crates/data_privacy_macros_impl/tests/snapshots/derive__redacted_display_unit.snap @@ -1,12 +1,11 @@ --- source: crates/data_privacy_macros_impl/tests/derive.rs -assertion_line: 93 expression: pretty --- impl ::data_privacy::RedactedDisplay for EmailAddress { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + redactor: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { ::std::write!(f, "EmailAddress")?; diff --git a/crates/fetch_hyper/README.md b/crates/fetch_hyper/README.md index 10d953664..d0bdcec1b 100644 --- a/crates/fetch_hyper/README.md +++ b/crates/fetch_hyper/README.md @@ -50,7 +50,7 @@ The runtime is supplied entirely by the caller via an This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG27JhwJzXkumG9Cgswtvbtk3G11RQ0eI0kW1G87aELyEYbR5YWSDgmhhbnlzcGF3bmUwLjUuMIJrZmV0Y2hfaHlwZXJlMC4xLjGCb2h0dHBfZXh0ZW5zaW9uc2UwLjQuMQ + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbbsmHAnNeS6Yb0KCzC29u2TcbXVFDR4jSRbUbztoQvIRhtHlhZIOCaGFueXNwYXduZTAuNS4wgmtmZXRjaF9oeXBlcmUwLjEuMYJvaHR0cF9leHRlbnNpb25zZTAuNC4x [__link0]: https://docs.rs/fetch_hyper/0.1.1/fetch_hyper/?search=HyperTransportBuilder [__link1]: https://docs.rs/fetch_hyper/0.1.1/fetch_hyper/?search=Connect [__link2]: https://docs.rs/fetch_hyper/0.1.1/fetch_hyper/?search=HyperTransportBuilder::configure_hyper diff --git a/crates/http_extensions/README.md b/crates/http_extensions/README.md index 62f80d419..99d95ee58 100644 --- a/crates/http_extensions/README.md +++ b/crates/http_extensions/README.md @@ -170,7 +170,7 @@ for future requests. This makes the crate particularly efficient for high-throug This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG_MLOQQsiV6zG7SjqsDGgd-WG03lILZ1aMTjG5fuakLrAwKLYWSFgmVieXRlc2YxLjExLjGCaGJ5dGVzYnVmZTAuNS4wgmRodHRwZTEuNC4xgmlodHRwX2JvZHllMS4wLjGCb2h0dHBfZXh0ZW5zaW9uc2UwLjQuMQ + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQb8ws5BCyJXrMbtKOqwMaB35YbTeUgtnVoxOMbl-5qQusDAothZIWCZWJ5dGVzZjEuMTEuMYJoYnl0ZXNidWZlMC41LjCCZGh0dHBlMS40LjGCaWh0dHBfYm9keWUxLjAuMYJvaHR0cF9leHRlbnNpb25zZTAuNC4x [__link0]: https://crates.io/crates/http/1.4.1 [__link1]: https://docs.rs/http_extensions/0.4.1/http_extensions/type.HttpRequest.html [__link10]: https://docs.rs/http_extensions/0.4.1/http_extensions/?search=StatusExt diff --git a/crates/layered/README.md b/crates/layered/README.md index 6afe9695b..c11f460dd 100644 --- a/crates/layered/README.md +++ b/crates/layered/README.md @@ -121,7 +121,7 @@ This ensures compatibility with multi-threaded async runtimes like Tokio. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG9cd3_rKpXlxG3cPQgH_Ia06Gylje5wjfL8MG8IUl5cvZMADYWSBgmdsYXllcmVkZTAuMy4w + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQb1x3f-sqleXEbdw9CAf8hrTobKWN7nCN8vwwbwhSXly9kwANhZIGCZ2xheWVyZWRlMC4zLjA [__link0]: https://docs.rs/layered/0.3.0/layered/?search=Service [__link1]: https://docs.rs/tower [__link10]: https://docs.rs/layered/0.3.0/layered/?search=Intercept diff --git a/crates/multitude/README.md b/crates/multitude/README.md index aac9e80d0..61d3ff6c4 100644 --- a/crates/multitude/README.md +++ b/crates/multitude/README.md @@ -383,9 +383,9 @@ existing `_rc`/`_arc` slice methods). This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGxSsckQO6X9rG16hze1SNSzMGw6W-TYGhj3rG1Gc1jfyNoYcYWSFgmhieXRlbXVja2YxLjI1LjCCZWJ5dGVzZjEuMTEuMYJoYnl0ZXNidWZlMC41LjCCaW11bHRpdHVkZWUwLjEuMIJoemVyb2NvcHlmMC44LjQ5 + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbFKxyRA7pf2sbXqHN7VI1LMwbDpb5NgaGPesbUZzWN_I2hhxhZIWCaGJ5dGVtdWNrZjEuMjUuMIJlYnl0ZXNmMS4xMS4xgmhieXRlc2J1ZmUwLjUuMIJpbXVsdGl0dWRlZTAuMS4wgmh6ZXJvY29weWYwLjguNDk [__link0]: https://crates.io/crates/bumpalo - [__link1]: https://docs.rs/multitude/0.1.0/multitude/?search=rc::Rc + [__link1]: https://docs.rs/multitude/0.1.0/multitude/?search=Rc [__link10]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String [__link11]: https://docs.rs/multitude/0.1.0/multitude/?search=vec::Vec [__link12]: https://crates.io/crates/dst-factory @@ -395,19 +395,19 @@ This crate was developed as part of The Oxidizer Project. Br [__link16]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::BoxUtf16Str [__link17]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::Utf16String [__link18]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::format_utf16 - [__link19]: https://docs.rs/multitude/0.1.0/multitude/?search=rc::Rc + [__link19]: https://docs.rs/multitude/0.1.0/multitude/?search=Rc [__link2]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcStr - [__link20]: https://docs.rs/multitude/0.1.0/multitude/?search=arc::Arc - [__link21]: https://docs.rs/multitude/0.1.0/multitude/?search=r#box::Box - [__link22]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena - [__link23]: https://docs.rs/multitude/0.1.0/multitude/?search=arc::Arc - [__link24]: https://docs.rs/multitude/0.1.0/multitude/?search=r#box::Box + [__link20]: https://docs.rs/multitude/0.1.0/multitude/?search=Arc + [__link21]: https://docs.rs/multitude/0.1.0/multitude/?search=Box + [__link22]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena + [__link23]: https://docs.rs/multitude/0.1.0/multitude/?search=Arc + [__link24]: https://docs.rs/multitude/0.1.0/multitude/?search=Box [__link25]: https://doc.rust-lang.org/stable/alloc/?search=boxed::Box [__link26]: https://docs.rs/multitude/0.1.0/multitude/?search=vec::Vec [__link27]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String [__link28]: https://crates.io/crates/allocator-api2 [__link29]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String - [__link3]: https://docs.rs/multitude/0.1.0/multitude/?search=arc::Arc + [__link3]: https://docs.rs/multitude/0.1.0/multitude/?search=Arc [__link30]: https://docs.rs/multitude/0.1.0/multitude/?search=vec::Vec [__link31]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String::into_arena_str [__link32]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcStr @@ -423,22 +423,22 @@ This crate was developed as part of The Oxidizer Project. Br [__link41]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::ArcUtf16Str [__link42]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::BoxStr [__link43]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::BoxUtf16Str - [__link44]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena + [__link44]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena [__link45]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String [__link46]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::Utf16String [__link47]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::format [__link48]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::format_utf16 [__link49]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String - [__link5]: https://docs.rs/multitude/0.1.0/multitude/?search=r#box::Box + [__link5]: https://docs.rs/multitude/0.1.0/multitude/?search=Box [__link50]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String::into_arena_str [__link51]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcStr [__link52]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::Utf16String [__link53]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::Utf16String::into_arena_utf16_str [__link54]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcUtf16Str - [__link55]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena - [__link56]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_arc - [__link57]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_rc - [__link58]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_box + [__link55]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena + [__link56]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_arc + [__link57]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_rc + [__link58]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_box [__link59]: https://doc.rust-lang.org/stable/core/?search=alloc::Layout [__link6]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::BoxStr [__link60]: https://crates.io/crates/dst-factory @@ -448,9 +448,9 @@ This crate was developed as part of The Oxidizer Project. Br [__link64]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::ArcStr [__link65]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::String [__link66]: https://docs.rs/multitude/0.1.0/multitude/?search=vec::Vec - [__link67]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_arc - [__link68]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_rc - [__link69]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena::alloc_dst_box + [__link67]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_arc + [__link68]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_rc + [__link69]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::alloc_dst_box [__link7]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcStr [__link70]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::RcUtf16Str [__link71]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::ArcUtf16Str @@ -460,16 +460,16 @@ This crate was developed as part of The Oxidizer Project. Br [__link75]: https://crates.io/crates/widestring [__link76]: https://docs.rs/multitude/0.1.0/multitude/?search=zerocopy::ZerocopyView [__link77]: https://docs.rs/zerocopy/0.8.49/zerocopy/?search=FromZeros - [__link78]: `Arena::zerocopy()` + [__link78]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::zerocopy [__link79]: https://docs.rs/multitude/0.1.0/multitude/?search=bytemuck::BytemuckView [__link8]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::ArcStr [__link80]: https://docs.rs/bytemuck/1.25.0/bytemuck/?search=Zeroable - [__link81]: `Arena::bytemuck()` + [__link81]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena::bytemuck [__link82]: https://doc.rust-lang.org/stable/std/convert/trait.From.html - [__link83]: https://docs.rs/multitude/0.1.0/multitude/?search=arc::Arc + [__link83]: https://docs.rs/multitude/0.1.0/multitude/?search=Arc [__link84]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::ArcStr [__link85]: https://docs.rs/bytes/1.11.1/bytes/?search=Bytes [__link86]: https://docs.rs/bytesbuf/0.5.0/bytesbuf/?search=mem::Memory - [__link87]: https://docs.rs/multitude/0.1.0/multitude/?search=arena::Arena + [__link87]: https://docs.rs/multitude/0.1.0/multitude/?search=Arena [__link88]: https://docs.rs/bytesbuf/0.5.0/bytesbuf/?search=BytesBuf [__link9]: https://docs.rs/multitude/0.1.0/multitude/?search=strings::BoxStr diff --git a/crates/multitude/src/arena_str_macros.rs b/crates/multitude/src/arena_str_macros.rs index cbd0a8193..1c3966cd3 100644 --- a/crates/multitude/src/arena_str_macros.rs +++ b/crates/multitude/src/arena_str_macros.rs @@ -126,8 +126,7 @@ macro_rules! impl_str_read_traits { }; } -pub(crate) use impl_str_accessors; -pub(crate) use impl_str_read_traits; +pub(crate) use {impl_str_accessors, impl_str_read_traits}; /// Element-generic Local-flavor storage scaffolding (`from_raw_data`, /// `from_in_chunk`, `from_owned_in_chunk`, `Clone`) shared by both the @@ -290,9 +289,7 @@ macro_rules! impl_str_handle_core { }; } -pub(crate) use impl_str_handle_core; -pub(crate) use impl_str_handle_local_storage; -pub(crate) use impl_str_handle_shared_storage; +pub(crate) use {impl_str_handle_core, impl_str_handle_local_storage, impl_str_handle_shared_storage}; /// UTF-16 sibling of [`impl_str_handle_core`] — emits `Clone`, /// `from_raw_data`, `data_ptr`, `Unpin`, `PartialEq` / diff --git a/crates/ohno/README.md b/crates/ohno/README.md index 6da7ba709..7d2c24b80 100644 --- a/crates/ohno/README.md +++ b/crates/ohno/README.md @@ -292,7 +292,7 @@ uniformly via [`Labeled::label`][__link21]. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG6wnfcY2uiFGG0Xrr3B-7vuMGx9RE6mpuHhQGxPVenE9RhyaYWSCgmRvaG5vZTAuMy4ygmtvaG5vX21hY3Jvc2UwLjMuMA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbrCd9xja6IUYbReuvcH7u-4wbH1ETqam4eFAbE9V6cT1GHJphZIKCZG9obm9lMC4zLjKCa29obm9fbWFjcm9zZTAuMy4w [__link0]: https://doc.rust-lang.org/stable/std/?search=fmt::Display [__link1]: https://doc.rust-lang.org/stable/std/?search=fmt::Debug [__link10]: https://doc.rust-lang.org/stable/std/macro.unreachable.html diff --git a/crates/recoverable/README.md b/crates/recoverable/README.md index 039043be9..7e0784ff3 100644 --- a/crates/recoverable/README.md +++ b/crates/recoverable/README.md @@ -86,7 +86,7 @@ assert_eq!(immediate.get_delay(), Some(Duration::ZERO)); This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG8WkGld0dAdTG1oAzDm5S-yFG7msTiEH07YIG3TZOaUlSzkRYWSBgmtyZWNvdmVyYWJsZWUwLjEuMg + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbxaQaV3R0B1MbWgDMOblL7IUbuaxOIQfTtggbdNk5pSVLORFhZIGCa3JlY292ZXJhYmxlZTAuMS4y [__link0]: https://docs.rs/recoverable/0.1.2/recoverable/?search=RecoveryInfo::never [__link1]: https://docs.rs/recoverable/0.1.2/recoverable/struct.RecoveryInfo.html [__link2]: https://docs.rs/recoverable/0.1.2/recoverable/trait.Recovery.html diff --git a/crates/seatbelt/README.md b/crates/seatbelt/README.md index 2b2a8d25e..fdbb977de 100644 --- a/crates/seatbelt/README.md +++ b/crates/seatbelt/README.md @@ -199,7 +199,7 @@ This crate provides several optional features that can be enabled in your `Cargo This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG2LxVq77_N9dG1R--JdN59FkG5IrJw60OjsiGw6BM6tJVV5kYWSFgmdsYXllcmVkZTAuMy4wgmtyZWNvdmVyYWJsZWUwLjEuMoJoc2VhdGJlbHRlMC41LjCCZHRpY2tlMC4zLjCCbXRvd2VyX3NlcnZpY2VlMC4zLjM + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbYvFWrvv8310bVH74l03n0WQbkisnDrQ6OyIbDoEzq0lVXmRhZIWCZ2xheWVyZWRlMC4zLjCCa3JlY292ZXJhYmxlZTAuMS4ygmhzZWF0YmVsdGUwLjUuMIJkdGlja2UwLjMuMIJtdG93ZXJfc2VydmljZWUwLjMuMw [__link0]: https://crates.io/crates/layered/0.3.0 [__link1]: https://docs.rs/layered/0.3.0/layered/?search=Stack [__link10]: https://docs.rs/seatbelt/0.5.0/seatbelt/hedging/index.html diff --git a/crates/seatbelt_http/README.md b/crates/seatbelt_http/README.md index 4fcd739aa..3ccc5fde0 100644 --- a/crates/seatbelt_http/README.md +++ b/crates/seatbelt_http/README.md @@ -48,7 +48,7 @@ type aliases and an extension trait: This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG0zpOHumdJRlG7vvc1rvGGXMG8lb6BInDEwlGyUJKXppjYapYWSDgm9odHRwX2V4dGVuc2lvbnNlMC40LjGCaHNlYXRiZWx0ZTAuNS4wgm1zZWF0YmVsdF9odHRwZTAuMi4x + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbTOk4e6Z0lGUbu-9zWu8YZcwbyVvoEicMTCUbJQkpemmNhqlhZIOCb2h0dHBfZXh0ZW5zaW9uc2UwLjQuMYJoc2VhdGJlbHRlMC41LjCCbXNlYXRiZWx0X2h0dHBlMC4yLjE [__link0]: https://crates.io/crates/seatbelt/0.5.0 [__link1]: https://crates.io/crates/seatbelt/0.5.0 [__link2]: https://docs.rs/http_extensions/0.4.1/http_extensions/?search=HttpRequest diff --git a/crates/templated_uri/Cargo.toml b/crates/templated_uri/Cargo.toml index d22ba657c..842cc9e75 100644 --- a/crates/templated_uri/Cargo.toml +++ b/crates/templated_uri/Cargo.toml @@ -18,10 +18,13 @@ repository = "https://github.com/microsoft/oxidizer/tree/main/crates/templated_u [package.metadata.cargo_check_external_types] allowed_external_types = [ - "data_privacy::data_class::DataClass", - "data_privacy::redacted::RedactedDebug", - "data_privacy::redacted::RedactedDisplay", - "data_privacy::redaction_engine::RedactionEngine", + "data_privacy_core::classified::Classified", + "data_privacy_core::data_class::DataClass", + "data_privacy_core::data_class::IntoDataClass", + "data_privacy_core::redacted::RedactedDebug", + "data_privacy_core::redacted::RedactedDisplay", + "data_privacy_core::redacted::RedactedToString", + "data_privacy_core::redactor::Redactor", "data_privacy::sensitive::Sensitive", "http::error::Error", "http::uri::authority::Authority", diff --git a/crates/templated_uri/README.md b/crates/templated_uri/README.md index 8bcb07c21..000e77b29 100644 --- a/crates/templated_uri/README.md +++ b/crates/templated_uri/README.md @@ -198,7 +198,7 @@ and servers based on [`hyper`][__link16] like [`reqwest`][__link17]. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG63iTBJYMadJG6nseEEoATCvG3YJS_EajemqG70n7pjeggIrYWSCgmRodHRwZTEuNC4xgm10ZW1wbGF0ZWRfdXJpZTAuMi4x + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbreJMElgxp0kbqex4QSgBMK8bdglL8RqN6aobvSfumN6CAithZIKCZGh0dHBlMS40LjGCbXRlbXBsYXRlZF91cmllMC4yLjE [__link0]: https://docs.rs/templated_uri/0.2.1/templated_uri/?search=Uri [__link1]: https://docs.rs/templated_uri/0.2.1/templated_uri/?search=BaseUri [__link10]: https://docs.rs/templated_uri/0.2.1/templated_uri/?search=Escape diff --git a/crates/templated_uri/src/path_and_query.rs b/crates/templated_uri/src/path_and_query.rs index 909845138..ae3d8eddd 100644 --- a/crates/templated_uri/src/path_and_query.rs +++ b/crates/templated_uri/src/path_and_query.rs @@ -7,7 +7,7 @@ use std::fmt::Formatter; use std::ops::Deref; use std::sync::Arc; -use data_privacy::{Classified, RedactedDebug, RedactedDisplay, RedactedToString, RedactionEngine, Sensitive}; +use data_privacy::{Classified, RedactedDebug, RedactedDisplay, RedactedToString, Redactor, Sensitive}; use http::uri::PathAndQuery as HttpPathAndQuery; use crate::error::UriError; @@ -80,15 +80,15 @@ impl PathAndQuery { impl RedactedDisplay for PathAndQuery { #[cfg_attr(test, mutants::skip)] // Do not mutate display output. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter<'_>) -> fmt::Result { match &self.0 { PathAndQueryInner::Static(classified_pq) => { // We can't use to_string in redaction because it automatically prepends a slash if the path doesn't start with one. // as_str doesn't do that, so we declassify to get the inner PathAndQuery and then use as_str. let reclassified = Sensitive::new(classified_pq.declassify_ref().as_str(), classified_pq.data_class().clone()); - RedactedDisplay::fmt(&reclassified, engine, f) + RedactedDisplay::fmt(&reclassified, redactor, f) } - PathAndQueryInner::Templated(templated) => RedactedDisplay::fmt(&**templated, engine, f), + PathAndQueryInner::Templated(templated) => RedactedDisplay::fmt(&**templated, redactor, f), } } } @@ -105,12 +105,12 @@ impl fmt::Debug for PathAndQuery { impl RedactedDebug for PathAndQuery { #[cfg_attr(test, mutants::skip)] // Do not mutate debug output. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter<'_>) -> fmt::Result { let mut tuple = f.debug_tuple("PathAndQuery"); match &self.0 { PathAndQueryInner::Static(_) => tuple.finish(), PathAndQueryInner::Templated(templated) => { - let rendered = templated.deref().to_redacted_string(engine); + let rendered = templated.deref().to_redacted_string(redactor); tuple.field(&rendered).finish() } } diff --git a/crates/templated_uri/src/uri.rs b/crates/templated_uri/src/uri.rs index 076071c6a..9af137280 100644 --- a/crates/templated_uri/src/uri.rs +++ b/crates/templated_uri/src/uri.rs @@ -7,7 +7,7 @@ use std::fmt; use std::fmt::{Debug, Formatter}; use std::str::FromStr; -use data_privacy::{DataClass, RedactedDebug, RedactedDisplay, RedactedToString, RedactionEngine, Sensitive}; +use data_privacy::{DataClass, RedactedDebug, RedactedDisplay, RedactedToString, Redactor, Sensitive}; use http::uri::{Parts, PathAndQuery as HttpPathAndQuery}; use crate::error::UriError; @@ -169,12 +169,12 @@ impl Uri { impl RedactedDisplay for Uri { #[cfg_attr(test, mutants::skip)] // Do not mutate display output. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> fmt::Result { if let Some(base_uri) = self.base_uri.as_ref() { write!(f, "{base_uri}")?; } - match self.path_and_query.as_ref().map(|p| p.to_redacted_string(engine)) { + match self.path_and_query.as_ref().map(|p| p.to_redacted_string(redactor)) { // If there is a base URI, trim the leading slash from the path and query to avoid double slashes. Some(pq) if self.base_uri.is_some() => f.write_str(pq.trim_start_matches('/'))?, Some(pq) => f.write_str(&pq)?, @@ -186,12 +186,12 @@ impl RedactedDisplay for Uri { impl RedactedDebug for Uri { #[cfg_attr(test, mutants::skip)] // Do not mutate debug output. - fn fmt(&self, engine: &RedactionEngine, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, redactor: &dyn Redactor, f: &mut Formatter) -> fmt::Result { if let Some(base_uri) = self.base_uri.as_ref() { write!(f, "{base_uri}")?; } - match self.path_and_query.as_ref().map(|p| p.to_redacted_string(engine)) { + match self.path_and_query.as_ref().map(|p| p.to_redacted_string(redactor)) { // If there is a base URI, trim the leading slash from the path and query to avoid double slashes. Some(pq) if self.base_uri.is_some() => f.write_str(pq.trim_start_matches('/'))?, Some(pq) => f.write_str(&pq)?, @@ -343,6 +343,8 @@ impl TryFrom for HttpPathAndQuery { #[cfg(test)] mod tests { + use data_privacy::RedactionEngine; + use super::*; #[test] diff --git a/crates/templated_uri_macros_impl/src/enum_template.rs b/crates/templated_uri_macros_impl/src/enum_template.rs index acfcb017a..67ea4051f 100644 --- a/crates/templated_uri_macros_impl/src/enum_template.rs +++ b/crates/templated_uri_macros_impl/src/enum_template.rs @@ -80,7 +80,7 @@ pub fn enum_template(ident: &Ident, data: &DataEnum) -> TokenStream { } impl ::data_privacy::RedactedDisplay for #ident { - fn fmt(&self, engine: &::data_privacy::RedactionEngine, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match self { #(#variant_matches => ::data_privacy::RedactedDisplay::fmt(template_variant, engine, f)?),* } diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__field_level_unredacted.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__field_level_unredacted.snap index 473d54cb4..9a2076930 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__field_level_unredacted.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__field_level_unredacted.snap @@ -81,7 +81,7 @@ impl ::std::fmt::Debug for Test { impl ::data_privacy::RedactedDisplay for Test { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/example.com/")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_codegen.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_codegen.snap index 95aefaad2..3beeefa52 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_codegen.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_codegen.snap @@ -92,7 +92,7 @@ impl ::std::fmt::Debug for OptionalTest { impl ::data_privacy::RedactedDisplay for OptionalTest { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/items/")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_with_unredacted_codegen.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_with_unredacted_codegen.snap index f0e74b2b5..363fa1582 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_with_unredacted_codegen.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_field_with_unredacted_codegen.snap @@ -84,7 +84,7 @@ impl ::std::fmt::Debug for OptionalUnredactedTest { impl ::data_privacy::RedactedDisplay for OptionalUnredactedTest { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/items")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_reference_field_codegen.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_reference_field_codegen.snap index 62d0986d5..d6964033d 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_reference_field_codegen.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__optional_reference_field_codegen.snap @@ -63,7 +63,7 @@ impl ::std::fmt::Debug for ReferenceOptional { impl ::data_privacy::RedactedDisplay for ReferenceOptional { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/items")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__query_param_is_kv_expansion.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__query_param_is_kv_expansion.snap index 55ca14bab..830ef26a7 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__query_param_is_kv_expansion.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__query_param_is_kv_expansion.snap @@ -72,7 +72,7 @@ impl ::std::fmt::Debug for QueryTest { impl ::data_privacy::RedactedDisplay for QueryTest { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/api/")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__template_enum_impl.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__template_enum_impl.snap index fdf3e7bac..933fce464 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__template_enum_impl.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__template_enum_impl.snap @@ -60,7 +60,7 @@ impl ::std::fmt::Debug for Test { impl ::data_privacy::RedactedDisplay for Test { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { match self { diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_unredacted_uri_impl.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_unredacted_uri_impl.snap index 79b97a512..24d5135da 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_unredacted_uri_impl.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_unredacted_uri_impl.snap @@ -81,7 +81,7 @@ impl ::std::fmt::Debug for Test { impl ::data_privacy::RedactedDisplay for Test { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/example.com/")?; diff --git a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_uri_impl.snap b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_uri_impl.snap index 2aff296ef..9564d97b3 100644 --- a/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_uri_impl.snap +++ b/crates/templated_uri_macros_impl/src/snapshots/templated_uri_macros_impl__tests__templated_uri_impl.snap @@ -81,7 +81,7 @@ impl ::std::fmt::Debug for Test { impl ::data_privacy::RedactedDisplay for Test { fn fmt( &self, - engine: &::data_privacy::RedactionEngine, + engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter, ) -> ::std::fmt::Result { f.write_str("/example.com/")?; diff --git a/crates/templated_uri_macros_impl/src/struct_template.rs b/crates/templated_uri_macros_impl/src/struct_template.rs index 75410fe07..7e24b1dfc 100644 --- a/crates/templated_uri_macros_impl/src/struct_template.rs +++ b/crates/templated_uri_macros_impl/src/struct_template.rs @@ -171,7 +171,7 @@ pub fn struct_template(ident: Ident, data: &DataStruct, attrs: &[Attribute]) -> } impl ::data_privacy::RedactedDisplay for #ident { - fn fmt(&self, engine: &::data_privacy::RedactionEngine, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, engine: &dyn ::data_privacy::Redactor, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { #redacted_display } } diff --git a/crates/thread_aware/README.md b/crates/thread_aware/README.md index c752efeb1..58c45189d 100644 --- a/crates/thread_aware/README.md +++ b/crates/thread_aware/README.md @@ -147,7 +147,7 @@ impl Service { This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG0kOaaLnd-MSG7mqaA0zTLDsGz_TKKPtf1H0G5WXL5jFrDU4YWSCgmx0aHJlYWRfYXdhcmVlMC43LjCCc3RocmVhZF9hd2FyZV9tYWNyb3NlMC43LjA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbSQ5poud34xIbuapoDTNMsOwbP9Moo-1_UfQblZcvmMWsNThhZIKCbHRocmVhZF9hd2FyZWUwLjcuMIJzdGhyZWFkX2F3YXJlX21hY3Jvc2UwLjcuMA [__link0]: https://docs.rs/thread_aware_macros/0.7.0/thread_aware_macros/?search=ThreadAware [__link1]: https://doc.rust-lang.org/stable/std/clone/trait.Clone.html [__link10]: https://docs.rs/thread_aware_macros/0.7.0/thread_aware_macros/?search=ThreadAware diff --git a/crates/tick/README.md b/crates/tick/README.md index 9a98f2a89..20e9428f4 100644 --- a/crates/tick/README.md +++ b/crates/tick/README.md @@ -227,7 +227,7 @@ contain additional examples of how to use the time primitives. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGy_QQ3DXXBTMG-wYrAGx4Q0zGwo33-kq3ZvDG8lbbk3MMbc-YWSCgmx0aHJlYWRfYXdhcmVlMC43LjCCZHRpY2tlMC4zLjA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQbL9BDcNdcFMwb7BisAbHhDTMbCjff6Srdm8MbyVtuTcwxtz5hZIKCbHRocmVhZF9hd2FyZWUwLjcuMIJkdGlja2UwLjMuMA [__link0]: https://docs.rs/tick/0.3.0/tick/?search=ClockControl [__link1]: https://docs.rs/tick/0.3.0/tick/?search=Clock [__link10]: https://docs.rs/tick/0.3.0/tick/?search=FutureExt diff --git a/crates/uniflight/README.md b/crates/uniflight/README.md index 76338e4bd..958c9dfd5 100644 --- a/crates/uniflight/README.md +++ b/crates/uniflight/README.md @@ -135,7 +135,7 @@ Use `--save-baseline` and `--baseline` flags to track regressions over time. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG-GLKhyiSDuBG53vClQ0bibwG1RKiKY8DLWOG2WNr0uorgHrYWSCgmx0aHJlYWRfYXdhcmVlMC43LjCCaXVuaWZsaWdodGUwLjIuMA + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbLiTyV0MU86EbZU15e0PmecoboQ9jo59bnAEbyDXw04U13GlhYvRhcoQb4YsqHKJIO4Ebne8KVDRuJvAbVEqIpjwMtY4bZY2vS6iuAethZIKCbHRocmVhZF9hd2FyZWUwLjcuMIJpdW5pZmxpZ2h0ZTAuMi4w [__link0]: https://docs.rs/uniflight/0.2.0/uniflight/struct.Merger.html [__link1]: https://docs.rs/uniflight/0.2.0/uniflight/?search=Merger::execute [__link10]: https://doc.rust-lang.org/stable/std/?search=hash::Hash diff --git a/justfiles/basic.just b/justfiles/basic.just index 53433880d..f80b45032 100644 --- a/justfiles/basic.just +++ b/justfiles/basic.just @@ -74,7 +74,7 @@ docs: # Run all stand-alone example binaries to ensure they complete with exit code 0. # Implemented in Rust (via cargo-script) so that exit codes propagate cleanly -# through `just` and CI on every platform — see AB#7306508. +# through `just` and CI on every platform - see AB#7306508. [script] examples PROFILE='dev' EXCLUDE='': $ErrorActionPreference = "Stop" diff --git a/scripts/mutants.rs b/scripts/mutants.rs index 21deef027..360715916 100644 --- a/scripts/mutants.rs +++ b/scripts/mutants.rs @@ -40,7 +40,7 @@ struct Args { // mutations if their tests reside in dependent packages. const TEST_GROUPS: &[&[&str]] = &[ &["bytesbuf"], - &["data_privacy", "data_privacy_macros", "data_privacy_macros_impl"], + &["data_privacy", "data_privacy_core", "data_privacy_macros", "data_privacy_macros_impl"], &["fundle", "fundle_macros", "fundle_macros_impl"], &["ohno", "ohno_macros"], &["templated_uri", "templated_uri_macros", "templated_uri_macros_impl"], diff --git a/scripts/publish-gh-release.rs b/scripts/publish-gh-release.rs index e72ab0c53..9048306d6 100644 --- a/scripts/publish-gh-release.rs +++ b/scripts/publish-gh-release.rs @@ -114,7 +114,7 @@ fn extract_changelog(crate_path: &str, crate_name: &str, version: &str) -> Resul for line in content.lines() { if line.starts_with("## ") { if found { - // Reached the next section — stop. + // Reached the next section - stop. break; } // Match "## [version]" or "## version" with an optional trailing date/suffix. diff --git a/scripts/run-examples.rs b/scripts/run-examples.rs index 04e0945a0..fa202bcc7 100644 --- a/scripts/run-examples.rs +++ b/scripts/run-examples.rs @@ -31,7 +31,7 @@ const TIMEOUT: Duration = Duration::from_secs(30); /// Examples that are expected to panic, hang, or require user interaction /// and so must be skipped by this runner. const EXCLUDED_EXAMPLES: &[&str] = &[ - // Interactive — requires user input from stdin. + // Interactive - requires user input from stdin. "employees", ];