use datavalue::OwnedDataValue;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Arc;
#[derive(Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[non_exhaustive]
pub struct Change {
pub path: Arc<str>,
#[serde(deserialize_with = "super::exact")]
#[cfg_attr(feature = "ts", ts(type = "JsonValue"))]
pub old_value: OwnedDataValue,
#[serde(deserialize_with = "super::exact")]
#[cfg_attr(feature = "ts", ts(type = "JsonValue"))]
pub new_value: OwnedDataValue,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub removed: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub replaced: bool,
#[serde(skip)]
pub(crate) trace_only: bool,
}
impl PartialEq for Change {
fn eq(&self, other: &Self) -> bool {
let Self {
path,
old_value,
new_value,
removed,
replaced,
trace_only: _,
} = self;
*path == other.path
&& *old_value == other.old_value
&& *new_value == other.new_value
&& *removed == other.removed
&& *replaced == other.replaced
}
}
impl fmt::Debug for Change {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
path,
old_value,
new_value,
removed,
replaced,
trace_only: _,
} = self;
f.debug_struct("Change")
.field("path", path)
.field("old_value", old_value)
.field("new_value", new_value)
.field("removed", removed)
.field("replaced", replaced)
.finish()
}
}
impl Change {
pub fn write(
path: impl Into<Arc<str>>,
old_value: OwnedDataValue,
new_value: OwnedDataValue,
) -> Self {
Self {
path: path.into(),
old_value,
new_value,
removed: false,
replaced: false,
trace_only: false,
}
}
pub(crate) fn assign(
path: &crate::expr::Path,
old_value: OwnedDataValue,
new_value: OwnedDataValue,
) -> Self {
Self {
replaced: path.is_root(),
..Self::write(Arc::clone(path.dotted()), old_value, new_value)
}
}
pub fn removal(path: impl Into<Arc<str>>, old_value: OwnedDataValue) -> Self {
Self {
path: path.into(),
old_value,
new_value: OwnedDataValue::Null,
removed: true,
replaced: false,
trace_only: false,
}
}
pub(crate) fn for_trace(
path: impl Into<Arc<str>>,
old_value: OwnedDataValue,
new_value: OwnedDataValue,
) -> Self {
Self {
trace_only: true,
..Self::write(path, old_value, new_value)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::expr::Path;
fn path(dotted: &str) -> Path {
dotted.parse().unwrap()
}
#[test]
fn each_kind_of_record_sets_its_own_flags() {
let (old, new) = (OwnedDataValue::Null, OwnedDataValue::from(1));
let flags = |c: &Change| (c.removed, c.replaced, c.trace_only);
assert_eq!(
flags(&Change::assign(&path("data"), old.clone(), new.clone())),
(false, true, false)
);
assert_eq!(
flags(&Change::assign(&path("data.a"), old.clone(), new.clone())),
(false, false, false)
);
assert_eq!(
flags(&Change::write("data", old.clone(), new.clone())),
(false, false, false)
);
assert_eq!(
flags(&Change::removal("data.a", new.clone())),
(true, false, false)
);
assert_eq!(
flags(&Change::for_trace("data.a", old, new)),
(false, false, true)
);
}
}