use std::mem::take;
use crate::{Mutation, MutationError, MutationKind, Mutations, Path, PathSegment};
#[cfg(feature = "json")]
mod json;
#[cfg(feature = "yaml")]
mod yaml;
#[cfg(feature = "json")]
pub use json::Json;
#[cfg(feature = "yaml")]
pub use yaml::Yaml;
pub trait Adapter: Sized {
type Value;
type Error;
fn from_mutations(mutation: Mutations) -> Result<Self, Self::Error>;
fn get_mut<'a>(
value: &'a mut Self::Value,
segment: &PathSegment,
allow_create: bool,
) -> Option<&'a mut Self::Value>;
#[cfg(feature = "delete")]
fn delete(value: &mut Self::Value, segment: &PathSegment) -> Option<Self::Value>;
#[cfg(feature = "append")]
fn append(value: &mut Self::Value, append_value: Self::Value) -> Option<usize>;
#[cfg(feature = "append")]
fn len(value: &Self::Value) -> Option<usize>;
#[cfg(feature = "truncate")]
fn truncate(value: &mut Self::Value, truncate_len: usize) -> Option<usize>;
fn mutate(
mut value: &mut Self::Value,
mut mutation: Mutation<Self::Value>,
path_stack: &mut Path<false>,
) -> Result<(), MutationError> {
let is_replace = matches!(mutation.kind, MutationKind::Replace { .. });
#[cfg(feature = "delete")]
let is_delete = matches!(mutation.kind, MutationKind::Delete);
while let Some(segment) = mutation.path.pop() {
let is_last_segment = mutation.path.is_empty();
#[cfg(feature = "delete")]
if is_last_segment && is_delete {
match Self::delete(value, &segment) {
Some(_) => return Ok(()),
None => {
path_stack.push(segment);
return Err(MutationError::IndexError { path: take(path_stack) });
}
}
}
let inner_value = Self::get_mut(value, &segment, is_replace && is_last_segment);
path_stack.push(segment);
let Some(inner_value) = inner_value else {
return Err(MutationError::IndexError { path: take(path_stack) });
};
value = inner_value;
}
#[cfg(feature = "delete")]
if is_delete {
return Err(MutationError::IndexError { path: take(path_stack) });
}
match mutation.kind {
MutationKind::Replace(replace_value) => {
*value = replace_value;
}
#[cfg(feature = "append")]
MutationKind::Append(append_value) => {
if Self::append(value, append_value).is_none() {
return Err(MutationError::OperationError { path: take(path_stack) });
}
}
#[cfg(feature = "truncate")]
MutationKind::Truncate(truncate_len) => {
let Some(remaining) = Self::truncate(value, truncate_len) else {
return Err(MutationError::OperationError { path: take(path_stack) });
};
if remaining > 0 {
return Err(MutationError::TruncateError {
path: take(path_stack),
actual_len: truncate_len - remaining,
truncate_len,
});
}
}
#[cfg(feature = "delete")]
MutationKind::Delete => unreachable!(),
MutationKind::Batch(mutations) => {
let len = path_stack.len();
for mutation in mutations {
Self::mutate(value, mutation, path_stack)?;
path_stack.truncate(len);
}
}
}
Ok(())
}
}