1use serde_json::Value;
2use std::fmt;
3
4#[derive(Debug, Clone, PartialEq)]
5pub enum DiffType {
6 Added,
7 Removed,
8 Modified,
9 Moved,
10}
11
12#[derive(Debug, Clone)]
13pub struct Diff {
14 pub path: String,
15 pub diff_type: DiffType,
16 pub old_value: Option<Value>,
17 pub new_value: Option<Value>,
18}
19
20impl fmt::Display for Diff {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match &self.diff_type {
23 DiffType::Added => {
24 write!(f, "Added at '{}': {:?}", self.path, self.new_value)
25 }
26 DiffType::Removed => {
27 write!(f, "Removed from '{}': {:?}", self.path, self.old_value)
28 }
29 DiffType::Modified => {
30 write!(
31 f,
32 "Modified at '{}': {:?} -> {:?}",
33 self.path, self.old_value, self.new_value
34 )
35 }
36 DiffType::Moved => {
37 write!(
38 f,
39 "Moved: {} -> {}",
40 self.path,
41 self.new_value.as_ref().unwrap()
42 )
43 }
44 }
45 }
46}
47
48pub struct JsonDiff {
49 ignore_order: bool,
50}
51
52impl JsonDiff {
53 pub fn new() -> Self {
54 Self {
55 ignore_order: false,
56 }
57 }
58
59 pub fn ignore_order(mut self, ignore: bool) -> Self {
60 self.ignore_order = ignore;
61 self
62 }
63
64 pub fn diff(&self, v1: &Value, v2: &Value) -> Vec<Diff> {
65 let mut diffs = Vec::new();
66 self.diff_values(v1, v2, "", &mut diffs);
67 diffs
68 }
69
70 fn diff_values(&self, v1: &Value, v2: &Value, path: &str, diffs: &mut Vec<Diff>) {
71 match (v1, v2) {
72 (Value::Null, Value::Null) => {}
73 (Value::Bool(b1), Value::Bool(b2)) if b1 == b2 => {}
74 (Value::Number(n1), Value::Number(n2)) if n1 == n2 => {}
75 (Value::String(s1), Value::String(s2)) if s1 == s2 => {}
76 (Value::Array(a1), Value::Array(a2)) => {
77 self.diff_arrays(a1, a2, path, diffs);
78 }
79 (Value::Object(o1), Value::Object(o2)) => {
80 self.diff_objects(o1, o2, path, diffs);
81 }
82 _ => {
83 diffs.push(Diff {
84 path: path.to_string(),
85 diff_type: DiffType::Modified,
86 old_value: Some(v1.clone()),
87 new_value: Some(v2.clone()),
88 });
89 }
90 }
91 }
92
93 fn diff_arrays(&self, a1: &[Value], a2: &[Value], path: &str, diffs: &mut Vec<Diff>) {
94 if self.ignore_order {
95 self.diff_arrays_ignore_order(a1, a2, path, diffs);
96 } else {
97 self.diff_arrays_preserve_order(a1, a2, path, diffs);
98 }
99 }
100
101 fn diff_arrays_preserve_order(
102 &self,
103 a1: &[Value],
104 a2: &[Value],
105 path: &str,
106 diffs: &mut Vec<Diff>,
107 ) {
108 let max_len = a1.len().max(a2.len());
109
110 for i in 0..max_len {
111 let new_path = if path.is_empty() {
112 format!("[{}]", i)
113 } else {
114 format!("{}[{}]", path, i)
115 };
116
117 match (a1.get(i), a2.get(i)) {
118 (Some(v1), Some(v2)) => {
119 self.diff_values(v1, v2, &new_path, diffs);
120 }
121 (Some(v1), None) => {
122 diffs.push(Diff {
123 path: new_path,
124 diff_type: DiffType::Removed,
125 old_value: Some(v1.clone()),
126 new_value: None,
127 });
128 }
129 (None, Some(v2)) => {
130 diffs.push(Diff {
131 path: new_path,
132 diff_type: DiffType::Added,
133 old_value: None,
134 new_value: Some(v2.clone()),
135 });
136 }
137 (None, None) => {}
138 }
139 }
140 }
141
142 fn diff_arrays_ignore_order(
143 &self,
144 a1: &[Value],
145 a2: &[Value],
146 path: &str,
147 diffs: &mut Vec<Diff>,
148 ) {
149 let mut unused1: Vec<bool> = vec![false; a1.len()];
150 let mut unused2: Vec<bool> = vec![false; a2.len()];
151
152 for (i, v1) in a1.iter().enumerate() {
153 let mut found = false;
154 for (j, v2) in a2.iter().enumerate() {
155 if !unused2[j] && self.values_equal(v1, v2) {
156 unused1[i] = true;
157 unused2[j] = true;
158 found = true;
159 break;
160 }
161 }
162
163 if !found {
164 let new_path = if path.is_empty() {
165 format!("[{}]", i)
166 } else {
167 format!("{}[{}]", path, i)
168 };
169
170 diffs.push(Diff {
171 path: new_path,
172 diff_type: DiffType::Removed,
173 old_value: Some(v1.clone()),
174 new_value: None,
175 });
176 }
177 }
178
179 for (j, v2) in a2.iter().enumerate() {
180 if !unused2[j] {
181 let new_path = if path.is_empty() {
182 format!("[{}]", j)
183 } else {
184 format!("{}[{}]", path, j)
185 };
186
187 diffs.push(Diff {
188 path: new_path,
189 diff_type: DiffType::Added,
190 old_value: None,
191 new_value: Some(v2.clone()),
192 });
193 }
194 }
195 }
196
197 fn diff_objects(
198 &self,
199 o1: &serde_json::Map<String, Value>,
200 o2: &serde_json::Map<String, Value>,
201 path: &str,
202 diffs: &mut Vec<Diff>,
203 ) {
204 let all_keys: std::collections::HashSet<&String> = o1.keys().chain(o2.keys()).collect();
205
206 for key in all_keys {
207 let new_path = if path.is_empty() {
208 key.clone()
209 } else {
210 format!("{}.{}", path, key)
211 };
212
213 match (o1.get(key), o2.get(key)) {
214 (Some(v1), Some(v2)) => {
215 self.diff_values(v1, v2, &new_path, diffs);
216 }
217 (Some(v1), None) => {
218 diffs.push(Diff {
219 path: new_path,
220 diff_type: DiffType::Removed,
221 old_value: Some(v1.clone()),
222 new_value: None,
223 });
224 }
225 (None, Some(v2)) => {
226 diffs.push(Diff {
227 path: new_path,
228 diff_type: DiffType::Added,
229 old_value: None,
230 new_value: Some(v2.clone()),
231 });
232 }
233 (None, None) => {}
234 }
235 }
236 }
237
238 fn values_equal(&self, v1: &Value, v2: &Value) -> bool {
239 match (v1, v2) {
240 (Value::Null, Value::Null) => true,
241 (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
242 (Value::Number(n1), Value::Number(n2)) => n1 == n2,
243 (Value::String(s1), Value::String(s2)) => s1 == s2,
244 (Value::Array(a1), Value::Array(a2)) => {
245 if a1.len() != a2.len() {
246 return false;
247 }
248 if self.ignore_order {
249 self.arrays_equal_ignore_order(a1, a2)
250 } else {
251 a1.iter()
252 .zip(a2.iter())
253 .all(|(v1, v2)| self.values_equal(v1, v2))
254 }
255 }
256 (Value::Object(o1), Value::Object(o2)) => {
257 if o1.len() != o2.len() {
258 return false;
259 }
260 o1.keys().all(|k| match (o1.get(k), o2.get(k)) {
261 (Some(v1), Some(v2)) => self.values_equal(v1, v2),
262 _ => false,
263 })
264 }
265 _ => false,
266 }
267 }
268
269 fn arrays_equal_ignore_order(&self, a1: &[Value], a2: &[Value]) -> bool {
270 if a1.len() != a2.len() {
271 return false;
272 }
273
274 let mut used = vec![false; a2.len()];
275
276 for v1 in a1 {
277 let mut found = false;
278 for (j, v2) in a2.iter().enumerate() {
279 if !used[j] && self.values_equal(v1, v2) {
280 used[j] = true;
281 found = true;
282 break;
283 }
284 }
285 if !found {
286 return false;
287 }
288 }
289
290 true
291 }
292}
293
294impl Default for JsonDiff {
295 fn default() -> Self {
296 Self::new()
297 }
298}