use std::borrow::Cow;
use super::redact::RedactableMapper;
use crate::policy::{RedactionPolicy, TextRedactionPolicy};
pub trait SensitiveWithPolicy<P>: Sized {
#[must_use]
fn redact_with_policy(self, policy: &TextRedactionPolicy) -> Self;
#[must_use]
fn redacted_string(&self, policy: &TextRedactionPolicy) -> String;
}
impl<P: RedactionPolicy> SensitiveWithPolicy<P> for String {
fn redact_with_policy(self, policy: &TextRedactionPolicy) -> Self {
policy.apply_to(self.as_str())
}
fn redacted_string(&self, policy: &TextRedactionPolicy) -> String {
policy.apply_to(self.as_str())
}
}
impl<P: RedactionPolicy> SensitiveWithPolicy<P> for Cow<'_, str> {
fn redact_with_policy(self, policy: &TextRedactionPolicy) -> Self {
Cow::Owned(policy.apply_to(self.as_ref()))
}
fn redacted_string(&self, policy: &TextRedactionPolicy) -> String {
policy.apply_to(self.as_ref())
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not implement `RedactableWithMapper`",
label = "this type cannot be walked for sensitive data",
note = "use `#[derive(Sensitive)]` on the type definition",
note = "or use `#[sensitive(Policy)]` if this is a leaf value like String"
)]
#[doc(hidden)]
pub trait RedactableWithMapper: Sized {
#[must_use]
fn redact_with<M: RedactableMapper>(self, mapper: &M) -> Self;
}
pub trait Redactable: RedactableWithMapper {
#[must_use]
fn redact(self) -> Self {
super::redact::redact(self)
}
}
impl<T> Redactable for T where T: RedactableWithMapper {}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use super::SensitiveWithPolicy;
use crate::policy::{Secret, TextRedactionPolicy};
#[test]
fn string_redact_with_policy() {
let original = String::from("my_secret");
let policy = TextRedactionPolicy::default_full();
let redacted: String =
<String as SensitiveWithPolicy<Secret>>::redact_with_policy(original, &policy);
assert_eq!(redacted, "[REDACTED]");
}
#[test]
fn string_redacted_string() {
let original = String::from("my_secret");
let policy = TextRedactionPolicy::default_full();
let result = <String as SensitiveWithPolicy<Secret>>::redacted_string(&original, &policy);
assert_eq!(result, "[REDACTED]");
}
#[test]
fn cow_redact_with_policy() {
let original: Cow<'static, str> = Cow::Borrowed("my_secret");
let policy = TextRedactionPolicy::default_full();
let redacted =
<Cow<'_, str> as SensitiveWithPolicy<Secret>>::redact_with_policy(original, &policy);
match redacted {
Cow::Owned(value) => assert_eq!(value, "[REDACTED]"),
Cow::Borrowed(_) => panic!("redacted Cow should be owned"),
}
}
#[test]
fn cow_redacted_string() {
let original: Cow<'static, str> = Cow::Borrowed("my_secret");
let policy = TextRedactionPolicy::default_full();
let result =
<Cow<'_, str> as SensitiveWithPolicy<Secret>>::redacted_string(&original, &policy);
assert_eq!(result, "[REDACTED]");
}
}