#[cfg(feature = "json")]
use qubit_budget::json::JsonValueBudget;
#[cfg(feature = "json")]
use super::rendered_operation::RenderedOperation;
#[cfg(feature = "json")]
use super::rendered_summary::rendered_summary;
use super::runtime_core::RuntimeCore;
use super::transaction_phase::TransactionPhase;
use crate::RedactionPolicy;
#[cfg(any(feature = "json", feature = "http", feature = "uri"))]
use crate::RedactionReason;
use crate::RedactionSummary;
use crate::Sensitivity;
use crate::policy::ResolvedField;
pub(crate) trait RuntimeSession {
fn runtime(&self) -> &RuntimeCore;
fn runtime_mut(&mut self) -> &mut RuntimeCore;
fn is_inspection(&self) -> bool;
fn observe_sensitivity(&mut self, sensitivity: Sensitivity);
#[must_use]
#[inline(always)]
fn policy(&self) -> &RedactionPolicy {
self.runtime().policy()
}
#[must_use]
fn begin_item_summary(&mut self) -> bool {
self.runtime_mut().begin_item_summary()
}
fn end_item_summary(&mut self, owns_item_summary: bool) {
self.runtime_mut().end_item_summary(owns_item_summary);
}
fn record_summary(&mut self, delta: RedactionSummary) {
self.runtime_mut().record_summary(delta);
}
fn record_output_bytes(&mut self, bytes: usize) {
self.runtime_mut().record_output_bytes(bytes);
}
#[must_use]
fn begin_domain_value(&mut self) -> bool {
self.runtime_mut().begin_domain_value()
}
#[must_use]
#[inline(always)]
fn admit_domain_field(&mut self) -> bool {
self.runtime_mut().admit_domain_field()
}
#[must_use]
#[inline(always)]
fn admit_domain_collection_item(&mut self) -> bool {
self.runtime_mut().admit_domain_collection_item()
}
#[must_use]
fn admit_format_node(&mut self, depth: usize) -> bool {
self.runtime_mut().admit_format_node(depth)
}
#[must_use]
#[inline(always)]
fn admit_format_collection_item(&mut self) -> bool {
self.admit_domain_collection_item()
}
#[must_use]
#[inline(always)]
fn preflight_format_item(&mut self, depth: usize) -> bool {
self.runtime_mut().preflight_format_item(depth)
}
#[must_use]
#[inline(always)]
fn preflight_collection_item(&mut self) -> bool {
self.runtime_mut().preflight_collection_item()
}
#[cfg(feature = "json")]
#[must_use]
fn admit_json_value(&mut self, value: &serde_json::Value) -> bool {
self.runtime_mut().admit_json_value(value)
}
#[cfg(feature = "json")]
fn split_json_admission(&mut self) -> (super::JsonStructureAdmission<'_>, &mut JsonValueBudget) {
self.runtime_mut().split_json_admission()
}
#[cfg(feature = "json")]
fn record_json_value_limit_reached(&mut self) {
self.runtime_mut().record_json_value_limit_reached();
}
#[inline(always)]
fn leave_domain_value(&mut self) {
self.runtime_mut().leave_domain_value();
}
#[must_use]
#[inline(always)]
fn domain_frame_is_truncated(&self) -> bool {
self.runtime().domain_frame_truncated
}
#[cfg(any(feature = "json", feature = "http", feature = "uri"))]
fn fail_inspection(&mut self, reason: RedactionReason) {
debug_assert!(self.is_inspection());
self.record_summary(RedactionSummary::truncated(reason));
}
fn inspect_field(&mut self, field: &str, value: &str) {
debug_assert!(self.is_inspection());
if !self.admit_input(field.len().saturating_add(value.len())) {
return;
}
if let ResolvedField::Sensitive { sensitivity } = self.policy().resolve_field(field) {
self.observe_sensitivity(sensitivity);
}
}
#[must_use]
#[inline(always)]
fn remaining_domain_frame_output_bytes(&self) -> usize {
if self.is_inspection() {
return usize::MAX;
}
self.remaining_output_bytes()
.saturating_sub(self.runtime().domain_frame_output_bytes)
}
fn append_domain_frame_fragment(&mut self, text: &str) {
if self.is_inspection() {
return;
}
for character in text.chars() {
self.runtime_mut().domain_frame.push(character);
self.runtime_mut().domain_frame_output_bytes += encoded_log_safe_len(character);
}
}
fn write_domain_fragment(&mut self, text: &str) -> bool {
if self.runtime().domain_frame_truncated {
return false;
}
if self.is_inspection() {
return true;
}
for character in text.chars() {
if encoded_log_safe_len(character) > self.remaining_domain_frame_output_bytes() {
self.mark_domain_frame_output_limit_reached();
self.truncate_domain_frame_without_output_limit();
return false;
}
self.runtime_mut().domain_frame.push(character);
self.runtime_mut().domain_frame_output_bytes += encoded_log_safe_len(character);
}
true
}
fn mark_domain_frame_output_limit_reached(&mut self) {
self.runtime_mut().domain_frame_output_limit_reached = true;
}
fn mark_domain_frame_truncated(&mut self) {
self.runtime_mut().domain_frame_truncated = true;
}
fn truncate_domain_frame_to(&mut self, limit: usize) {
while self.runtime().domain_frame_output_bytes > limit {
let Some(character) = self.runtime_mut().domain_frame.pop() else {
self.runtime_mut().domain_frame_output_bytes = 0;
return;
};
self.runtime_mut().domain_frame_output_bytes = self
.runtime()
.domain_frame_output_bytes
.saturating_sub(encoded_log_safe_len(character));
}
}
fn truncate_domain_frame_without_output_limit(&mut self) {
if self.runtime().domain_frame_truncated {
return;
}
if self.is_inspection() {
self.mark_domain_frame_truncated();
return;
}
const MARKER: &str = "<truncated>";
if MARKER.len() > self.remaining_output_bytes() {
self.truncate_domain_frame_to(0);
self.mark_domain_frame_output_limit_reached();
self.runtime_mut().phase = TransactionPhase::OutputExhausted;
} else {
let limit = self.remaining_output_bytes().saturating_sub(MARKER.len());
self.truncate_domain_frame_to(limit);
self.append_domain_frame_fragment(MARKER);
}
self.mark_domain_frame_truncated();
}
fn trim_domain_frame_separator(&mut self) {
if self.is_inspection() {
return;
}
if self.runtime().domain_frame.ends_with(", ") {
let length = self.runtime().domain_frame.len();
self.runtime_mut().domain_frame.truncate(length - 2);
self.runtime_mut().domain_frame_output_bytes = self.runtime().domain_frame_output_bytes.saturating_sub(2);
}
}
#[must_use]
fn finish_domain_frame(&mut self) -> (String, bool, bool) {
let output = std::mem::take(&mut self.runtime_mut().domain_frame);
let truncated = std::mem::take(&mut self.runtime_mut().domain_frame_truncated);
let output_limit_reached = std::mem::take(&mut self.runtime_mut().domain_frame_output_limit_reached);
self.runtime_mut().domain_frame_output_bytes = 0;
(output, truncated, output_limit_reached)
}
#[cfg(feature = "json")]
fn record_rendered_provenance(&mut self, operation: &RenderedOperation) {
let summary = rendered_summary(operation.completion(), operation.reasons());
self.record_summary(summary);
}
#[must_use]
#[inline(always)]
fn is_output_exhausted(&self) -> bool {
!self.is_inspection() && self.runtime().is_output_exhausted()
}
#[must_use]
#[inline(always)]
fn skip_aggregate_for_exhausted_output(&mut self) -> bool {
!self.is_inspection() && self.runtime_mut().skip_aggregate_for_exhausted_output()
}
#[must_use]
#[inline(always)]
fn remaining_output_bytes(&self) -> usize {
if self.is_inspection() {
usize::MAX
} else {
self.runtime().remaining_output_bytes()
}
}
fn admit_input(&mut self, bytes: usize) -> bool {
self.runtime_mut().admit_input(bytes)
}
#[cfg(any(feature = "json", feature = "http", feature = "uri"))]
#[must_use]
fn admit_input_prefix<'text>(&mut self, text: &'text str) -> &'text str {
self.runtime_mut().admit_input_prefix(text)
}
#[cfg(feature = "http")]
fn admit_source_input(&mut self, total: Option<usize>, inspectable: usize) -> bool {
self.runtime_mut().admit_source_input(total, inspectable)
}
}
#[must_use]
fn encoded_log_safe_len(character: char) -> usize {
let mut buffer = [0_u8; 12];
crate::output::log_escape::encode_log_safe_character(character, &mut buffer)
.map_or(character.len_utf8(), |encoded| encoded.len())
}