From f04ba930129d922dd7509071e00cea803069938f Mon Sep 17 00:00:00 2001 From: Christopher Regali Date: Sat, 25 May 2024 22:58:24 +0200 Subject: [PATCH] Add error message to structural problems, Refactoring comparison code into more readable functions. --- .vscode/launch.json | 64 +++++++++++++ src/enums.rs | 8 ++ src/process.rs | 227 +++++++++++++++++++++++--------------------- 3 files changed, 192 insertions(+), 107 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7d355e2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,64 @@ +{ + // Verwendet IntelliSense zum Ermitteln möglicher Attribute. + // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. + // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'json_diff'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=json_diff_ng" + ], + "filter": { + "name": "json_diff", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'json_diff'", + "cargo": { + "args": [ + "build", + "--bin=json_diff", + "--package=json_diff_ng" + ], + "filter": { + "name": "json_diff", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'json_diff'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=json_diff", + "--package=json_diff_ng" + ], + "filter": { + "name": "json_diff", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/enums.rs b/src/enums.rs index 6c7f51f..c423078 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -5,12 +5,20 @@ use vg_errortools::FatIOError; #[derive(Debug, Error)] pub enum Error { + #[error("Misc error: {0}")] + Misc(String), #[error("Error opening file: {0}")] IOError(#[from] FatIOError), #[error("Error parsing first json: {0}")] JSON(#[from] serde_json::Error), } +impl From for Error { + fn from(value: String) -> Self { + Self::Misc(value) + } +} + #[derive(Debug)] pub enum DiffType { RootMismatch, diff --git a/src/process.rs b/src/process.rs index a6663b4..c5ef18e 100644 --- a/src/process.rs +++ b/src/process.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::collections::HashSet; -use diffs::{Diff, myers, Replace}; +use diffs::{myers, Diff, Replace}; use regex::Regex; use serde_json::Map; use serde_json::Value; @@ -19,7 +19,7 @@ pub fn compare_jsons( ) -> Result { let value1 = serde_json::from_str(a)?; let value2 = serde_json::from_str(b)?; - Ok(match_json(&value1, &value2, sort_arrays, ignore_keys)) + match_json(&value1, &value2, sort_arrays, ignore_keys) } fn values_to_node(vec: Vec<(usize, &Value)>) -> KeyNode { if vec.is_empty() { @@ -72,107 +72,120 @@ pub fn match_json( value2: &Value, sort_arrays: bool, ignore_keys: &[Regex], -) -> Mismatch { +) -> Result { match (value1, value2) { - (Value::Object(a), Value::Object(b)) => { - let diff = intersect_maps(a, b, ignore_keys); - let mut left_only_keys = get_map_of_keys(diff.left_only); - let mut right_only_keys = get_map_of_keys(diff.right_only); - let intersection_keys = diff.intersection; - - let mut unequal_keys = KeyNode::Nil; - - for key in intersection_keys { - let Mismatch { - left_only_keys: l, - right_only_keys: r, - keys_in_both: u, - } = match_json( - a.get(&key).unwrap(), - b.get(&key).unwrap(), - sort_arrays, - ignore_keys, - ); - left_only_keys = insert_child_key_map(left_only_keys, l, &key); - right_only_keys = insert_child_key_map(right_only_keys, r, &key); - unequal_keys = insert_child_key_map(unequal_keys, u, &key); - } + (Value::Object(a), Value::Object(b)) => process_objects(a, b, ignore_keys, sort_arrays), + (Value::Array(a), Value::Array(b)) => process_arrays(sort_arrays, a, ignore_keys, b), + (a, b) => process_values(a, b), + } +} - Mismatch::new(left_only_keys, right_only_keys, unequal_keys) - } - (Value::Array(a), Value::Array(b)) => { - let a = preprocess_array(sort_arrays, a, ignore_keys); - let b = preprocess_array(sort_arrays, b, ignore_keys); - - let mut replaced = Vec::new(); - let mut deleted = Vec::new(); - let mut inserted = Vec::new(); - - let mut diff = Replace::new(ListDiffHandler::new( - &mut replaced, - &mut deleted, - &mut inserted, - )); - myers::diff( - &mut diff, - a.as_slice(), - 0, - a.len(), - b.as_slice(), - 0, - b.len(), - ) - .unwrap(); - - fn extract_one_sided_values( - v: Vec<(usize, usize)>, - vals: &[Value], - ) -> Vec<(usize, &Value)> { - v.into_iter() - .flat_map(|(o, ol)| (o..o + ol).map(|i| (i, &vals[i]))) - .collect::>() - } +fn process_values(a: &Value, b: &Value) -> Result { + if a == b { + Ok(Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil)) + } else { + Ok(Mismatch::new( + KeyNode::Nil, + KeyNode::Nil, + KeyNode::Value(a.clone(), b.clone()), + )) + } +} - let left_only_values: Vec<_> = extract_one_sided_values(deleted, a.as_slice()); - let right_only_values: Vec<_> = extract_one_sided_values(inserted, b.as_slice()); - - let mut left_only_nodes = values_to_node(left_only_values); - let mut right_only_nodes = values_to_node(right_only_values); - let mut diff = KeyNode::Nil; - - for (o, ol, n, nl) in replaced { - let max_length = ol.max(nl); - for i in 0..max_length { - let inner_a = a.get(o + i).unwrap_or(&Value::Null); - let inner_b = b.get(n + i).unwrap_or(&Value::Null); - - let cdiff = match_json(inner_a, inner_b, sort_arrays, ignore_keys); - let position = o + i; - let Mismatch { - left_only_keys: l, - right_only_keys: r, - keys_in_both: u, - } = cdiff; - left_only_nodes = insert_child_key_diff(left_only_nodes, l, position); - right_only_nodes = insert_child_key_diff(right_only_nodes, r, position); - diff = insert_child_key_diff(diff, u, position); - } - } +fn process_objects( + a: &Map, + b: &Map, + ignore_keys: &[Regex], + sort_arrays: bool, +) -> Result { + let diff = intersect_maps(a, b, ignore_keys); + let mut left_only_keys = get_map_of_keys(diff.left_only); + let mut right_only_keys = get_map_of_keys(diff.right_only); + let intersection_keys = diff.intersection; + + let mut unequal_keys = KeyNode::Nil; + + for key in intersection_keys { + let Mismatch { + left_only_keys: l, + right_only_keys: r, + keys_in_both: u, + } = match_json( + a.get(&key).unwrap(), + b.get(&key).unwrap(), + sort_arrays, + ignore_keys, + )?; + left_only_keys = insert_child_key_map(left_only_keys, l, &key)?; + right_only_keys = insert_child_key_map(right_only_keys, r, &key)?; + unequal_keys = insert_child_key_map(unequal_keys, u, &key)?; + } + + Ok(Mismatch::new(left_only_keys, right_only_keys, unequal_keys)) +} - Mismatch::new(left_only_nodes, right_only_nodes, diff) - } - (a, b) => { - if a == b { - Mismatch::new(KeyNode::Nil, KeyNode::Nil, KeyNode::Nil) - } else { - Mismatch::new( - KeyNode::Nil, - KeyNode::Nil, - KeyNode::Value(a.clone(), b.clone()), - ) - } +fn process_arrays( + sort_arrays: bool, + a: &Vec, + ignore_keys: &[Regex], + b: &Vec, +) -> Result { + let a = preprocess_array(sort_arrays, a, ignore_keys); + let b = preprocess_array(sort_arrays, b, ignore_keys); + + let mut replaced = Vec::new(); + let mut deleted = Vec::new(); + let mut inserted = Vec::new(); + + let mut diff = Replace::new(ListDiffHandler::new( + &mut replaced, + &mut deleted, + &mut inserted, + )); + myers::diff( + &mut diff, + a.as_slice(), + 0, + a.len(), + b.as_slice(), + 0, + b.len(), + ) + .unwrap(); + + fn extract_one_sided_values(v: Vec<(usize, usize)>, vals: &[Value]) -> Vec<(usize, &Value)> { + v.into_iter() + .flat_map(|(o, ol)| (o..o + ol).map(|i| (i, &vals[i]))) + .collect::>() + } + + let left_only_values: Vec<_> = extract_one_sided_values(deleted, a.as_slice()); + let right_only_values: Vec<_> = extract_one_sided_values(inserted, b.as_slice()); + + let mut left_only_nodes = values_to_node(left_only_values); + let mut right_only_nodes = values_to_node(right_only_values); + let mut diff = KeyNode::Nil; + + for (o, ol, n, nl) in replaced { + let max_length = ol.max(nl); + for i in 0..max_length { + let inner_a = a.get(o + i).unwrap_or(&Value::Null); + let inner_b = b.get(n + i).unwrap_or(&Value::Null); + + let cdiff = match_json(inner_a, inner_b, sort_arrays, ignore_keys)?; + let position = o + i; + let Mismatch { + left_only_keys: l, + right_only_keys: r, + keys_in_both: u, + } = cdiff; + left_only_nodes = insert_child_key_diff(left_only_nodes, l, position)?; + right_only_nodes = insert_child_key_diff(right_only_nodes, r, position)?; + diff = insert_child_key_diff(diff, u, position)?; } } + + Ok(Mismatch::new(left_only_nodes, right_only_nodes, diff)) } fn preprocess_array<'a>( @@ -267,33 +280,33 @@ fn get_map_of_keys(set: HashSet) -> KeyNode { } } -fn insert_child_key_diff(parent: KeyNode, child: KeyNode, line: usize) -> KeyNode { +fn insert_child_key_diff(parent: KeyNode, child: KeyNode, line: usize) -> Result { if child == KeyNode::Nil { - return parent; + return Ok(parent); } if let KeyNode::Array(mut array) = parent { array.push((line, child)); - KeyNode::Array(array) + Ok(KeyNode::Array(array)) } else if let KeyNode::Nil = parent { - KeyNode::Array(vec![(line, child)]) + Ok(KeyNode::Array(vec![(line, child)])) } else { - parent // TODO Trying to insert child node in a Value variant : Should not happen => Throw an error instead. + Err(format!("Tried to insert child: {child:?} into parent {parent:?} - structure incoherent, expected a parent array - somehow json structure seems broken").into()) } } -fn insert_child_key_map(parent: KeyNode, child: KeyNode, key: &String) -> KeyNode { +fn insert_child_key_map(parent: KeyNode, child: KeyNode, key: &String) -> Result { if child == KeyNode::Nil { - return parent; + return Ok(parent); } if let KeyNode::Node(mut map) = parent { map.insert(String::from(key), child); - KeyNode::Node(map) + Ok(KeyNode::Node(map)) } else if let KeyNode::Nil = parent { let mut map = HashMap::new(); map.insert(String::from(key), child); - KeyNode::Node(map) + Ok(KeyNode::Node(map)) } else { - parent // TODO Trying to insert child node in a Value variant : Should not happen => Throw an error instead. + Err(format!("Tried to insert child: {child:?} into parent {parent:?} - structure incoherent, expected a parent object - somehow json structure seems broken").into()) } }