Skip to content

Feature/0.6.0 #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
[package]
name = "json_diff_ng"
version = "0.6.0-RC1"
version = "0.6.0-RC3"
authors = ["ksceriath", "ChrisRega"]
edition = "2021"
license = "Unlicense"
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."
description = "A JSON diff library and CLI."
readme = "README.md"
homepage = "https://github.com/ChrisRega/json-diff"
repository = "https://github.com/ChrisRega/json-diff"
keywords = ["cli", "diff", "json"]
categories = ["command-line-utilities"]

[lib]
name = "json_diff"
name = "json_diff_ng"
path = "src/lib.rs"
crate-type = ["lib"]

[[bin]]
name = "json_diff"
name = "json_diff_ng"
path = "src/main.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -29,4 +29,4 @@ serde_json = { version = "1.0", features = ["preserve_order"] }
maplit = "1.0"
clap = { version = "4.5", features = ["derive"] }
diffs = "0.5"
regex = "1.10"
regex = "1.10"
41 changes: 35 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# json-diff
# json-diff-ng

[![Crates.io](https://img.shields.io/crates/d/json_diff_ng?style=flat)](https://crates.io/crates/json_diff_ng)
[![Documentation](https://docs.rs/json_diff_ng/badge.svg)](https://docs.rs/json_diff_ng)
![CI](https://github.com/ChrisRega/json-diff/actions/workflows/rust.yml/badge.svg?branch=master "CI")
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)

## Contributors:

<a href="https://github.com/ChrisRega/json-diff/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ChrisRega/json-diff" alt="Contributors"/>
</a>

## Library
json_diff_ng can be used to get diffs of json-serializable structures in rust.

### Usage example
```rust
use json_diff::compare_strs;
let data1 = r#"["a",{"c": ["d","f"] },"b"]"#;
let data2 = r#"["b",{"c": ["e","d"] },"a"]"#;
let diffs = compare_strs(data1, data2, true, &[]).unwrap();
assert!(!diffs.is_empty());
let diffs = diffs.unequal_values.get_diffs();
assert_eq!(diffs.len(), 1);
assert_eq!(
diffs.first().unwrap().to_string(),
r#".[0].c.[1].("f" != "e")"#
);
```

See [docs.rs](https://docs.rs/json_diff_ng) for more details.


## CLI
json-diff is a command line utility to compare two jsons.

Input can be fed as inline strings or through files.
Expand All @@ -17,9 +50,5 @@ file : read input from json files
direct : read input from command line

### Installation
`$ cargo install json_diff_ng`

Currently, json-diff is available through crates.io (apart from building this repo directly). For crate installation,
* Install cargo, through rustup
`$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
* Install json-diff
`$ cargo install json_diff`
56 changes: 0 additions & 56 deletions src/ds/key_node.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/ds/mod.rs

This file was deleted.

93 changes: 86 additions & 7 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,65 @@ impl From<String> for Error {
}
}

use std::collections::HashMap;

use serde_json::Value;

#[derive(Debug, PartialEq)]
pub enum DiffTreeNode {
Null,
Value(Value, Value),
Node(HashMap<String, DiffTreeNode>),
Array(Vec<(usize, DiffTreeNode)>),
}

impl<'a> DiffTreeNode {
pub fn get_diffs(&'a self) -> Vec<DiffEntry<'a>> {
let mut buf = Vec::new();
self.follow_path(&mut buf, &[]);
buf
}

pub fn follow_path<'b>(
&'a self,
diffs: &mut Vec<DiffEntry<'a>>,
offset: &'b [PathElement<'a>],
) {
match self {
DiffTreeNode::Null => {
let is_map_child = offset
.last()
.map(|o| matches!(o, PathElement::Object(_)))
.unwrap_or_default();
if is_map_child {
diffs.push(DiffEntry {
path: offset.to_vec(),
values: None,
});
}
}
DiffTreeNode::Value(l, r) => diffs.push(DiffEntry {
path: offset.to_vec(),
values: Some((l, r)),
}),
DiffTreeNode::Node(o) => {
for (k, v) in o {
let mut new_offset = offset.to_vec();
new_offset.push(PathElement::Object(k));
v.follow_path(diffs, &new_offset);
}
}
DiffTreeNode::Array(v) => {
for (l, k) in v {
let mut new_offset = offset.to_vec();
new_offset.push(PathElement::ArrayEntry(*l));
k.follow_path(diffs, &new_offset);
}
}
}
}
}

#[derive(Debug)]
pub enum DiffType {
RootMismatch,
Expand All @@ -40,18 +99,38 @@ impl Display for DiffType {
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PathElement {
Object(String),
pub enum PathElement<'a> {
Object(&'a str),
ArrayEntry(usize),
}

impl<'a> PathElement<'a> {
pub fn resolve<'b>(&self, v: &'b serde_json::Value) -> Option<&'b serde_json::Value> {
match self {
PathElement::Object(o) => v.get(o),
PathElement::ArrayEntry(i) => v.get(*i),
}
}

pub fn resolve_mut<'b>(
&self,
v: &'b mut serde_json::Value,
) -> Option<&'b mut serde_json::Value> {
match self {
PathElement::Object(o) => v.get_mut(o),
PathElement::ArrayEntry(i) => v.get_mut(*i),
}
}
}

/// A view on a single end-node of the [`DiffKeyNode`] tree.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DiffEntry {
pub path: Vec<PathElement>,
pub values: Option<(String, String)>,
pub struct DiffEntry<'a> {
pub path: Vec<PathElement<'a>>,
pub values: Option<(&'a serde_json::Value, &'a serde_json::Value)>,
}

impl Display for DiffEntry {
impl Display for DiffEntry<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for element in &self.path {
write!(f, ".{element}")?;
Expand All @@ -67,7 +146,7 @@ impl Display for DiffEntry {
}
}

impl Display for PathElement {
impl Display for PathElement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PathElement::Object(o) => {
Expand Down
38 changes: 37 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
pub mod ds;
//! # Library for comparing JSON data structures
//! ## Summary
//! Main entry points are [`compare_strs`] to compare string slices and [`compare_serde_values`] to compare already parse [`serde_json::Value`]
//! ## Example:
//! ```rust
//! use json_diff_ng::compare_strs;
//! let data1 = r#"["a",{"c": ["d","f"] },"b"]"#;
//! let data2 = r#"["b",{"c": ["e","d"] },"a"]"#;
//! let diffs = compare_strs(data1, data2, true, &[]).unwrap();
//! assert!(!diffs.is_empty());
//! let diffs = diffs.unequal_values.get_diffs();
//!
//! assert_eq!(diffs.len(), 1);
//! assert_eq!(
//! diffs.first().unwrap().to_string(),
//! r#".[0].c.[1].("f" != "e")"#
//! );
//! ```
//! ## How to handle the results
//! Results are returned in a triple of [`DiffTreeNode`] called [`Mismatch`].
//! The triple consists of values only on the left side, values only on the right side and values on both sides that differ.
//! Since tree traversal is not usually what you want to do on client side, [`DiffTreeNode`] offers [`DiffTreeNode::get_diffs`] to retrieve
//! a flat list of [`DiffEntry`] which is more easily usable. The path in the json is collapsed into a vector of [`PathElement`] which can be used to follow the diff.
//! Similarly, all diffs after an operation can be collected using [`Mismatch::all_diffs`].
//!
//!

pub mod enums;
pub mod mismatch;
pub mod process;
pub mod sort;
pub use enums::DiffEntry;
pub use enums::DiffTreeNode;
pub use enums::DiffType;
pub use enums::Error;
pub use mismatch::Mismatch;
pub use process::compare_serde_values;
pub use process::compare_strs;
pub type Result<T> = std::result::Result<T, Error>;
9 changes: 4 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use clap::Parser;
use clap::Subcommand;

use json_diff::{ds::mismatch::Mismatch, process::compare_jsons};
use json_diff::enums::Error;
use json_diff_ng::{compare_strs, Mismatch, Result};

#[derive(Subcommand, Clone)]
/// Input selection
Expand All @@ -29,7 +28,7 @@ struct Args {
truncation_length: usize,
}

fn main() -> Result<(), Error> {
fn main() -> Result<()> {
let args = Args::parse();
let (json_1, json_2) = match args.cmd {
Mode::Direct { json_2, json_1 } => (json_1, json_2),
Expand All @@ -40,7 +39,7 @@ fn main() -> Result<(), Error> {
}
};

let mismatch = compare_jsons(&json_1, &json_2, args.sort_arrays, &[])?;
let mismatch = compare_strs(&json_1, &json_2, args.sort_arrays, &[])?;

let comparison_result = check_diffs(mismatch)?;
if !comparison_result {
Expand All @@ -49,7 +48,7 @@ fn main() -> Result<(), Error> {
Ok(())
}

pub fn check_diffs(result: Mismatch) -> Result<bool, Error> {
pub fn check_diffs(result: Mismatch) -> Result<bool> {
let mismatches = result.all_diffs();
let is_good = mismatches.is_empty();
for (d_type, key) in mismatches {
Expand Down
Loading
Loading