use super::super::JsonValueLimits;
use crate::json::JsonMeasurement;
use crate::resource::MeasuredBudgetError;
use crate::resource::ResourceLimit;
use crate::resource::ResourceQuantity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::json) enum PreparedJsonAdmission<Q>
where
Q: ResourceQuantity,
{
Null { depth: Q },
Boolean { depth: Q },
String { depth: Q, bytes: Q },
Number { depth: Q, bytes: Q },
Array { depth: Q, items: Q },
Object { depth: Q, entries: Q },
Key { bytes: Q },
}
impl<Q> PreparedJsonAdmission<Q>
where
Q: ResourceQuantity,
{
pub(in crate::json) fn prepare<R>(
limits: &JsonValueLimits<R, Q>,
measurement: JsonMeasurement,
) -> Result<Self, MeasuredBudgetError<R, Q>>
where
R: Clone,
{
let structure = limits.structure_limits();
match measurement {
JsonMeasurement::Null { depth } => Ok(Self::Null {
depth: convert(depth, structure.depth_limit())?,
}),
JsonMeasurement::Boolean { depth } => Ok(Self::Boolean {
depth: convert(depth, structure.depth_limit())?,
}),
JsonMeasurement::String { depth, bytes } => Ok(Self::String {
depth: convert(depth, structure.depth_limit())?,
bytes: convert_payload(bytes, limits.string_bytes_limit(), limits.payload_bytes_limit())?,
}),
JsonMeasurement::Number { depth, bytes } => Ok(Self::Number {
depth: convert(depth, structure.depth_limit())?,
bytes: convert_payload(bytes, limits.number_bytes_limit(), limits.payload_bytes_limit())?,
}),
JsonMeasurement::Array { depth, items } => Ok(Self::Array {
depth: convert(depth, structure.depth_limit())?,
items: convert(items, structure.sequence_items_limit())?,
}),
JsonMeasurement::Object { depth, entries } => Ok(Self::Object {
depth: convert(depth, structure.depth_limit())?,
entries: convert(entries, structure.map_entries_limit())?,
}),
JsonMeasurement::Key { bytes } => Ok(Self::Key {
bytes: convert_payload(bytes, structure.key_bytes_limit(), limits.payload_bytes_limit())?,
}),
}
}
pub(in crate::json) fn check_point<R>(
&self,
limits: &JsonValueLimits<R, Q>,
) -> Result<(), MeasuredBudgetError<R, Q>>
where
R: Clone,
{
let structure = limits.structure_limits();
match self {
Self::Null { depth } | Self::Boolean { depth } => check_limit(structure.depth_limit(), *depth),
Self::String { depth, bytes } => {
check_limit(structure.depth_limit(), *depth)?;
check_limit(limits.string_bytes_limit(), *bytes)
}
Self::Number { depth, bytes } => {
check_limit(structure.depth_limit(), *depth)?;
check_limit(limits.number_bytes_limit(), *bytes)
}
Self::Array { depth, items } => {
check_limit(structure.depth_limit(), *depth)?;
check_limit(structure.sequence_items_limit(), *items)
}
Self::Object { depth, entries } => {
check_limit(structure.depth_limit(), *depth)?;
check_limit(structure.map_entries_limit(), *entries)
}
Self::Key { bytes } => check_limit(structure.key_bytes_limit(), *bytes),
}
}
}
fn convert<R, Q>(amount: usize, limit: Option<&ResourceLimit<R, Q>>) -> Result<Q, MeasuredBudgetError<R, Q>>
where
R: Clone,
Q: ResourceQuantity,
{
let Some(limit) = limit else {
return Ok(Q::ZERO);
};
Q::try_from_usize(amount).map_err(|source| MeasuredBudgetError::quantity(limit.resource().clone(), source))
}
fn convert_payload<R, Q>(
amount: usize,
point_limit: Option<&ResourceLimit<R, Q>>,
payload_limit: Option<&ResourceLimit<R, Q>>,
) -> Result<Q, MeasuredBudgetError<R, Q>>
where
R: Clone,
Q: ResourceQuantity,
{
if let Some(limit) = point_limit {
return Q::try_from_usize(amount)
.map_err(|source| MeasuredBudgetError::quantity(limit.resource().clone(), source));
}
convert(amount, payload_limit)
}
fn check_limit<R, Q>(limit: Option<&ResourceLimit<R, Q>>, actual: Q) -> Result<(), MeasuredBudgetError<R, Q>>
where
R: Clone,
Q: ResourceQuantity,
{
match limit {
Some(limit) => limit.check(actual).map_err(MeasuredBudgetError::from),
None => Ok(()),
}
}