use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum Change {
Added { path: String, new: Value },
Removed { path: String, old: Value },
Changed {
path: String,
old: Value,
new: Value,
},
}
impl Change {
pub fn path(&self) -> &str {
match self {
Change::Added { path, .. }
| Change::Removed { path, .. }
| Change::Changed { path, .. } => path,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ValueDiff {
pub changes: Vec<Change>,
pub truncated: usize,
}
impl ValueDiff {
pub fn is_empty(&self) -> bool {
self.changes.is_empty() && self.truncated == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct ByteDiff {
pub common_prefix: usize,
pub common_suffix: usize,
pub old_len: usize,
pub new_len: usize,
}
impl ByteDiff {
pub fn is_empty(&self) -> bool {
self.old_len == self.new_len && self.common_prefix == self.old_len
}
pub fn ranges(&self) -> (std::ops::Range<usize>, std::ops::Range<usize>) {
(
self.common_prefix..self.old_len - self.common_suffix,
self.common_prefix..self.new_len - self.common_suffix,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn a_change_is_tagged_by_op() {
let d = ValueDiff {
changes: vec![
Change::Changed {
path: "value".into(),
old: json!(41),
new: json!(42),
},
Change::Added {
path: "fresh".into(),
new: json!(true),
},
Change::Removed {
path: "gone".into(),
old: json!(null),
},
],
truncated: 0,
};
assert_eq!(
serde_json::to_value(&d).unwrap(),
json!({
"changes": [
{"op": "changed", "path": "value", "old": 41, "new": 42},
{"op": "added", "path": "fresh", "new": true},
{"op": "removed", "path": "gone", "old": null},
],
"truncated": 0,
})
);
let back: ValueDiff = serde_json::from_value(serde_json::to_value(&d).unwrap()).unwrap();
assert_eq!(back, d);
}
#[test]
fn a_byte_diff_round_trips() {
let d = ByteDiff {
common_prefix: 6,
common_suffix: 0,
old_len: 11,
new_len: 11,
};
assert_eq!(
serde_json::to_value(d).unwrap(),
json!({"common_prefix": 6, "common_suffix": 0, "old_len": 11, "new_len": 11})
);
}
}