use std::fmt;
use serde_json::Value;
use crate::{
RedactValue as _,
RedactedValue,
RedactionPolicy,
};
#[cfg(feature = "serde")]
use super::internal::{
JsonRedactionState,
JsonUnkeyedValuePolicy,
};
#[must_use = "format or serialize the redacted JSON view"]
pub struct RedactedJson<'value, 'policy> {
value: &'value Value,
policy: &'policy RedactionPolicy,
}
impl<'value, 'policy> RedactedJson<'value, 'policy> {
#[inline(always)]
pub const fn new(
value: &'value Value,
policy: &'policy RedactionPolicy,
) -> Self {
Self { value, policy }
}
#[cfg(feature = "serde")]
fn to_redacted_value(&self) -> Value {
let mut value = self.value.clone();
let mut remaining_mask_bytes = usize::MAX;
let mut state = JsonRedactionState::new(
self.policy,
JsonUnkeyedValuePolicy::PassThrough,
&mut remaining_mask_bytes,
);
let _ = state.redact(&mut value);
value
}
}
impl fmt::Debug for RedactedJson<'_, '_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_json(self.value, self.policy, formatter)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for RedactedJson<'_, '_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde::Serialize::serialize(&self.to_redacted_value(), serializer)
}
}
fn fmt_json(
value: &Value,
policy: &RedactionPolicy,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match value {
Value::Array(values) => {
let mut output = formatter.debug_list();
for value in values {
output.entry(&RedactedJson::new(value, policy));
}
output.finish()
}
Value::Object(values) => {
let mut output = formatter.debug_map();
for (key, value) in values {
if let Some(sensitivity) = policy.sensitivity_for(key) {
fmt_masked_entry(
&mut output,
key,
value,
sensitivity,
policy,
);
} else {
output.entry(key, &RedactedJson::new(value, policy));
}
}
output.finish()
}
value => fmt::Debug::fmt(value, formatter),
}
}
fn fmt_masked_entry(
output: &mut fmt::DebugMap<'_, '_>,
key: &str,
value: &Value,
sensitivity: crate::Sensitivity,
policy: &RedactionPolicy,
) {
match value {
Value::String(text) => {
let redacted = text.redact_value(sensitivity, policy.masking());
output.entry(&key, &redacted);
}
_ => {
let redacted = RedactedValue::opaque(sensitivity, policy.masking());
output.entry(&key, &redacted);
}
};
}