#[cfg(feature = "json")]
use qubit_budget::json::JsonValueBudget;
#[cfg(feature = "json")]
use qubit_json::value::traverse::JsonTreeReader;
#[cfg(feature = "json")]
use serde_json::Value;
use super::StructuralBudget;
use crate::RedactionLimits;
use crate::RedactionUsage;
pub(super) struct RedactionBudget {
output_limit: usize,
structural: StructuralBudget,
usage: RedactionUsage,
active_operation_usage: Option<RedactionUsage>,
#[cfg(feature = "json")]
json_budget: JsonValueBudget,
}
#[cfg(feature = "json")]
pub(super) type JsonAdmissionBudgetParts<'budget> = (
&'budget mut StructuralBudget,
&'budget mut RedactionUsage,
&'budget mut Option<RedactionUsage>,
&'budget mut JsonValueBudget,
);
impl RedactionBudget {
#[must_use]
pub(super) fn new(limits: &RedactionLimits) -> Self {
Self {
output_limit: limits.max_output_bytes(),
structural: StructuralBudget::new(limits.structural_limits()),
usage: RedactionUsage::empty(),
active_operation_usage: None,
#[cfg(feature = "json")]
json_budget: limits.json_limits().budget(),
}
}
#[must_use]
pub(super) const fn output_limit(&self) -> usize {
self.output_limit
}
#[must_use]
pub(super) const fn usage(&self) -> RedactionUsage {
self.usage
}
pub(super) fn begin_operation_usage(&mut self) -> bool {
if self.active_operation_usage.is_some() {
return false;
}
self.active_operation_usage = Some(RedactionUsage::empty());
true
}
#[must_use]
pub(super) const fn active_operation_usage(&self) -> Option<RedactionUsage> {
self.active_operation_usage
}
pub(super) fn end_operation_usage(&mut self, owns_operation: bool) {
if owns_operation {
self.active_operation_usage = None;
}
}
pub(super) fn record_output_bytes(&mut self, bytes: usize) {
self.usage = self.usage.with_added_output_bytes(bytes);
if let Some(usage) = self.active_operation_usage {
self.active_operation_usage = Some(usage.with_added_output_bytes(bytes));
}
}
pub(super) fn record_input(&mut self, presented: usize, inspected: usize) {
self.usage = self.usage.with_input(presented, inspected);
if let Some(usage) = self.active_operation_usage {
self.active_operation_usage = Some(usage.with_input(presented, inspected));
}
}
#[cfg(feature = "http")]
pub(super) fn record_source_input(&mut self, presented: usize, inspected: usize, omitted: Option<usize>) {
self.usage = self.usage.with_source_input(presented, inspected, omitted);
if let Some(usage) = self.active_operation_usage {
self.active_operation_usage = Some(usage.with_source_input(presented, inspected, omitted));
}
}
pub(super) fn record_structural_node(&mut self, depth: usize) {
self.usage = self.usage.with_domain_node(depth);
if let Some(usage) = self.active_operation_usage {
self.active_operation_usage = Some(usage.with_domain_node(depth));
}
}
pub(super) fn record_collection_item(&mut self) {
self.usage = self.usage.with_collection_item();
if let Some(usage) = self.active_operation_usage {
self.active_operation_usage = Some(usage.with_collection_item());
}
}
pub(super) fn structural(&mut self) -> &mut StructuralBudget {
&mut self.structural
}
#[cfg(feature = "json")]
pub(super) fn admit_json_value(&mut self, root: &Value) -> bool {
let mut transaction = self.json_budget.transaction();
let admitted = JsonTreeReader::new(&mut transaction).account(root).is_ok();
let committed = transaction.commit().is_ok();
admitted && committed
}
#[cfg(feature = "http")]
pub(super) fn json_value_budget_mut(&mut self) -> &mut JsonValueBudget {
&mut self.json_budget
}
#[cfg(feature = "json")]
pub(super) fn split_json_admission(&mut self) -> JsonAdmissionBudgetParts<'_> {
(
&mut self.structural,
&mut self.usage,
&mut self.active_operation_usage,
&mut self.json_budget,
)
}
}