use std::fmt::{
self,
Write as _,
};
use crate::{
LogOutputLimit,
RedactionPolicy,
Sensitivity,
text::internal::BoundedLogEscapeWriter,
};
use super::{
RedactedJson,
redact_json_text_in_place::redacted_json_text,
};
#[must_use = "format or serialize the redacted JSON text view"]
pub struct RedactedJsonText<'text, 'policy> {
text: &'text str,
policy: &'policy RedactionPolicy,
}
impl<'text, 'policy> RedactedJsonText<'text, 'policy> {
pub const fn new(
text: &'text str,
policy: &'policy RedactionPolicy,
) -> Self {
Self { text, policy }
}
#[inline(always)]
const fn exceeds_diagnostic_input_budget(&self) -> bool {
self.text.len()
> self.policy.limits().diagnostic_event().max_input_bytes()
}
#[inline(always)]
fn opaque_secret(&self) -> &str {
self.policy.masking().mask_opaque(Sensitivity::Secret)
}
fn diagnostic_json_text(&self) -> String {
if self.exceeds_diagnostic_input_budget() {
return self.opaque_secret().to_owned();
}
redacted_json_text(self.text, self.policy)
}
}
impl fmt::Debug for RedactedJsonText<'_, '_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut writer = BoundedLogEscapeWriter::new(LogOutputLimit::from(
self.policy.limits().diagnostic_event(),
));
if self.exceeds_diagnostic_input_budget() {
let _ = write!(&mut writer, "{:?}", self.opaque_secret());
} else {
match serde_json::from_str(self.text) {
Ok(value) if formatter.alternate() => {
let _ = write!(
&mut writer,
"{:#?}",
RedactedJson::new(&value, self.policy),
);
}
Ok(value) => {
let _ = write!(
&mut writer,
"{:?}",
RedactedJson::new(&value, self.policy),
);
}
Err(_) => {
let _ = write!(&mut writer, "{:?}", self.opaque_secret());
}
}
}
formatter.write_str(&writer.finish())
}
}
impl fmt::Display for RedactedJsonText<'_, '_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut writer = BoundedLogEscapeWriter::new(LogOutputLimit::from(
self.policy.limits().diagnostic_event(),
));
let _ = writer.write_str(&self.diagnostic_json_text());
formatter.write_str(&writer.finish())
}
}
mod session_view {
use std::fmt;
use crate::{
RedactedJsonText,
RedactionSession,
Sensitivity,
policy::OutputCharge,
};
#[must_use = "format the nested redacted JSON text view"]
pub struct RedactedJsonTextSession<'text, 'session, 'policy> {
text: &'text str,
session: &'session RedactionSession<'policy>,
}
impl<'text, 'session, 'policy>
RedactedJsonTextSession<'text, 'session, 'policy>
{
#[inline(always)]
pub fn new(
text: &'text str,
session: &'session RedactionSession<'policy>,
) -> Self {
Self { text, session }
}
fn render(&self) -> String {
let policy = self.session.policy();
if !self.session.consume_input(self.text.len()) {
return self.fallback();
}
let mut rendered = String::new();
if fmt::write(
&mut rendered,
format_args!("{:?}", RedactedJsonText::new(self.text, policy),),
)
.is_err()
{
return self.fallback();
}
let fallback = policy.masking().mask_opaque(Sensitivity::Secret);
match self
.session
.charge_output_or_fallback(rendered.len(), fallback.len())
{
OutputCharge::Complete => rendered,
OutputCharge::Fallback => fallback.to_owned(),
OutputCharge::Exhausted => String::new(),
}
}
fn fallback(&self) -> String {
let fallback = self
.session
.policy()
.masking()
.mask_opaque(Sensitivity::Secret);
match self
.session
.charge_output_or_fallback(fallback.len(), fallback.len())
{
OutputCharge::Complete => fallback.to_owned(),
OutputCharge::Fallback | OutputCharge::Exhausted => {
String::new()
}
}
}
}
impl fmt::Debug for RedactedJsonTextSession<'_, '_, '_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.render())
}
}
impl fmt::Display for RedactedJsonTextSession<'_, '_, '_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.render())
}
}
}
pub use session_view::RedactedJsonTextSession;
#[cfg(feature = "serde")]
impl serde::Serialize for RedactedJsonText<'_, '_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&redacted_json_text(self.text, self.policy))
}
}