use std::{
borrow::Cow,
fmt,
fmt::Write as _,
};
use crate::{
InputOutputLimit,
LogOutputLimit,
RedactionSession,
Redactor,
Sensitivity,
};
use super::{
DiagnosticWriteStatus,
LogSafeText,
internal::BoundedLogEscapeWriter,
};
#[must_use = "finish the diagnostic into log-safe text"]
pub struct DiagnosticLogBuilder {
writer: BoundedLogEscapeWriter,
}
impl DiagnosticLogBuilder {
#[inline]
pub fn new(budget: InputOutputLimit) -> Self {
Self {
writer: BoundedLogEscapeWriter::new(LogOutputLimit::from(budget)),
}
}
pub fn push_fmt(
&mut self,
arguments: fmt::Arguments<'_>,
) -> Result<DiagnosticWriteStatus, fmt::Error> {
if self.writer.is_truncated() {
return Ok(DiagnosticWriteStatus::Truncated);
}
match fmt::write(&mut self.writer, arguments) {
Ok(()) => Ok(DiagnosticWriteStatus::Complete),
Err(_) if self.writer.is_truncated() => {
Ok(DiagnosticWriteStatus::Truncated)
}
Err(error) => Err(error),
}
}
#[inline]
pub fn push_safe(
&mut self,
text: &LogSafeText<'_>,
) -> DiagnosticWriteStatus {
if self.writer.is_truncated() {
return DiagnosticWriteStatus::Truncated;
}
let _ = self.writer.write_str(text.as_str());
if self.writer.is_truncated() {
DiagnosticWriteStatus::Truncated
} else {
DiagnosticWriteStatus::Complete
}
}
pub fn push_redacted_field(
&mut self,
session: &RedactionSession<'_>,
field: &str,
value: &str,
) -> DiagnosticWriteStatus {
if self.writer.is_truncated() {
return DiagnosticWriteStatus::Truncated;
}
let redactor = Redactor::new(session.policy().clone());
let text = redactor
.redact_field_with_session(session, field, value)
.escape_for_log();
self.push_safe(&text)
}
pub fn push_redacted_at(
&mut self,
session: &RedactionSession<'_>,
level: Sensitivity,
value: &str,
) -> DiagnosticWriteStatus {
if self.writer.is_truncated() {
return DiagnosticWriteStatus::Truncated;
}
let redactor = Redactor::new(session.policy().clone());
let text = redactor
.redact_at_with_session(session, level, value)
.escape_for_log();
self.push_safe(&text)
}
#[must_use]
#[inline]
pub const fn is_truncated(&self) -> bool {
self.writer.is_truncated()
}
#[inline]
pub fn finish(self) -> LogSafeText<'static> {
LogSafeText::from_escaped(Cow::Owned(self.writer.finish()))
}
}