use serde_json::Value;
use crate::{
RedactionPolicy,
Sensitivity,
};
use super::{
JsonRedactionOutcome,
JsonUnkeyedValuePolicy,
};
pub(crate) struct JsonRedactionState<'policy, 'budget, 'marker> {
policy: &'policy RedactionPolicy,
unkeyed: JsonUnkeyedValuePolicy<'marker>,
remaining_mask_bytes: &'budget mut usize,
}
impl<'policy, 'budget, 'marker> JsonRedactionState<'policy, 'budget, 'marker> {
#[inline(always)]
pub(crate) const fn new(
policy: &'policy RedactionPolicy,
unkeyed: JsonUnkeyedValuePolicy<'marker>,
remaining_mask_bytes: &'budget mut usize,
) -> Self {
Self {
policy,
unkeyed,
remaining_mask_bytes,
}
}
pub(crate) fn redact(&mut self, value: &mut Value) -> JsonRedactionOutcome {
self.redact_value(value, false)
}
fn redact_value(
&mut self,
value: &mut Value,
has_field: bool,
) -> JsonRedactionOutcome {
match value {
Value::Object(values) => self.redact_object(values),
Value::Array(values) => self.redact_array(values, has_field),
Value::Null
| Value::Bool(_)
| Value::Number(_)
| Value::String(_) => self.redact_scalar(value, has_field),
}
}
fn redact_object(
&mut self,
values: &mut serde_json::Map<String, Value>,
) -> JsonRedactionOutcome {
let mut outcome = JsonRedactionOutcome::default();
for (key, value) in values {
if let Some(level) = self.policy.sensitivity_for(key) {
self.mask_keyed_value(value, level);
} else {
outcome.merge(self.redact_value(value, true));
}
}
outcome
}
fn redact_array(
&mut self,
values: &mut Vec<Value>,
has_field: bool,
) -> JsonRedactionOutcome {
let mut outcome = JsonRedactionOutcome::default();
for value in values {
outcome.merge(self.redact_value(value, has_field));
}
outcome
}
fn redact_scalar(
&mut self,
value: &mut Value,
has_field: bool,
) -> JsonRedactionOutcome {
if has_field {
return JsonRedactionOutcome::default();
}
match self.unkeyed {
JsonUnkeyedValuePolicy::PassThrough => {
JsonRedactionOutcome::passed_unkeyed()
}
JsonUnkeyedValuePolicy::Redact {
marker,
truncated_marker,
} => {
*value = Value::String(
self.take_unkeyed_marker(marker, truncated_marker),
);
JsonRedactionOutcome::default()
}
}
}
fn mask_keyed_value(&mut self, value: &mut Value, level: Sensitivity) {
let masked = match value {
Value::String(text) => self
.policy
.masking()
.mask_bounded(level, text, *self.remaining_mask_bytes)
.into_owned(),
_ => self
.policy
.masking()
.mask_opaque_bounded(level, *self.remaining_mask_bytes),
};
*self.remaining_mask_bytes =
self.remaining_mask_bytes.saturating_sub(masked.len());
*value = Value::String(masked);
}
fn take_unkeyed_marker(
&mut self,
marker: &str,
truncated_marker: &str,
) -> String {
let selected = if *self.remaining_mask_bytes >= marker.len() {
marker
} else if *self.remaining_mask_bytes >= truncated_marker.len() {
truncated_marker
} else {
return String::new();
};
*self.remaining_mask_bytes =
self.remaining_mask_bytes.saturating_sub(selected.len());
selected.to_owned()
}
}