From bce14bc95c666f87b44b10e69af34dd5647c83d2 Mon Sep 17 00:00:00 2001 From: nohupped_arch Date: Tue, 22 Sep 2020 02:58:02 +0800 Subject: [PATCH 1/4] * moved functions from main to lib to use it as a library * removed a few unwraps in favour of Result inside the lib --- Cargo.toml | 11 ++- src/constants.rs | 2 +- src/lib.rs | 196 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 225 ++++------------------------------------------- 4 files changed, 222 insertions(+), 212 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index a38e735..821d727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,19 @@ repository = "https://github.com/ksceriath/json-diff" keywords = ["cli", "diff", "json"] categories = ["command-line-utilities"] +[lib] +name = "json_diff" +path = "src/lib.rs" +crate-type = ["lib"] + +[[bin]] +name = "json_diff" +path = "src/main.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] serde_json = "1.0.41" maplit = "1.0.2" colored = "1.9.0" -structopt = "0.3.5" \ No newline at end of file +structopt = "0.3.5" diff --git a/src/constants.rs b/src/constants.rs index 472337a..9fc9bd6 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,5 @@ -use std::fmt; use colored::*; +use std::fmt; #[derive(Debug)] pub enum Message { diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7f988b6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,196 @@ +pub mod constants; +pub mod ds; +mod process; + +use colored::*; +use constants::Message; +use ds::{key_node::KeyNode, mismatch::Mismatch}; +use std::io::{self, Write}; + +pub fn compare_jsons(a: &str, b: &str) -> Result { + let value1 = serde_json::from_str(a)?; + let value2 = serde_json::from_str(b)?; + Ok(process::match_json(&value1, &value2)) +} + +pub fn display_output(result: Mismatch) -> Result<(), std::io::Error> { + let no_mismatch = Mismatch { + left_only_keys: KeyNode::Nil, + right_only_keys: KeyNode::Nil, + keys_in_both: KeyNode::Nil, + }; + + let stdout = io::stdout(); + let mut handle = io::BufWriter::new(stdout.lock()); + Ok(if no_mismatch == result { + writeln!(handle, "\n{}", Message::NoMismatch)?; + } else { + match result.keys_in_both { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.keys_in_both.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::Mismatch)?; + for key in keys { + writeln!(handle, "{}", key)?; + } + } + KeyNode::Value(_, _) => writeln!(handle, "{}", Message::RootMismatch)?, + KeyNode::Nil => (), + } + match result.left_only_keys { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.left_only_keys.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::LeftExtra)?; + for key in keys { + writeln!(handle, "{}", key.red().bold())?; + } + } + KeyNode::Value(_, _) => (), + KeyNode::Nil => (), + } + match result.right_only_keys { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.right_only_keys.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::RightExtra)?; + for key in keys { + writeln!(handle, "{}", key.green().bold())?; + } + } + KeyNode::Value(_, _) => (), + KeyNode::Nil => (), + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + use maplit::hashmap; + use serde_json::json; + + #[test] + fn nested_diff() { + let data1 = r#"{ + "a":"b", + "b":{ + "c":{ + "d":true, + "e":5, + "f":9, + "h":{ + "i":true, + "j":false + } + } + } + }"#; + let data2 = r#"{ + "a":"b", + "b":{ + "c":{ + "d":true, + "e":6, + "g":0, + "h":{ + "i":false, + "k":false + } + } + } + }"#; + + let expected_left = KeyNode::Node(hashmap! { + "b".to_string() => KeyNode::Node(hashmap! { + "c".to_string() => KeyNode::Node(hashmap! { + "f".to_string() => KeyNode::Nil, + "h".to_string() => KeyNode::Node( hashmap! { + "j".to_string() => KeyNode::Nil, + } + ), + } + ), + }), + }); + let expected_right = KeyNode::Node(hashmap! { + "b".to_string() => KeyNode::Node(hashmap! { + "c".to_string() => KeyNode::Node(hashmap! { + "g".to_string() => KeyNode::Nil, + "h".to_string() => KeyNode::Node(hashmap! { + "k".to_string() => KeyNode::Nil, + } + ) + } + ) + } + ) + }); + let expected_uneq = KeyNode::Node(hashmap! { + "b".to_string() => KeyNode::Node(hashmap! { + "c".to_string() => KeyNode::Node(hashmap! { + "e".to_string() => KeyNode::Value(json!(5), json!(6)), + "h".to_string() => KeyNode::Node(hashmap! { + "i".to_string() => KeyNode::Value(json!(true), json!(false)), + } + ) + } + ) + } + ) + }); + let expected = Mismatch::new(expected_left, expected_right, expected_uneq); + + let mismatch = compare_jsons(data1, data2).unwrap(); + assert_eq!(mismatch, expected, "Diff was incorrect."); + } + + #[test] + fn no_diff() { + let data1 = r#"{ + "a":"b", + "b":{ + "c":{ + "d":true, + "e":5, + "f":9, + "h":{ + "i":true, + "j":false + } + } + } + }"#; + let data2 = r#"{ + "a":"b", + "b":{ + "c":{ + "d":true, + "e":5, + "f":9, + "h":{ + "i":true, + "j":false + } + } + } + }"#; + + assert_eq!( + compare_jsons(data1, data2).unwrap(), + Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) + ); + } + + #[test] + fn no_json() { + let data1 = r#"{}"#; + let data2 = r#"{}"#; + + assert_eq!( + compare_jsons(data1, data2).unwrap(), + Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) + ); + } +} diff --git a/src/main.rs b/src/main.rs index 3a7850c..251d05a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,5 @@ -mod constants; -mod ds; -mod process; - -use colored::*; -use constants::Message; -use ds::{key_node::KeyNode, mismatch::Mismatch}; -use serde_json; -use std::{ - fmt, fs, - io::{self, Write}, - process as proc, - str::FromStr, -}; +use json_diff::{compare_jsons, constants::Message, display_output}; +use std::{fmt, fs, process as proc, str::FromStr}; use structopt::StructOpt; const HELP: &str = r#" @@ -58,11 +46,6 @@ struct Cli { source2: String, } -fn error_exit(message: constants::Message) -> ! { - eprintln!("{}", message); - proc::exit(1); -} - fn main() { let args = Cli::from_args(); @@ -80,198 +63,20 @@ fn main() { } } }; - display_output(compare_jsons(&data1, &data2)); -} - -fn display_output(result: Mismatch) { - let no_mismatch = Mismatch { - left_only_keys: KeyNode::Nil, - right_only_keys: KeyNode::Nil, - keys_in_both: KeyNode::Nil, - }; - - let stdout = io::stdout(); - let mut handle = io::BufWriter::new(stdout.lock()); - if no_mismatch == result { - writeln!(handle, "\n{}", Message::NoMismatch).unwrap(); - } else { - match result.keys_in_both { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.keys_in_both.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::Mismatch).unwrap(); - for key in keys { - writeln!(handle, "{}", key).unwrap(); - } - } - KeyNode::Value(_, _) => writeln!(handle, "{}", Message::RootMismatch).unwrap(), - KeyNode::Nil => (), - } - match result.left_only_keys { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.left_only_keys.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::LeftExtra).unwrap(); - for key in keys { - writeln!(handle, "{}", key.red().bold()).unwrap(); - } - } - KeyNode::Value(_, _) => error_exit(Message::UnknownError), - KeyNode::Nil => (), - } - match result.right_only_keys { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.right_only_keys.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::RightExtra).unwrap(); - for key in keys { - writeln!(handle, "{}", key.green().bold()).unwrap(); - } - } - KeyNode::Value(_, _) => error_exit(Message::UnknownError), - KeyNode::Nil => (), - } - } -} - -fn compare_jsons(a: &str, b: &str) -> Mismatch { - if let Ok(value1) = serde_json::from_str(a) { - if let Ok(value2) = serde_json::from_str(b) { - process::match_json(&value1, &value2) - } else { - error_exit(Message::JSON2); + let mismatch = match compare_jsons(&data1, &data2) { + Ok(mismatch) => mismatch, + Err(err) => { + eprintln!("{}", err); + proc::exit(1) } - } else { - error_exit(Message::JSON1); - } + }; + match display_output(mismatch) { + Ok(_) => (), + Err(err) => eprintln!("{}", err), + }; } -#[cfg(test)] -mod tests { - use super::*; - use maplit::hashmap; - use serde_json::json; - - #[test] - fn nested_diff() { - let data1 = r#"{ - "a":"b", - "b":{ - "c":{ - "d":true, - "e":5, - "f":9, - "h":{ - "i":true, - "j":false - } - } - } - }"#; - let data2 = r#"{ - "a":"b", - "b":{ - "c":{ - "d":true, - "e":6, - "g":0, - "h":{ - "i":false, - "k":false - } - } - } - }"#; - - let expected_left = KeyNode::Node(hashmap! { - "b".to_string() => KeyNode::Node(hashmap! { - "c".to_string() => KeyNode::Node(hashmap! { - "f".to_string() => KeyNode::Nil, - "h".to_string() => KeyNode::Node( hashmap! { - "j".to_string() => KeyNode::Nil, - } - ), - } - ), - }), - }); - let expected_right = KeyNode::Node(hashmap! { - "b".to_string() => KeyNode::Node(hashmap! { - "c".to_string() => KeyNode::Node(hashmap! { - "g".to_string() => KeyNode::Nil, - "h".to_string() => KeyNode::Node(hashmap! { - "k".to_string() => KeyNode::Nil, - } - ) - } - ) - } - ) - }); - let expected_uneq = KeyNode::Node(hashmap! { - "b".to_string() => KeyNode::Node(hashmap! { - "c".to_string() => KeyNode::Node(hashmap! { - "e".to_string() => KeyNode::Value(json!(5), json!(6)), - "h".to_string() => KeyNode::Node(hashmap! { - "i".to_string() => KeyNode::Value(json!(true), json!(false)), - } - ) - } - ) - } - ) - }); - let expected = Mismatch::new(expected_left, expected_right, expected_uneq); - - let mismatch = compare_jsons(data1, data2); - assert_eq!(mismatch, expected, "Diff was incorrect."); - } - - #[test] - fn no_diff() { - let data1 = r#"{ - "a":"b", - "b":{ - "c":{ - "d":true, - "e":5, - "f":9, - "h":{ - "i":true, - "j":false - } - } - } - }"#; - let data2 = r#"{ - "a":"b", - "b":{ - "c":{ - "d":true, - "e":5, - "f":9, - "h":{ - "i":true, - "j":false - } - } - } - }"#; - - assert_eq!( - compare_jsons(data1, data2), - Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) - ); - } - - #[test] - fn no_json() { - let data1 = r#"{}"#; - let data2 = r#"{}"#; - - assert_eq!( - compare_jsons(data1, data2), - Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) - ); - } +fn error_exit(message: Message) -> ! { + eprintln!("{}", message); + proc::exit(1); } From 4e655a504359695b97fde24269ee6f816f7c5cc4 Mon Sep 17 00:00:00 2001 From: nohupped_arch Date: Wed, 23 Sep 2020 00:34:16 +0800 Subject: [PATCH 2/4] Move output to bin --- src/lib.rs | 58 ++--------------------------------------------- src/main.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7f988b6..13eb8c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,72 +2,18 @@ pub mod constants; pub mod ds; mod process; -use colored::*; -use constants::Message; -use ds::{key_node::KeyNode, mismatch::Mismatch}; -use std::io::{self, Write}; - +use ds::mismatch::Mismatch; pub fn compare_jsons(a: &str, b: &str) -> Result { let value1 = serde_json::from_str(a)?; let value2 = serde_json::from_str(b)?; Ok(process::match_json(&value1, &value2)) } -pub fn display_output(result: Mismatch) -> Result<(), std::io::Error> { - let no_mismatch = Mismatch { - left_only_keys: KeyNode::Nil, - right_only_keys: KeyNode::Nil, - keys_in_both: KeyNode::Nil, - }; - - let stdout = io::stdout(); - let mut handle = io::BufWriter::new(stdout.lock()); - Ok(if no_mismatch == result { - writeln!(handle, "\n{}", Message::NoMismatch)?; - } else { - match result.keys_in_both { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.keys_in_both.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::Mismatch)?; - for key in keys { - writeln!(handle, "{}", key)?; - } - } - KeyNode::Value(_, _) => writeln!(handle, "{}", Message::RootMismatch)?, - KeyNode::Nil => (), - } - match result.left_only_keys { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.left_only_keys.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::LeftExtra)?; - for key in keys { - writeln!(handle, "{}", key.red().bold())?; - } - } - KeyNode::Value(_, _) => (), - KeyNode::Nil => (), - } - match result.right_only_keys { - KeyNode::Node(_) => { - let mut keys = Vec::new(); - result.right_only_keys.absolute_keys(&mut keys, None); - writeln!(handle, "\n{}:", Message::RightExtra)?; - for key in keys { - writeln!(handle, "{}", key.green().bold())?; - } - } - KeyNode::Value(_, _) => (), - KeyNode::Nil => (), - } - }) -} #[cfg(test)] mod tests { use super::*; - + use super::ds::{key_node::KeyNode, mismatch::Mismatch}; use maplit::hashmap; use serde_json::json; diff --git a/src/main.rs b/src/main.rs index 251d05a..5092976 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,15 @@ -use json_diff::{compare_jsons, constants::Message, display_output}; -use std::{fmt, fs, process as proc, str::FromStr}; +use colored::*; +use json_diff::{ + compare_jsons, + constants::Message, + ds::{key_node::KeyNode, mismatch::Mismatch}, +}; +use std::{ + fmt, fs, + io::{self, Write}, + process as proc, + str::FromStr, +}; use structopt::StructOpt; const HELP: &str = r#" @@ -80,3 +90,54 @@ fn error_exit(message: Message) -> ! { eprintln!("{}", message); proc::exit(1); } + +pub fn display_output(result: Mismatch) -> Result<(), std::io::Error> { + let no_mismatch = Mismatch { + left_only_keys: KeyNode::Nil, + right_only_keys: KeyNode::Nil, + keys_in_both: KeyNode::Nil, + }; + + let stdout = io::stdout(); + let mut handle = io::BufWriter::new(stdout.lock()); + Ok(if no_mismatch == result { + writeln!(handle, "\n{}", Message::NoMismatch)?; + } else { + match result.keys_in_both { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.keys_in_both.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::Mismatch)?; + for key in keys { + writeln!(handle, "{}", key)?; + } + } + KeyNode::Value(_, _) => writeln!(handle, "{}", Message::RootMismatch)?, + KeyNode::Nil => (), + } + match result.left_only_keys { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.left_only_keys.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::LeftExtra)?; + for key in keys { + writeln!(handle, "{}", key.red().bold())?; + } + } + KeyNode::Value(_, _) => (), + KeyNode::Nil => (), + } + match result.right_only_keys { + KeyNode::Node(_) => { + let mut keys = Vec::new(); + result.right_only_keys.absolute_keys(&mut keys, None); + writeln!(handle, "\n{}:", Message::RightExtra)?; + for key in keys { + writeln!(handle, "{}", key.green().bold())?; + } + } + KeyNode::Value(_, _) => (), + KeyNode::Nil => (), + } + }) +} From fc3fc7e8a6c1f21e7b65df5aa1dc954c5d4331bd Mon Sep 17 00:00:00 2001 From: nohupped_arch Date: Wed, 23 Sep 2020 01:01:40 +0800 Subject: [PATCH 3/4] Test case for source parse error --- src/constants.rs | 3 ++- src/lib.rs | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 9fc9bd6..1074046 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,8 @@ use colored::*; use std::fmt; -#[derive(Debug)] +// PartialEq is added for the sake of Test case that uses assert_eq +#[derive(Debug, PartialEq)] pub enum Message { BadOption, SOURCE1, diff --git a/src/lib.rs b/src/lib.rs index 13eb8c5..804909b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,18 @@ pub mod constants; pub mod ds; mod process; - +use constants::Message; use ds::mismatch::Mismatch; -pub fn compare_jsons(a: &str, b: &str) -> Result { - let value1 = serde_json::from_str(a)?; - let value2 = serde_json::from_str(b)?; + +pub fn compare_jsons(a: &str, b: &str) -> Result { + let value1 = match serde_json::from_str(a) { + Ok(val1) => val1, + Err(_) => return Err(Message::JSON1), + }; + let value2 = match serde_json::from_str(b) { + Ok(val2) => val2, + Err(_) => return Err(Message::JSON2), + }; Ok(process::match_json(&value1, &value2)) } @@ -139,4 +146,28 @@ mod tests { Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) ); } + + #[test] + fn parse_err_source_one() { + let invalid_json1 = r#"{invalid: json}"#; + let valid_json2 = r#"{"a":"b"}"#; + match compare_jsons(invalid_json1, valid_json2) { + Ok(_) => panic!("This shouldn't be an Ok"), + Err(err) => { + assert_eq!(Message::JSON1, err); + }, + }; + } + + #[test] + fn parse_err_source_two() { + let valid_json1 = r#"{"a":"b"}"#; + let invalid_json2 = r#"{invalid: json}"#; + match compare_jsons(valid_json1, invalid_json2) { + Ok(_) => panic!("This shouldn't be an Ok"), + Err(err) => { + assert_eq!(Message::JSON2, err); + }, + }; + } } From 34f270f2d5a9770377c53a6877f3c176c5e1a611 Mon Sep 17 00:00:00 2001 From: nohupped_arch Date: Wed, 23 Sep 2020 01:04:24 +0800 Subject: [PATCH 4/4] cargo fmt --- src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 804909b..6fd78eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,11 +16,10 @@ pub fn compare_jsons(a: &str, b: &str) -> Result { Ok(process::match_json(&value1, &value2)) } - #[cfg(test)] mod tests { - use super::*; use super::ds::{key_node::KeyNode, mismatch::Mismatch}; + use super::*; use maplit::hashmap; use serde_json::json; @@ -155,7 +154,7 @@ mod tests { Ok(_) => panic!("This shouldn't be an Ok"), Err(err) => { assert_eq!(Message::JSON1, err); - }, + } }; } @@ -167,7 +166,7 @@ mod tests { Ok(_) => panic!("This shouldn't be an Ok"), Err(err) => { assert_eq!(Message::JSON2, err); - }, + } }; } }