use qubit_budget::MeasuredBudgetError;
use qubit_budget::ResourceQuantity;
use qubit_budget::json::JsonMeasurement;
use qubit_budget::json::JsonValueTransaction;
use serde_json::Value;
use super::JsonTreeContext;
use super::JsonTreeLocation;
use super::JsonTreeProcessError;
use super::JsonTreeVisitor;
use super::internal::ChildCursor;
use super::internal::NoopVisitor;
use super::internal::ReadFrame;
use super::internal::ReadFrameState;
use crate::value::internal::json_value_measurement;
pub struct JsonTreeReader<'transaction, 'budget, R, Q>
where
Q: ResourceQuantity,
{
transaction: &'transaction mut JsonValueTransaction<'budget, R, Q>,
enforce_limits: bool,
}
impl<'transaction, 'budget, R, Q> JsonTreeReader<'transaction, 'budget, R, Q>
where
R: Clone,
Q: ResourceQuantity,
{
#[inline(always)]
#[must_use]
pub fn new(transaction: &'transaction mut JsonValueTransaction<'budget, R, Q>) -> Self {
let enforce_limits = transaction.has_limits();
Self {
transaction,
enforce_limits,
}
}
pub fn process<V>(&mut self, value: &Value, visitor: &mut V) -> Result<(), JsonTreeProcessError<R, Q, V::Error>>
where
V: JsonTreeVisitor,
{
let mut pending = vec![ReadFrame::enter(
value,
JsonTreeContext {
depth: 1,
location: JsonTreeLocation::Root,
},
)];
while let Some(frame) = pending.last_mut() {
match &mut frame.state {
ReadFrameState::Enter => {
let value = frame.value;
let context = frame.context;
if self.enforce_limits {
if let JsonTreeLocation::ObjectValue { key } = context.location {
self.transaction.try_admit(JsonMeasurement::Key { bytes: key.len() })?;
}
self.admit(value, context.depth)?;
}
visitor.enter(value, context).map_err(JsonTreeProcessError::Visitor)?;
frame.state = ReadFrameState::Children(ChildCursor::new(value, context.depth));
}
ReadFrameState::Children(cursor) => {
if let Some((value, location, depth)) = cursor.next() {
pending.push(ReadFrame::enter(value, JsonTreeContext { depth, location }));
} else {
frame.state = ReadFrameState::Leave;
}
}
ReadFrameState::Leave => {
let frame = pending.pop().expect("read frame exists");
visitor
.leave(frame.value, frame.context)
.map_err(JsonTreeProcessError::Visitor)?;
}
}
}
Ok(())
}
pub fn account(&mut self, value: &Value) -> Result<(), MeasuredBudgetError<R, Q>> {
if !self.enforce_limits {
return Ok(());
}
match self.process(value, &mut NoopVisitor) {
Ok(()) => Ok(()),
Err(JsonTreeProcessError::Budget(error)) => Err(error),
Err(JsonTreeProcessError::Visitor(error)) => match error {},
}
}
fn admit(&mut self, value: &Value, depth: usize) -> Result<(), MeasuredBudgetError<R, Q>> {
self.transaction.try_admit(json_value_measurement(value, depth))
}
}