Skip to content

Commit ec09467

Browse files
authored
Merge pull request #5 from ChrisRega/feature/0.6.0-new-diffs-api
Improve API for library usage
2 parents b021c2e + 7073fd2 commit ec09467

File tree

5 files changed

+116
-117
lines changed

5 files changed

+116
-117
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[package]
22
name = "json_diff_ng"
3-
version = "0.5.0"
3+
version = "0.6.0-RC1"
44
authors = ["ksceriath", "ChrisRega"]
55
edition = "2021"
66
license = "Unlicense"
7-
description = "A small diff tool utility for comparing jsons. Forked from ksceriath and improved for usage as a library and with good support for array diffs."
7+
description = "A small diff tool utility for comparing jsons. Forked from ksceriath and improved for usage as a library and with proper support for array diffs."
88
readme = "README.md"
99
homepage = "https://github.com/ChrisRega/json-diff"
1010
repository = "https://github.com/ChrisRega/json-diff"

src/ds/key_node.rs

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,54 @@
1-
use crate::enums::ValueType;
2-
use serde_json::Value;
31
use std::collections::HashMap;
42

3+
use serde_json::Value;
4+
5+
use crate::enums::{DiffEntry, PathElement};
6+
57
#[derive(Debug, PartialEq)]
68
pub enum KeyNode {
79
Nil,
810
Value(Value, Value),
911
Node(HashMap<String, KeyNode>),
10-
}
11-
12-
fn truncate(s: &str, max_chars: usize) -> String {
13-
match s.char_indices().nth(max_chars) {
14-
None => String::from(s),
15-
Some((idx, _)) => {
16-
let shorter = &s[..idx];
17-
let snip = "//SNIP//";
18-
let new_s = format!("{}{}", shorter, snip);
19-
new_s
20-
}
21-
}
12+
Array(Vec<(usize, KeyNode)>),
2213
}
2314

2415
impl KeyNode {
25-
pub fn absolute_keys_to_vec(&self, max_display_length: Option<usize>) -> Vec<ValueType> {
26-
let mut vec = Vec::new();
27-
self.absolute_keys(&mut vec, None, max_display_length);
28-
vec
16+
pub fn get_diffs(&self) -> Vec<DiffEntry> {
17+
let mut buf = Vec::new();
18+
self.follow_path(&mut buf, &[]);
19+
buf
2920
}
3021

31-
pub fn absolute_keys(
32-
&self,
33-
keys: &mut Vec<ValueType>,
34-
key_from_root: Option<String>,
35-
max_display_length: Option<usize>,
36-
) {
37-
let max_display_length = max_display_length.unwrap_or(4000);
38-
let val_key = |key: Option<String>| {
39-
key.map(|mut s| {
40-
s.push_str("->");
41-
s
42-
})
43-
.unwrap_or_default()
44-
};
22+
pub fn follow_path(&self, diffs: &mut Vec<DiffEntry>, offset: &[PathElement]) {
4523
match self {
4624
KeyNode::Nil => {
47-
if let Some(key) = key_from_root {
48-
keys.push(ValueType::new_key(key))
25+
let is_map_child = offset
26+
.last()
27+
.map(|o| matches!(o, PathElement::Object(_)))
28+
.unwrap_or_default();
29+
if is_map_child {
30+
diffs.push(DiffEntry {
31+
path: offset.to_vec(),
32+
values: None,
33+
});
34+
}
35+
}
36+
KeyNode::Value(l, r) => diffs.push(DiffEntry {
37+
path: offset.to_vec(),
38+
values: Some((l.to_string(), r.to_string())),
39+
}),
40+
KeyNode::Node(o) => {
41+
for (k, v) in o {
42+
let mut new_offset = offset.to_vec();
43+
new_offset.push(PathElement::Object(k.clone()));
44+
v.follow_path(diffs, &new_offset);
4945
}
5046
}
51-
KeyNode::Value(a, b) => keys.push(ValueType::new_value(
52-
val_key(key_from_root),
53-
truncate(a.to_string().as_str(), max_display_length),
54-
truncate(b.to_string().as_str(), max_display_length),
55-
)),
56-
KeyNode::Node(map) => {
57-
for (key, value) in map {
58-
value.absolute_keys(
59-
keys,
60-
Some(format!("{}{}", val_key(key_from_root.clone()), key)),
61-
Some(max_display_length),
62-
)
47+
KeyNode::Array(v) => {
48+
for (l, k) in v {
49+
let mut new_offset = offset.to_vec();
50+
new_offset.push(PathElement::ArrayEntry(*l));
51+
k.follow_path(diffs, &new_offset);
6352
}
6453
}
6554
}

src/ds/mismatch.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::ds::key_node::KeyNode;
2-
use crate::enums::{DiffType, ValueType};
2+
use crate::enums::{DiffEntry, DiffType};
33

44
#[derive(Debug, PartialEq)]
55
pub struct Mismatch {
@@ -31,24 +31,20 @@ impl Mismatch {
3131
&& self.right_only_keys == KeyNode::Nil
3232
}
3333

34-
pub fn all_diffs(&self) -> Vec<(DiffType, ValueType)> {
35-
self.all_diffs_trunc(None)
36-
}
37-
38-
pub fn all_diffs_trunc(&self, truncation_length: Option<usize>) -> Vec<(DiffType, ValueType)> {
34+
pub fn all_diffs(&self) -> Vec<(DiffType, DiffEntry)> {
3935
let both = self
4036
.keys_in_both
41-
.absolute_keys_to_vec(truncation_length)
37+
.get_diffs()
4238
.into_iter()
4339
.map(|k| (DiffType::Mismatch, k));
4440
let left = self
4541
.left_only_keys
46-
.absolute_keys_to_vec(truncation_length)
42+
.get_diffs()
4743
.into_iter()
4844
.map(|k| (DiffType::LeftExtra, k));
4945
let right = self
5046
.right_only_keys
51-
.absolute_keys_to_vec(truncation_length)
47+
.get_diffs()
5248
.into_iter()
5349
.map(|k| (DiffType::RightExtra, k));
5450

@@ -59,6 +55,7 @@ impl Mismatch {
5955
#[cfg(test)]
6056
mod test {
6157
use super::*;
58+
6259
#[test]
6360
fn empty_diffs() {
6461
let empty = Mismatch::empty();

src/enums.rs

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::fmt::{Display, Formatter};
2+
23
use thiserror::Error;
34
use vg_errortools::FatIOError;
45

@@ -30,45 +31,42 @@ impl Display for DiffType {
3031
}
3132
}
3233

33-
pub enum ValueType {
34-
Key(String),
35-
Value {
36-
key: String,
37-
value_left: String,
38-
value_right: String,
39-
},
34+
#[derive(Clone, Debug, PartialEq, Eq)]
35+
pub enum PathElement {
36+
Object(String),
37+
ArrayEntry(usize),
4038
}
4139

42-
impl ValueType {
43-
pub fn new_value(key: String, value_left: String, value_right: String) -> Self {
44-
Self::Value {
45-
value_right,
46-
value_left,
47-
key,
48-
}
49-
}
50-
pub fn new_key(key: String) -> Self {
51-
Self::Key(key)
52-
}
40+
#[derive(Clone, Debug, Default, PartialEq, Eq)]
41+
pub struct DiffEntry {
42+
pub path: Vec<PathElement>,
43+
pub values: Option<(String, String)>,
44+
}
5345

54-
pub fn get_key(&self) -> &str {
55-
match self {
56-
ValueType::Value { key, .. } => key.as_str(),
57-
ValueType::Key(key) => key.as_str(),
46+
impl Display for DiffEntry {
47+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48+
for element in &self.path {
49+
write!(f, ".{element}")?;
5850
}
51+
if let Some((l, r)) = &self.values {
52+
if l != r {
53+
write!(f, ".({l} != {r})")?;
54+
} else {
55+
write!(f, ".({l})")?;
56+
}
57+
}
58+
Ok(())
5959
}
6060
}
6161

62-
impl Display for ValueType {
62+
impl Display for PathElement {
6363
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
6464
match self {
65-
ValueType::Key(key) => write!(f, "{key}"),
66-
ValueType::Value {
67-
value_left,
68-
key,
69-
value_right,
70-
} => {
71-
write!(f, "{key}{{{value_left}!={value_right}}}")
65+
PathElement::Object(o) => {
66+
write!(f, "{o}")
67+
}
68+
PathElement::ArrayEntry(l) => {
69+
write!(f, "[{l}]")
7270
}
7371
}
7472
}

0 commit comments

Comments
 (0)