use super::{schema, Memory, PatchArguments};
use crate::{
format::{merge_map_field, CodecError, DecodeLimits, ModelValue, ValueView},
limits::Limits,
};
use core::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PatchError {
Codec(CodecError),
EntryLimit,
InvalidArguments,
PlaneNotMap,
NullInArray,
}
impl From<CodecError> for PatchError {
fn from(error: CodecError) -> Self {
Self::Codec(error)
}
}
impl fmt::Display for PatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Codec(error) => return error.fmt(f),
Self::EntryLimit => "patch exceeds aggregate entry limit",
Self::InvalidArguments => "patch arguments must be the object patch itself",
Self::PlaneNotMap => "plane patch must be an object or null",
Self::NullInArray => "replacement array data contains null",
})
}
}
impl core::error::Error for PatchError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Codec(error) => Some(error),
_ => None,
}
}
}
impl Memory {
pub fn apply_patch(&mut self, bytes: &[u8], limits: &Limits) -> Result<&[u8], PatchError> {
PatchArguments::decode(bytes, limits)?.apply(self)
}
pub(super) fn patched(
&self,
patch: ValueView<'_>,
limits: &Limits,
) -> Result<Self, PatchError> {
let bounds = DecodeLimits {
max_block_bytes: limits.max_block_bytes.min(limits.max_memory_bytes),
max_depth: limits.max_depth,
max_collection_entries: limits.max_collection_entries,
};
crate::format::validate_model_value(self.as_bytes(), &bounds)?;
let bytes = merge_map_field(
ValueView::preflighted(self.as_bytes()),
"value",
patch,
&bounds,
super::plane,
)?;
let block = ModelValue::from_encoded(bytes, &bounds)?;
let value_span = schema(block.as_bytes()).map_err(|error| match error {
super::MemoryError::Codec(error) => error,
_ => CodecError::Malformed,
})?;
Ok(Self { block, value_span })
}
}
#[cfg(test)]
mod tests;