use std::{borrow::Cow, collections::VecDeque};
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;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` has no declared redaction behavior",
label = "raw values cannot be redacted or certified as redacted output",
note = "derive `Sensitive`, `NotSensitive`, or `NotSensitiveDisplay` on the type",
note = "or wrap the value in `SensitiveValue<T, P>` / `NotSensitiveValue<T>`"
)]
pub trait Redactable: RedactableWithMapper {
#[must_use]
fn redact(self) -> Self {
super::redact::redact(self)
}
}
impl<T: Redactable> Redactable for Option<T> {}
impl<T: Redactable, E: Redactable> Redactable for Result<T, E> {}
impl<T: Redactable> Redactable for Vec<T> {}
impl<T: Redactable> Redactable for VecDeque<T> {}
impl<T: Redactable, const N: usize> Redactable for [T; N] {}
impl<T: Redactable> Redactable for Box<T> {}
impl<T: Redactable + Clone> Redactable for std::sync::Arc<T> {}
impl<T: Redactable + Clone> Redactable for std::rc::Rc<T> {}
impl<T: Redactable> Redactable for std::cell::RefCell<T> {}
impl<T: Redactable + Copy> Redactable for std::cell::Cell<T> {}
impl<T: Redactable> Redactable for std::sync::Mutex<T> {}
impl<T: Redactable> Redactable for std::sync::RwLock<T> {}
impl<K, V, S> Redactable for std::collections::HashMap<K, V, S>
where
K: std::hash::Hash + Eq,
V: Redactable,
S: std::hash::BuildHasher + Clone,
{
}
impl<K: Ord, V: Redactable> Redactable for std::collections::BTreeMap<K, V> {}
impl<T, S> Redactable for std::collections::HashSet<T, S>
where
T: Redactable + std::hash::Hash + Eq,
S: std::hash::BuildHasher + Clone,
{
}
impl<T: Redactable + Ord> Redactable for std::collections::BTreeSet<T> {}
macro_rules! impl_tuple_redactable {
($($name:ident),+ $(,)?) => {
impl<$($name),+> Redactable for ($($name,)+)
where
$($name: Redactable,)+
{
}
};
}
impl_tuple_redactable!(T0);
impl_tuple_redactable!(T0, T1);
impl_tuple_redactable!(T0, T1, T2);
impl_tuple_redactable!(T0, T1, T2, T3);
#[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]");
}
}