use super::StructureLimits;
use super::StructureResource;
use crate::resource::BudgetError;
use crate::resource::ResourceBudget;
use crate::resource::ResourceQuantity;
use crate::resource::check_limit;
#[derive(Debug, PartialEq, Eq)]
pub struct StructureBudget<R = StructureResource, Q = usize>
where
Q: ResourceQuantity,
{
limits: StructureLimits<R, Q>,
nodes: Option<ResourceBudget<R, Q>>,
}
impl<R, Q> StructureBudget<R, Q>
where
R: Clone,
Q: ResourceQuantity,
{
#[inline]
#[must_use = "the budget check result must be handled"]
pub(crate) fn new(limits: StructureLimits<R, Q>) -> Self {
Self {
nodes: limits.nodes_limit().cloned().map(ResourceBudget::from_limit),
limits,
}
}
#[inline]
#[must_use = "the budget check result must be handled"]
pub fn check_depth(&self, actual: Q) -> Result<(), BudgetError<R, Q>> {
check_limit(self.limits.depth_limit(), actual)
}
#[inline]
pub fn charge_node(&mut self) -> Result<(), BudgetError<R, Q>> {
self.charge_nodes(Q::ONE)
}
#[inline]
pub fn charge_nodes(&mut self, amount: Q) -> Result<(), BudgetError<R, Q>> {
match &mut self.nodes {
Some(nodes) => nodes.try_consume(amount).map_err(BudgetError::from),
None => Ok(()),
}
}
#[inline]
#[must_use = "the budget check result must be handled"]
pub fn check_sequence_items(&self, actual: Q) -> Result<(), BudgetError<R, Q>> {
check_limit(self.limits.sequence_items_limit(), actual)
}
#[inline]
#[must_use = "the budget check result must be handled"]
pub fn check_map_entries(&self, actual: Q) -> Result<(), BudgetError<R, Q>> {
check_limit(self.limits.map_entries_limit(), actual)
}
#[inline]
#[must_use = "the budget check result must be handled"]
pub fn check_key_bytes(&self, actual: Q) -> Result<(), BudgetError<R, Q>> {
check_limit(self.limits.key_bytes_limit(), actual)
}
#[inline]
pub fn enter_node(&mut self, depth: Q) -> Result<(), BudgetError<R, Q>> {
self.check_depth(depth)?;
self.charge_node()
}
#[inline]
pub fn enter_sequence(&mut self, depth: Q, items: Q) -> Result<(), BudgetError<R, Q>> {
self.check_depth(depth)?;
self.check_sequence_items(items)?;
self.charge_node()
}
#[inline]
pub fn enter_map(&mut self, depth: Q, entries: Q) -> Result<(), BudgetError<R, Q>> {
self.check_depth(depth)?;
self.check_map_entries(entries)?;
self.charge_node()
}
#[must_use]
#[inline(always)]
pub const fn limits(&self) -> &StructureLimits<R, Q> {
&self.limits
}
#[must_use]
#[inline(always)]
pub const fn has_nodes_limit(&self) -> bool {
self.nodes.is_some()
}
#[must_use]
#[inline(always)]
pub const fn remaining_nodes(&self) -> Option<Q> {
match &self.nodes {
Some(nodes) => Some(nodes.remaining()),
None => None,
}
}
#[must_use]
#[inline(always)]
pub fn used_nodes(&self) -> Option<Q> {
self.nodes.as_ref().map(ResourceBudget::used)
}
}