use std::borrow::Cow;
use super::{
FieldSanitizePolicy,
NameMatchMode,
SensitiveFieldPreset,
SensitivityLevel,
canonicalize_field_name,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldSanitizer {
policy: FieldSanitizePolicy,
}
impl FieldSanitizer {
pub const fn new(policy: FieldSanitizePolicy) -> Self {
Self { policy }
}
pub const fn policy(&self) -> &FieldSanitizePolicy {
&self.policy
}
pub fn policy_mut(&mut self) -> &mut FieldSanitizePolicy {
&mut self.policy
}
pub fn insert_sensitive_field(&mut self, field: &str, level: SensitivityLevel) {
self.policy.sensitive_fields.insert(field, level);
}
pub fn extend_sensitive_fields<I, S>(&mut self, fields: I, level: SensitivityLevel)
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.policy.sensitive_fields.extend(fields, level);
}
pub fn extend_preset(&mut self, preset: SensitiveFieldPreset) {
self.policy.sensitive_fields.extend_preset(preset);
}
pub fn sensitivity_for_name(
&self,
name: &str,
match_mode: NameMatchMode,
) -> Option<SensitivityLevel> {
let fields = &self.policy.sensitive_fields;
if let Some(level) = fields.level_for(name) {
return Some(level);
}
if match_mode == NameMatchMode::Exact {
return None;
}
let canonical_name = canonicalize_field_name(name);
if canonical_name.is_empty() {
return None;
}
fields
.iter()
.filter_map(|(field, level)| {
if canonical_name != field && canonical_name.ends_with(field) {
Some((field.len(), level))
} else {
None
}
})
.max_by_key(|(field_len, level)| (*field_len, *level))
.map(|(_, level)| level)
}
pub fn sanitize_value<'a>(
&self,
field: &str,
value: &'a str,
match_mode: NameMatchMode,
) -> Cow<'a, str> {
let Some(level) = self.sensitivity_for_name(field, match_mode) else {
return Cow::Borrowed(value);
};
self.policy.mask_policies.for_level(level).mask(value)
}
pub fn sanitize_map<M>(&self, map: &M, match_mode: NameMatchMode) -> M
where
for<'a> &'a M: IntoIterator<Item = (&'a String, &'a String)>,
M: FromIterator<(String, String)>,
{
map.into_iter()
.map(|(field, value)| {
(
field.clone(),
self.sanitize_value(field, value.as_str(), match_mode)
.into_owned(),
)
})
.collect()
}
pub fn sanitize_map_in_place<M>(&self, map: &mut M, match_mode: NameMatchMode)
where
for<'a> &'a mut M: IntoIterator<Item = (&'a String, &'a mut String)>,
{
for (field, value) in map {
let sanitized = self.sanitize_value(field, value.as_str(), match_mode);
if let Cow::Owned(sanitized) = sanitized {
*value = sanitized;
}
}
}
}
impl Default for FieldSanitizer {
fn default() -> Self {
Self::new(FieldSanitizePolicy::default())
}
}