use std::fmt::Debug;
use erased_serde::Serialize;
use crate::{Path, PathSegment};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MutationKind<T> {
Replace(T),
#[cfg(feature = "append")]
Append(T),
#[cfg(feature = "truncate")]
Truncate(usize),
#[cfg(feature = "delete")]
Delete,
Batch(Vec<Mutation<T>>),
}
impl<T> MutationKind<T> {
#[cfg(any(feature = "json", feature = "yaml"))]
pub(crate) fn try_map<U, E>(self, f: &mut impl FnMut(T) -> Result<U, E>) -> Result<MutationKind<U>, E> {
Ok(match self {
MutationKind::Replace(value) => MutationKind::Replace(f(value)?),
#[cfg(feature = "append")]
MutationKind::Append(value) => MutationKind::Append(f(value)?),
#[cfg(feature = "truncate")]
MutationKind::Truncate(len) => MutationKind::Truncate(len),
#[cfg(feature = "delete")]
MutationKind::Delete => MutationKind::Delete,
MutationKind::Batch(batch) => {
MutationKind::Batch(batch.into_iter().map(|m| m.try_map(f)).collect::<Result<_, E>>()?)
}
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mutation<V> {
pub path: Path<true>,
pub kind: MutationKind<V>,
}
impl<V> Mutation<V> {
#[cfg(any(feature = "json", feature = "yaml"))]
pub(crate) fn try_map<U, E>(self, f: &mut impl FnMut(V) -> Result<U, E>) -> Result<Mutation<U>, E> {
Ok(Mutation {
path: self.path,
kind: self.kind.try_map(f)?,
})
}
fn make_batch(&mut self, capacity: usize) -> &mut Vec<Self> {
if self.path.is_empty()
&& let MutationKind::Batch(ref mut batch) = self.kind
{
return batch;
}
let old = std::mem::replace(
self,
Mutation {
path: vec![].into(),
kind: MutationKind::Batch(Vec::with_capacity(capacity)),
},
);
let MutationKind::Batch(batch) = &mut self.kind else {
unreachable!()
};
batch.push(old);
batch
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mutations<V = Box<dyn Serialize>> {
inner: Option<Mutation<V>>,
capacity: usize,
is_replace: bool,
}
impl<V> Default for Mutations<V> {
fn default() -> Self {
Self::new()
}
}
impl<V> From<MutationKind<V>> for Mutations<V> {
fn from(kind: MutationKind<V>) -> Self {
Self {
is_replace: matches!(kind, MutationKind::Replace(_)),
inner: Some(Mutation {
path: Default::default(),
kind,
}),
capacity: 2,
}
}
}
impl<V> From<Mutations<V>> for Option<Mutation<V>> {
fn from(value: Mutations<V>) -> Self {
value.into_inner()
}
}
impl<V> Mutations<V> {
pub fn new() -> Self {
Self {
is_replace: false,
inner: None,
capacity: 2,
}
}
pub fn with_capacity(mut self, capacity: usize) -> Self {
self.capacity = capacity;
self
}
pub fn with_replace(mut self, is_replace: bool) -> Self {
self.is_replace = is_replace;
self
}
pub fn with_prefix(mut self, segment: impl Into<PathSegment>) -> Self {
if let Some(mutation) = &mut self.inner {
mutation.path.push(segment.into());
}
self
}
pub fn into_inner(self) -> Option<Mutation<V>> {
self.inner
}
pub fn is_replace(&self) -> bool {
self.is_replace
}
pub fn extend(&mut self, mutations: impl Into<Self>) {
let Some(incoming) = mutations.into().into_inner() else {
return;
};
let Some(existing) = &mut self.inner else {
self.inner = Some(incoming);
return;
};
let existing_batch: &mut Vec<Mutation<V>> = existing.make_batch(self.capacity);
if incoming.path.is_empty()
&& let MutationKind::Batch(incoming_batch) = incoming.kind
{
existing_batch.extend(incoming_batch);
} else {
existing_batch.push(incoming);
}
}
pub fn insert(&mut self, segment: impl Into<PathSegment>, mutations: impl Into<Self>) {
self.extend(mutations.into().with_prefix(segment))
}
pub fn len(&self) -> usize {
match &self.inner {
None => 0,
Some(mutation) => match &mutation.kind {
MutationKind::Batch(batch) if mutation.path.is_empty() => batch.len(),
_ => 1,
},
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[cfg(feature = "truncate")]
pub fn truncate(len: usize) -> Self {
MutationKind::Truncate(len).into()
}
#[cfg(feature = "delete")]
pub fn delete() -> Self {
MutationKind::Delete.into()
}
#[cfg(feature = "delete")]
pub fn into_delete(mut self) -> Self {
if let Some(mutation) = &mut self.inner {
match &mut mutation.kind {
MutationKind::Batch(batch) => {
for mutation in batch {
mutation.kind = MutationKind::Delete;
}
}
_ => mutation.kind = MutationKind::Delete,
}
}
self
}
}
pub(crate) struct SerializeRef<T: ?Sized>(pub *const T);
impl<T> serde::Serialize for SerializeRef<T>
where
T: serde::Serialize + ?Sized,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
unsafe { &*self.0 }.serialize(serializer)
}
}
impl Mutations {
pub fn replace_owned<T: serde::Serialize + 'static>(value: T) -> Self {
MutationKind::Replace(Box::new(value) as Box<dyn Serialize>).into()
}
pub fn replace<T: serde::Serialize + ?Sized + 'static>(value: &T) -> Self {
Self::replace_owned(SerializeRef(value))
}
#[cfg(feature = "append")]
pub fn append_owned<T: serde::Serialize + 'static>(value: T) -> Self {
MutationKind::Append(Box::new(value) as Box<dyn Serialize>).into()
}
#[cfg(feature = "append")]
pub fn append<T: serde::Serialize + ?Sized + 'static>(value: &T) -> Self {
Self::append_owned(SerializeRef(value))
}
}