use crate::diff::{Changeable, CollectionDiffEntry, Diffable, PrimitiveDiff, VecDiff};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value};
use std::collections::BTreeMap;
#[derive(Debug, Serialize, Deserialize)]
pub struct ValueMapDiff(pub BTreeMap<String, CollectionDiffEntry<Value>>);
impl PartialEq for ValueMapDiff {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Changeable for ValueMapDiff {
fn is_changed(&self) -> bool {
self.0.values().any(|v| v.is_changed())
}
}
impl Diffable for Map<String, Value> {
type Repr = ValueMapDiff;
fn diff(&self, b: &Self) -> Self::Repr {
let mut out = BTreeMap::new();
for (k, v) in self {
let other = b.get(k);
match other {
Some(other) => {
let diff = v.diff(other);
if diff.is_changed() {
out.insert(k.clone(), CollectionDiffEntry::Changed(diff))
} else {
out.insert(k.clone(), CollectionDiffEntry::Unchanged)
}
}
None => out.insert(k.clone(), CollectionDiffEntry::Removed(v.clone())),
};
}
for (k, v) in b {
if out.contains_key(k) {
continue;
}
out.insert(k.clone(), CollectionDiffEntry::Added(v.clone()));
}
ValueMapDiff(out)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub enum ValueDiff {
Unchanged,
VariantChanged {
old: Value,
new: Value,
},
BoolChanged {
old: bool,
new: bool,
},
StringChanged {
old: String,
new: String,
},
NumberChanged {
old: Number,
new: Number,
},
ArrayChanged(VecDiff<Value>),
ObjectChanged(ValueMapDiff),
}
impl PartialEq for ValueDiff {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::VariantChanged {
old: a_old,
new: a_new,
},
Self::VariantChanged {
old: b_old,
new: b_new,
},
) => a_old == b_old && a_new == b_new,
(
Self::BoolChanged {
old: a_old,
new: a_new,
},
Self::BoolChanged {
old: b_old,
new: b_new,
},
) => a_old == b_old && a_new == b_new,
(
Self::StringChanged {
old: a_old,
new: a_new,
},
Self::StringChanged {
old: b_old,
new: b_new,
},
) => a_old == b_old && a_new == b_new,
(
Self::NumberChanged {
old: a_old,
new: a_new,
},
Self::NumberChanged {
old: b_old,
new: b_new,
},
) => a_old == b_old && a_new == b_new,
(Self::ArrayChanged(a), Self::ArrayChanged(b)) => a == b,
(Self::ObjectChanged(a), Self::ObjectChanged(b)) => a == b,
_ => false,
}
}
}
impl Changeable for ValueDiff {
fn is_changed(&self) -> bool {
!matches!(self, Self::Unchanged)
}
}
impl Diffable for Value {
type Repr = ValueDiff;
fn diff(&self, b: &Self) -> Self::Repr {
match (self, b) {
(Self::Null, Self::Null) => ValueDiff::Unchanged,
(Self::Bool(a), Self::Bool(b)) => match a.diff(b) {
PrimitiveDiff::Changed { old, new } => ValueDiff::BoolChanged { old, new },
PrimitiveDiff::Unchanged => ValueDiff::Unchanged,
},
(Self::Number(na), Self::Number(nb)) => match na == nb {
true => ValueDiff::Unchanged,
false => ValueDiff::NumberChanged {
old: na.clone(),
new: nb.clone(),
},
},
(Self::String(a), Self::String(b)) => match a.diff(b) {
PrimitiveDiff::Changed { old, new } => ValueDiff::StringChanged { old, new },
PrimitiveDiff::Unchanged => ValueDiff::Unchanged,
},
(Self::Array(a), Self::Array(b)) => {
let diff = a.diff(b);
match diff.is_changed() {
true => ValueDiff::ArrayChanged(diff),
false => ValueDiff::Unchanged,
}
}
(Self::Object(a), Self::Object(b)) => {
let diff = a.diff(b);
match diff.is_changed() {
true => ValueDiff::ObjectChanged(diff),
false => ValueDiff::Unchanged,
}
}
_ => ValueDiff::VariantChanged {
old: self.clone(),
new: b.clone(),
},
}
}
}