use crate::{
apply::ApplyContext, counting_serializer::CountingSerializer, Config, ElementStackEntry,
FieldPathMode, SerdeDiff,
};
use serde::{de, ser::SerializeSeq, Deserialize, Serialize, Serializer};
use std::{borrow::Cow, cell::Cell};
#[doc(hidden)]
pub struct DiffContext<'a, S: SerializeSeq> {
element_stack: Option<Vec<ElementStackEntry<'a, S>>>,
serializer: &'a mut S,
implicit_exit_written: bool,
parent_element_stack: Option<&'a mut Option<Vec<ElementStackEntry<'a, S>>>>,
element_stack_start: usize,
field_path_mode: FieldPathMode,
has_changes: bool,
}
impl<'a, S: SerializeSeq> Drop for DiffContext<'a, S> {
fn drop(&mut self) {
if let Some(parent) = self.parent_element_stack.take() {
if let Some(mut stack) = self.element_stack.take() {
if self.element_stack_start < stack.len() {
stack.drain(self.element_stack_start..);
}
parent.replace(stack);
}
}
}
}
#[doc(hidden)]
impl<'a, S: SerializeSeq> DiffContext<'a, S> {
pub fn field_path_mode(&self) -> FieldPathMode {
self.field_path_mode
}
pub fn has_changes(&self) -> bool {
self.has_changes
}
pub fn push_field(&mut self, field_name: &'static str) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(DiffPathElementValue::Field(
Cow::Borrowed(field_name),
)));
}
pub fn push_variant(&mut self, variant_name: &'static str) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(
DiffPathElementValue::EnumVariant(Cow::Borrowed(variant_name)),
));
}
pub fn push_full_variant(&mut self) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(
DiffPathElementValue::FullEnumVariant,
));
}
pub fn push_field_index(&mut self, field_idx: u16) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(
DiffPathElementValue::FieldIndex(field_idx),
));
}
pub fn push_collection_index(&mut self, idx: usize) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(
DiffPathElementValue::CollectionIndex(idx),
));
}
pub fn push_collection_add(&mut self) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::PathElement(
DiffPathElementValue::AddToCollection,
));
}
pub fn push_field_element(&mut self, f: &'a dyn Fn(&mut S) -> Result<(), S::Error>) {
self.element_stack
.as_mut()
.unwrap()
.push(ElementStackEntry::Closure(f));
}
pub fn pop_path_element(&mut self) -> Result<(), S::Error> {
let element_stack = self.element_stack.as_mut().unwrap();
if element_stack.is_empty() {
if !self.implicit_exit_written {
let cmd = DiffCommandRef::<()>::Exit;
self.serializer.serialize_element(&cmd)
} else {
self.implicit_exit_written = false;
Ok(())
}
} else {
element_stack.pop();
self.element_stack_start = std::cmp::min(element_stack.len(), self.element_stack_start);
Ok(())
}
}
pub fn save_value<T: Serialize>(&mut self, value: &T) -> Result<(), S::Error> {
self.save_command(&DiffCommandRef::Value(value), true, true)
}
pub fn save_command<'b, T: Serialize>(
&mut self,
value: &DiffCommandRef<'b, T>,
implicit_exit: bool,
is_change: bool,
) -> Result<(), S::Error> {
let element_stack = self.element_stack.as_mut().unwrap();
if !element_stack.is_empty() {
for element in element_stack.drain(0..element_stack.len()) {
match element {
ElementStackEntry::PathElement(element) => self
.serializer
.serialize_element(&DiffCommandRef::<()>::Enter(element))?,
ElementStackEntry::Closure(closure) => (closure)(&mut self.serializer)?,
};
}
self.element_stack_start = 0;
}
self.has_changes |= is_change;
self.implicit_exit_written = implicit_exit;
self.serializer.serialize_element(value)
}
pub fn reborrow<'c, 'd: 'c>(&'d mut self) -> DiffContext<'c, S>
where
'a: 'c,
'a: 'd,
{
let element_stack = self.element_stack.take();
let element_stack_ref = unsafe {
std::mem::transmute::<
&'d mut Option<Vec<ElementStackEntry<'a, S>>>,
&'c mut Option<Vec<ElementStackEntry<'c, S>>>,
>(&mut self.element_stack)
};
DiffContext {
element_stack_start: element_stack.as_ref().unwrap().len(),
element_stack,
parent_element_stack: Some(element_stack_ref),
serializer: &mut *self.serializer,
implicit_exit_written: self.implicit_exit_written,
field_path_mode: self.field_path_mode,
has_changes: false,
}
}
}
pub struct Diff<'a, 'b, T> {
pub(crate) old: &'a T,
pub(crate) new: &'b T,
pub(crate) field_path_mode: FieldPathMode,
pub(crate) has_changes: Cell<bool>,
}
impl<'a, 'b, T: SerdeDiff + 'a + 'b> Diff<'a, 'b, T> {
pub fn serializable(old: &'a T, new: &'b T) -> Self {
Config::default().serializable_diff(old, new)
}
pub fn diff<S: Serializer>(serializer: S, old: &'a T, new: &'b T) -> Result<S::Ok, S::Error> {
Config::default().diff(serializer, old, new)
}
pub fn has_changes(&self) -> bool {
self.has_changes.get()
}
}
impl<'a, 'b, T: SerdeDiff> Serialize for Diff<'a, 'b, T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.has_changes.set(false);
let num_elements = if !serializer.is_human_readable() {
let mut serializer = CountingSerializer { num_elements: 0 };
let mut seq = serializer.serialize_seq(None).unwrap();
{
let mut ctx = DiffContext {
element_stack_start: 0,
element_stack: Some(Vec::new()),
serializer: &mut seq,
implicit_exit_written: false,
parent_element_stack: None,
field_path_mode: self.field_path_mode,
has_changes: false,
};
self.old.diff(&mut ctx, &self.new).unwrap();
}
seq.end().unwrap();
Some(serializer.num_elements)
} else {
None
};
let mut seq = serializer.serialize_seq(num_elements)?;
{
let mut ctx = DiffContext {
element_stack_start: 0,
element_stack: Some(Vec::new()),
serializer: &mut seq,
implicit_exit_written: false,
parent_element_stack: None,
field_path_mode: self.field_path_mode,
has_changes: false,
};
self.old.diff(&mut ctx, &self.new)?;
self.has_changes.set(ctx.has_changes);
}
Ok(seq.end()?)
}
}
pub(crate) struct DeserWrapper<'a, T> {
pub(crate) val: &'a mut T,
}
pub(crate) struct DiffCommandDeserWrapper<'a, T> {
pub(crate) val_wrapper: DeserWrapper<'a, T>,
}
pub(crate) enum DiffCommandField {
Enter,
Value,
Remove,
AddKey,
EnterKey,
RemoveKey,
Exit,
}
pub(crate) struct DiffCommandFieldVisitor;
const VARIANTS: &'static [&'static str] = &[
"Enter",
"Value",
"Remove",
"AddKey",
"EnterKey",
"RemoveKey",
"Exit",
];
impl<'de> de::Visitor<'de> for DiffCommandFieldVisitor {
type Value = DiffCommandField;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Formatter::write_str(formatter, "variant identifier")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
match value {
0u64 => Ok(DiffCommandField::Enter),
1u64 => Ok(DiffCommandField::Value),
2u64 => Ok(DiffCommandField::Remove),
3u64 => Ok(DiffCommandField::AddKey),
4u64 => Ok(DiffCommandField::EnterKey),
5u64 => Ok(DiffCommandField::RemoveKey),
6u64 => Ok(DiffCommandField::Exit),
_ => Err(de::Error::invalid_value(
de::Unexpected::Unsigned(value),
&"variant index 0 <= i < 7",
)),
}
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match value {
"Enter" => Ok(DiffCommandField::Enter),
"Value" => Ok(DiffCommandField::Value),
"Remove" => Ok(DiffCommandField::Remove),
"AddKey" => Ok(DiffCommandField::AddKey),
"EnterKey" => Ok(DiffCommandField::EnterKey),
"RemoveKey" => Ok(DiffCommandField::RemoveKey),
"Exit" => Ok(DiffCommandField::Exit),
_ => Err(de::Error::unknown_variant(value, VARIANTS)),
}
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match value {
b"Enter" => Ok(DiffCommandField::Enter),
b"Value" => Ok(DiffCommandField::Value),
b"Remove" => Ok(DiffCommandField::Remove),
b"AddKey" => Ok(DiffCommandField::AddKey),
b"EnterKey" => Ok(DiffCommandField::EnterKey),
b"RemoveKey" => Ok(DiffCommandField::RemoveKey),
b"Exit" => Ok(DiffCommandField::Exit),
_ => {
let value = &String::from_utf8_lossy(value);
Err(de::Error::unknown_variant(value, VARIANTS))
}
}
}
}
impl<'de> Deserialize<'de> for DiffCommandField {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
de::Deserializer::deserialize_identifier(deserializer, DiffCommandFieldVisitor)
}
}
impl<'a, 'de, T> de::DeserializeSeed<'de> for DiffCommandDeserWrapper<'a, T>
where
T: de::Deserialize<'de>,
{
type Value = DiffCommandValue<'de, T>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor<'de, 'a, T>
where
T: de::Deserialize<'de>,
{
seed: DeserWrapper<'a, T>,
lifetime: std::marker::PhantomData<&'de ()>,
}
impl<'de, 'a, T> de::Visitor<'de> for Visitor<'de, 'a, T>
where
T: de::Deserialize<'de>,
{
type Value = DiffCommandValue<'de, T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Formatter::write_str(formatter, "enum DiffCommandValueTest")
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: de::EnumAccess<'de>,
{
match de::EnumAccess::variant(data)? {
(DiffCommandField::Enter, variant) => {
let enter =
de::VariantAccess::newtype_variant::<DiffPathElementValue>(variant)?;
Ok(DiffCommandValue::Enter(enter))
}
(DiffCommandField::Value, variant)
| (DiffCommandField::AddKey, variant)
| (DiffCommandField::EnterKey, variant)
| (DiffCommandField::RemoveKey, variant) => {
de::VariantAccess::newtype_variant_seed::<DeserWrapper<T>>(
variant, self.seed,
)?;
Ok(DiffCommandValue::DeserializedValue)
}
(DiffCommandField::Remove, variant) => {
let num_elements = de::VariantAccess::newtype_variant::<usize>(variant)?;
Ok(DiffCommandValue::Remove(num_elements))
}
(DiffCommandField::Exit, variant) => {
de::VariantAccess::unit_variant(variant)?;
Ok(DiffCommandValue::Exit)
}
}
}
}
de::Deserializer::deserialize_enum(
deserializer,
"DiffCommandValueTest",
VARIANTS,
Visitor {
seed: self.val_wrapper,
lifetime: std::marker::PhantomData,
},
)
}
}
impl<'a, 'de, T: Deserialize<'de>> de::DeserializeSeed<'de> for DeserWrapper<'a, T> {
type Value = Self;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: de::Deserializer<'de>,
{
Deserialize::deserialize_in_place(deserializer, self.val)?;
Ok(self)
}
}
pub(crate) struct DiffCommandIgnoreValue;
impl<'de> de::DeserializeSeed<'de> for DiffCommandIgnoreValue {
type Value = DiffCommandValue<'de, ()>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor<'de> {
lifetime: std::marker::PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = DiffCommandValue<'de, ()>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Formatter::write_str(formatter, "enum DiffCommandValueTest")
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: de::EnumAccess<'de>,
{
match de::EnumAccess::variant(data)? {
(DiffCommandField::Enter, variant) => {
let enter =
de::VariantAccess::newtype_variant::<DiffPathElementValue>(variant)?;
Ok(DiffCommandValue::Enter(enter))
}
(DiffCommandField::Value, variant)
| (DiffCommandField::AddKey, variant)
| (DiffCommandField::EnterKey, variant)
| (DiffCommandField::RemoveKey, variant) => {
de::VariantAccess::newtype_variant::<de::IgnoredAny>(variant)?;
Ok(DiffCommandValue::Value(()))
}
(DiffCommandField::Remove, variant) => {
let num_elements = de::VariantAccess::newtype_variant::<usize>(variant)?;
Ok(DiffCommandValue::Remove(num_elements))
}
(DiffCommandField::Exit, variant) => {
de::VariantAccess::unit_variant(variant)?;
Ok(DiffCommandValue::Exit)
}
}
}
}
de::Deserializer::deserialize_enum(
deserializer,
"DiffCommandValueTest",
VARIANTS,
Visitor {
lifetime: std::marker::PhantomData,
},
)
}
}
#[doc(hidden)]
#[derive(Serialize, Debug)]
pub enum DiffCommandRef<'a, T: Serialize> {
Enter(DiffPathElementValue<'a>),
Value(&'a T),
Remove(usize),
AddKey(&'a T),
EnterKey(&'a T),
RemoveKey(&'a T),
Exit,
}
#[doc(hidden)]
#[derive(Deserialize, Debug)]
pub enum DiffCommandValue<'a, T> {
#[serde(borrow)]
Enter(DiffPathElementValue<'a>),
Value(T),
Remove(usize),
AddKey(T),
EnterKey(T),
RemoveKey(T),
Exit,
Nothing,
DeserializedValue,
}
#[doc(hidden)]
#[derive(Serialize, Deserialize, Debug)]
pub enum DiffPathElementValue<'a> {
#[serde(borrow)]
Field(Cow<'a, str>),
FieldIndex(u16),
EnumVariant(Cow<'a, str>),
FullEnumVariant,
CollectionIndex(usize),
AddToCollection,
}
impl<T: SerdeDiff + Serialize + for<'a> Deserialize<'a>> SerdeDiff for Vec<T> {
fn diff<'a, S: SerializeSeq>(
&self,
ctx: &mut DiffContext<'a, S>,
other: &Self,
) -> Result<bool, S::Error> {
let mut self_iter = self.iter();
let mut other_iter = other.iter();
let mut idx = 0;
let mut need_exit = false;
let mut changed = false;
loop {
let self_item = self_iter.next();
let other_item = other_iter.next();
match (self_item, other_item) {
(None, None) => break,
(Some(_), None) => {
let mut num_to_remove = 1;
while self_iter.next().is_some() {
num_to_remove += 1;
}
ctx.save_command::<()>(&DiffCommandRef::Remove(num_to_remove), true, true)?;
changed = true;
need_exit = false;
}
(None, Some(other_item)) => {
ctx.save_command::<()>(
&DiffCommandRef::Enter(DiffPathElementValue::AddToCollection),
false,
true,
)?;
ctx.save_command(&DiffCommandRef::Value(other_item), true, true)?;
need_exit = true;
changed = true;
}
(Some(self_item), Some(other_item)) => {
ctx.push_collection_index(idx);
if <T as SerdeDiff>::diff(self_item, ctx, other_item)? {
need_exit = true;
changed = true;
}
ctx.pop_path_element()?;
}
}
idx += 1;
}
if need_exit {
ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?;
}
Ok(changed)
}
fn apply<'de, A>(
&mut self,
seq: &mut A,
ctx: &mut ApplyContext,
) -> Result<bool, <A as de::SeqAccess<'de>>::Error>
where
A: de::SeqAccess<'de>,
{
let mut changed = false;
while let Some(cmd) = ctx.read_next_command::<A, T>(seq)? {
use DiffCommandValue::*;
use DiffPathElementValue::*;
match cmd {
Enter(Field(_)) => {
ctx.skip_value(seq)?;
break;
}
Enter(CollectionIndex(idx)) => {
if let Some(value_ref) = self.get_mut(idx) {
changed |= <T as SerdeDiff>::apply(value_ref, seq, ctx)?;
} else {
ctx.skip_value(seq)?;
}
}
Enter(AddToCollection) => {
if let Value(v) = ctx
.read_next_command(seq)?
.expect("Expected value after AddToCollection")
{
changed = true;
self.push(v);
} else {
panic!("Expected value after AddToCollection");
}
}
Remove(num_elements) => {
let new_length = self.len().saturating_sub(num_elements);
self.truncate(new_length);
changed = true;
break;
}
_ => break,
}
}
Ok(changed)
}
}