1use serde::Serialize;
2use serde_json::Value;
3use regex::Regex;
4use std::collections::HashMap;
5use anyhow::{Result, anyhow};
7use quick_xml::de::from_str;
8use csv::ReaderBuilder;
9
10#[derive(Debug, PartialEq, Serialize)]
11pub enum DiffResult {
12 Added(String, Value),
13 Removed(String, Value),
14 Modified(String, Value, Value),
15 TypeChanged(String, Value, Value),
16}
17
18pub fn diff(
19 v1: &Value,
20 v2: &Value,
21 ignore_keys_regex: Option<&Regex>,
22 epsilon: Option<f64>,
23 array_id_key: Option<&str>,
24) -> Vec<DiffResult> {
25 let mut results = Vec::new();
26
27 if !values_are_equal(v1, v2, epsilon) {
29 let type_match = match (v1, v2) {
30 (Value::Null, Value::Null) => true,
31 (Value::Bool(_), Value::Bool(_)) => true,
32 (Value::Number(_), Value::Number(_)) => true,
33 (Value::String(_), Value::String(_)) => true,
34 (Value::Array(_), Value::Array(_)) => true,
35 (Value::Object(_), Value::Object(_)) => true,
36 _ => false,
37 };
38
39 if !type_match {
40 results.push(DiffResult::TypeChanged("".to_string(), v1.clone(), v2.clone()));
41 return results; } else if v1.is_object() && v2.is_object() {
43 diff_objects("", v1.as_object().unwrap(), v2.as_object().unwrap(), &mut results, ignore_keys_regex, epsilon, array_id_key);
44 } else if v1.is_array() && v2.is_array() {
45 diff_arrays("", v1.as_array().unwrap(), v2.as_array().unwrap(), &mut results, ignore_keys_regex, epsilon, array_id_key);
46 } else {
47 results.push(DiffResult::Modified("".to_string(), v1.clone(), v2.clone()));
49 return results;
50 }
51 }
52
53 results
54}
55
56fn diff_recursive(
57 path: &str,
58 v1: &Value,
59 v2: &Value,
60 results: &mut Vec<DiffResult>,
61 ignore_keys_regex: Option<&Regex>,
62 epsilon: Option<f64>,
63 array_id_key: Option<&str>,
64) {
65 match (v1, v2) {
66 (Value::Object(map1), Value::Object(map2)) => {
67 diff_objects(path, map1, map2, results, ignore_keys_regex, epsilon, array_id_key);
68 }
69 (Value::Array(arr1), Value::Array(arr2)) => {
70 diff_arrays(path, arr1, arr2, results, ignore_keys_regex, epsilon, array_id_key);
71 }
72 _ => { }
73 }
74}
75
76fn diff_objects(
77 path: &str,
78 map1: &serde_json::Map<String, Value>,
79 map2: &serde_json::Map<String, Value>,
80 results: &mut Vec<DiffResult>,
81 ignore_keys_regex: Option<&Regex>,
82 epsilon: Option<f64>,
83 array_id_key: Option<&str>,
84) {
85 for (key, value1) in map1 {
87 let current_path = if path.is_empty() { key.clone() } else { format!("{}.{}", path, key) };
88 if let Some(regex) = ignore_keys_regex {
89 if regex.is_match(key) {
90 continue;
91 }
92 }
93 match map2.get(key) {
94 Some(value2) => {
95 if value1.is_object() && value2.is_object() || value1.is_array() && value2.is_array() {
97 diff_recursive(¤t_path, value1, value2, results, ignore_keys_regex, epsilon, array_id_key);
98 } else if !values_are_equal(value1, value2, epsilon) {
99 let type_match = match (value1, value2) {
100 (Value::Null, Value::Null) => true,
101 (Value::Bool(_), Value::Bool(_)) => true,
102 (Value::Number(_), Value::Number(_)) => true,
103 (Value::String(_), Value::String(_)) => true,
104 (Value::Array(_), Value::Array(_)) => true,
105 (Value::Object(_), Value::Object(_)) => true,
106 _ => false,
107 };
108
109 if !type_match {
110 results.push(DiffResult::TypeChanged(current_path, value1.clone(), value2.clone()));
111 } else {
112 results.push(DiffResult::Modified(current_path, value1.clone(), value2.clone()));
113 }
114 }
115 }
116 None => {
117 results.push(DiffResult::Removed(current_path, value1.clone()));
118 }
119 }
120 }
121
122 for (key, value2) in map2 {
124 if !map1.contains_key(key) {
125 let current_path = if path.is_empty() { key.clone() } else { format!("{}.{}", path, key) };
126 results.push(DiffResult::Added(current_path, value2.clone()));
127 }
128 }
129}
130
131fn diff_arrays(
132 path: &str,
133 arr1: &Vec<Value>,
134 arr2: &Vec<Value>,
135 results: &mut Vec<DiffResult>,
136 ignore_keys_regex: Option<&Regex>,
137 epsilon: Option<f64>,
138 array_id_key: Option<&str>,
139) {
140 if let Some(id_key) = array_id_key {
141 let mut map1: HashMap<Value, &Value> = HashMap::new();
142 let mut no_id_elements1: Vec<(usize, &Value)> = Vec::new();
143 for (i, val) in arr1.iter().enumerate() {
144 if let Some(id_val) = val.get(id_key) {
145 map1.insert(id_val.clone(), val);
146 } else {
147 no_id_elements1.push((i, val));
148 }
149 }
150
151 let mut map2: HashMap<Value, &Value> = HashMap::new();
152 let mut no_id_elements2: Vec<(usize, &Value)> = Vec::new();
153 for (i, val) in arr2.iter().enumerate() {
154 if let Some(id_val) = val.get(id_key) {
155 map2.insert(id_val.clone(), val);
156 } else {
157 no_id_elements2.push((i, val));
158 }
159 }
160
161 for (id_val, val1) in &map1 {
163 let current_path = format!("{}[{}={}]", path, id_key, id_val);
164 match map2.get(&id_val) {
165 Some(val2) => {
166 if val1.is_object() && val2.is_object() || val1.is_array() && val2.is_array() {
168 diff_recursive(¤t_path, val1, val2, results, ignore_keys_regex, epsilon, array_id_key);
169 } else if !values_are_equal(val1, val2, epsilon) {
170 let type_match = match (val1, val2) {
171 (Value::Null, Value::Null) => true,
172 (Value::Bool(_), Value::Bool(_)) => true,
173 (Value::Number(_), Value::Number(_)) => true,
174 (Value::String(_), Value::String(_)) => true,
175 (Value::Array(_), Value::Array(_)) => true,
176 (Value::Object(_), Value::Object(_)) => true,
177 _ => false,
178 };
179
180 if !type_match {
181 results.push(DiffResult::TypeChanged(current_path, (*val1).clone(), (*val2).clone()));
182 } else {
183 results.push(DiffResult::Modified(current_path, (*val1).clone(), (*val2).clone()));
184 }
185 }
186 }
187 None => {
188 results.push(DiffResult::Removed(current_path, (*val1).clone()));
189 }
190 }
191 }
192
193 for (id_val, val2) in map2 {
195 if !map1.contains_key(&id_val) {
196 let current_path = format!("{}[{}={}]", path, id_key, id_val);
197 results.push(DiffResult::Added(current_path, val2.clone()));
198 }
199 }
200
201 let max_len = no_id_elements1.len().max(no_id_elements2.len());
203 for i in 0..max_len {
204 match (no_id_elements1.get(i), no_id_elements2.get(i)) {
205 (Some((idx1, val1)), Some((_idx2, val2))) => {
206 let current_path = format!("{}[{}]", path, idx1);
207 if val1.is_object() && val2.is_object() || val1.is_array() && val2.is_array() {
208 diff_recursive(¤t_path, val1, val2, results, ignore_keys_regex, epsilon, array_id_key);
209 } else if !values_are_equal(val1, val2, epsilon) {
210 let type_match = match (val1, val2) {
211 (Value::Null, Value::Null) => true,
212 (Value::Bool(_), Value::Bool(_)) => true,
213 (Value::Number(_), Value::Number(_)) => true,
214 (Value::String(_), Value::String(_)) => true,
215 (Value::Array(_), Value::Array(_)) => true,
216 (Value::Object(_), Value::Object(_)) => true,
217 _ => false,
218 };
219
220 if !type_match {
221 results.push(DiffResult::TypeChanged(current_path, (*val1).clone(), (*val2).clone()));
222 } else {
223 results.push(DiffResult::Modified(current_path, (*val1).clone(), (*val2).clone()));
224 }
225 }
226 }
227 (Some((idx1, val1)), None) => {
228 let current_path = format!("{}[{}]", path, idx1);
229 results.push(DiffResult::Removed(current_path, (*val1).clone()));
230 }
231 (None, Some((idx2, val2))) => {
232 let current_path = format!("{}[{}]", path, idx2);
233 results.push(DiffResult::Added(current_path, (*val2).clone()));
234 }
235 (None, None) => break,
236 }
237 }
238 } else {
239 let max_len = arr1.len().max(arr2.len());
241 for i in 0..max_len {
242 let current_path = format!("{}[{}]", path, i);
243 match (arr1.get(i), arr2.get(i)) {
244 (Some(val1), Some(val2)) => {
245 if val1.is_object() && val2.is_object() || val1.is_array() && val2.is_array() {
247 diff_recursive(¤t_path, val1, val2, results, ignore_keys_regex, epsilon, array_id_key);
248 } else if !values_are_equal(val1, val2, epsilon) {
249 let type_match = match (val1, val2) {
250 (Value::Null, Value::Null) => true,
251 (Value::Bool(_), Value::Bool(_)) => true,
252 (Value::Number(_), Value::Number(_)) => true,
253 (Value::String(_), Value::String(_)) => true,
254 (Value::Array(_), Value::Array(_)) => true,
255 (Value::Object(_), Value::Object(_)) => true,
256 _ => false,
257 };
258
259 if !type_match {
260 results.push(DiffResult::TypeChanged(current_path, val1.clone(), val2.clone()));
261 } else {
262 results.push(DiffResult::Modified(current_path, val1.clone(), val2.clone()));
263 }
264 }
265 }
266 (Some(val1), None) => {
267 results.push(DiffResult::Removed(current_path, val1.clone()));
268 }
269 (None, Some(val2)) => {
270 results.push(DiffResult::Added(current_path, val2.clone()));
271 }
272 (None, None) => { }
273 }
274 }
275 }
276}
277
278fn values_are_equal(v1: &Value, v2: &Value, epsilon: Option<f64>) -> bool {
279 if let (Some(e), Value::Number(n1), Value::Number(n2)) = (epsilon, v1, v2) {
280 if let (Some(f1), Some(f2)) = (n1.as_f64(), n2.as_f64()) {
281 return (f1 - f2).abs() < e;
282 }
283 }
284 v1 == v2
285}
286
287pub fn value_type_name(value: &Value) -> &str {
288 match value {
289 Value::Null => "Null",
290 Value::Bool(_) => "Boolean",
291 Value::Number(_) => "Number",
292 Value::String(_) => "String",
293 Value::Array(_) => "Array",
294 Value::Object(_) => "Object",
295 }
296}
297
298pub fn parse_ini(content: &str) -> Result<Value> {
299 use configparser::ini::Ini;
300
301 let mut ini = Ini::new();
302 ini.read(content.to_string())
303 .map_err(|e| anyhow!("Failed to parse INI: {}", e))?;
304
305 let mut root_map = serde_json::Map::new();
306
307 for section_name in ini.sections() {
308 let mut section_map = serde_json::Map::new();
309
310 if let Some(section) = ini.get_map_ref().get(§ion_name) {
311 for (key, value) in section {
312 if let Some(v) = value {
313 section_map.insert(key.clone(), Value::String(v.clone()));
314 } else {
315 section_map.insert(key.clone(), Value::Null);
316 }
317 }
318 }
319
320 root_map.insert(section_name, Value::Object(section_map));
321 }
322
323 Ok(Value::Object(root_map))
324}
325
326pub fn parse_xml(content: &str) -> Result<Value> {
327 let value: Value = from_str(content)?;
328 Ok(value)
329}
330
331pub fn parse_csv(content: &str) -> Result<Value> {
332 let mut reader = ReaderBuilder::new().from_reader(content.as_bytes());
333 let mut records = Vec::new();
334
335 let headers = reader.headers()?.clone();
336 let has_headers = !headers.is_empty();
337
338 for result in reader.into_records() {
339 let record = result?;
340 if has_headers {
341 let mut obj = serde_json::Map::new();
342 for (i, header) in headers.iter().enumerate() {
343 if let Some(value) = record.get(i) {
344 obj.insert(header.to_string(), Value::String(value.to_string()));
345 }
346 }
347 records.push(Value::Object(obj));
348 } else {
349 let mut arr = Vec::new();
350 for field in record.iter() {
351 arr.push(Value::String(field.to_string()));
352 }
353 records.push(Value::Array(arr));
354 }
355 }
356 Ok(Value::Array(records))
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use serde_json::json;
363
364 #[test]
365 fn test_diff_no_changes() {
366 let v1 = json!({ "a": 1, "b": 2 });
367 let v2 = json!({ "a": 1, "b": 2 });
368 let differences = diff(&v1, &v2, None, None, None);
369 assert!(differences.is_empty());
370 }
371
372 #[test]
373 fn test_diff_value_modified() {
374 let v1 = json!({ "a": 1, "b": 2 });
375 let v2 = json!({ "a": 1, "b": 3 });
376 let differences = diff(&v1, &v2, None, None, None);
377 assert_eq!(differences.len(), 1);
378 assert_eq!(differences[0], DiffResult::Modified("b".to_string(), json!(2), json!(3)));
379 }
380
381 #[test]
382 fn test_diff_key_added() {
383 let v1 = json!({ "a": 1 });
384 let v2 = json!({ "a": 1, "b": 2 });
385 let differences = diff(&v1, &v2, None, None, None);
386 assert_eq!(differences.len(), 1);
387 assert_eq!(differences[0], DiffResult::Added("b".to_string(), json!(2)));
388 }
389
390 #[test]
391 fn test_diff_key_removed() {
392 let v1 = json!({ "a": 1, "b": 2 });
393 let v2 = json!({ "a": 1 });
394 let differences = diff(&v1, &v2, None, None, None);
395 assert_eq!(differences.len(), 1);
396 assert_eq!(differences[0], DiffResult::Removed("b".to_string(), json!(2)));
397 }
398
399 #[test]
400 fn test_diff_type_changed() {
401 let v1 = json!({ "a": 1 });
402 let v2 = json!({ "a": "1" });
403 let differences = diff(&v1, &v2, None, None, None);
404 assert_eq!(differences.len(), 1);
405 assert_eq!(differences[0], DiffResult::TypeChanged("a".to_string(), json!(1), json!("1")));
406 }
407
408 #[test]
409 fn test_diff_nested_object_modified() {
410 let v1 = json!({ "a": { "b": 1 } });
411 let v2 = json!({ "a": { "b": 2 } });
412 let differences = diff(&v1, &v2, None, None, None);
413 assert_eq!(differences.len(), 1);
414 assert_eq!(differences[0], DiffResult::Modified("a.b".to_string(), json!(1), json!(2)));
415 }
416
417 #[test]
418 fn test_diff_array_element_added() {
419 let v1 = json!([1, 2]);
420 let v2 = json!([1, 2, 3]);
421 let differences = diff(&v1, &v2, None, None, None);
422 assert_eq!(differences.len(), 1);
423 assert_eq!(differences[0], DiffResult::Added("[2]".to_string(), json!(3)));
424 }
425
426 #[test]
427 fn test_diff_array_element_removed() {
428 let v1 = json!([1, 2, 3]);
429 let v2 = json!([1, 2]);
430 let differences = diff(&v1, &v2, None, None, None);
431 assert_eq!(differences.len(), 1);
432 assert_eq!(differences[0], DiffResult::Removed("[2]".to_string(), json!(3)));
433 }
434
435 #[test]
436 fn test_diff_array_element_modified() {
437 let v1 = json!([1, 2, 3]);
438 let v2 = json!([1, 2, 4]);
439 let differences = diff(&v1, &v2, None, None, None);
440 assert_eq!(differences.len(), 1);
441 assert_eq!(differences[0], DiffResult::Modified("[2]".to_string(), json!(3), json!(4)));
442 }
443
444 #[test]
445 fn test_diff_nested_array_element_modified() {
446 let v1 = json!({ "a": [1, 2, 3] });
447 let v2 = json!({ "a": [1, 2, 4] });
448 let differences = diff(&v1, &v2, None, None, None);
449 assert_eq!(differences.len(), 1);
450 assert_eq!(differences[0], DiffResult::Modified("a[2]".to_string(), json!(3), json!(4)));
451 }
452
453 #[test]
454 fn test_diff_root_type_changed() {
455 let v1 = json!(1);
456 let v2 = json!("1");
457 let differences = diff(&v1, &v2, None, None, None);
458 assert_eq!(differences.len(), 1);
459 assert_eq!(differences[0], DiffResult::TypeChanged("".to_string(), json!(1), json!("1")));
460 }
461
462 #[test]
463 fn test_diff_nested_object_and_array() {
464 let v1 = json!({
465 "config": {
466 "users": [
467 {"id": 1, "name": "Alice"},
468 {"id": 2, "name": "Bob"}
469 ],
470 "settings": {"theme": "dark"}
471 }
472 });
473 let v2 = json!({
474 "config": {
475 "users": [
476 {"id": 1, "name": "Alice"},
477 {"id": 2, "name": "Robert"},
478 {"id": 3, "name": "Charlie"}
479 ],
480 "settings": {"theme": "light", "font_size": 12}
481 }
482 });
483 let differences = diff(&v1, &v2, None, None, None);
484 assert_eq!(differences.len(), 4);
485 assert!(differences.contains(&DiffResult::Modified("config.users[1].name".to_string(), json!("Bob"), json!("Robert"))));
486 assert!(differences.contains(&DiffResult::Added("config.users[2]".to_string(), json!({"id": 3, "name": "Charlie"}))));
487 assert!(differences.contains(&DiffResult::Modified("config.settings.theme".to_string(), json!("dark"), json!("light"))));
488 assert!(differences.contains(&DiffResult::Added("config.settings.font_size".to_string(), json!(12))));
489 }
490
491 #[test]
492 fn test_diff_empty_objects_and_arrays() {
493 let v1 = json!({
494 "empty_obj": {},
495 "empty_arr": [],
496 "data": "value"
497 });
498 let v2 = json!({
499 "empty_obj": {},
500 "empty_arr": [],
501 "data": "new_value"
502 });
503 let differences = diff(&v1, &v2, None, None, None);
504 assert_eq!(differences.len(), 1);
505 assert_eq!(differences[0], DiffResult::Modified("data".to_string(), json!("value"), json!("new_value")));
506 }
507
508 #[test]
509 fn test_diff_root_array_changes() {
510 let v1 = json!([
511 {"id": 1},
512 {"id": 2}
513 ]);
514 let v2 = json!([
515 {"id": 1},
516 {"id": 3},
517 {"id": 4}
518 ]);
519 let differences = diff(&v1, &v2, None, None, None);
520 assert_eq!(differences.len(), 2);
521 assert!(differences.contains(&DiffResult::Modified("[1].id".to_string(), json!(2), json!(3))));
522 assert!(differences.contains(&DiffResult::Added("[2]".to_string(), json!({"id": 4}))));
523 }
524
525 #[test]
526 fn test_diff_ignore_keys_regex() {
527 let v1 = json!({ "id": 1, "name": "Alice", "_timestamp": "abc" });
528 let v2 = json!({ "id": 2, "name": "Alice", "_timestamp": "def" });
529 let regex = Regex::new(r"^_.*").unwrap();
530 let differences = diff(&v1, &v2, Some(®ex), None, None);
531 assert_eq!(differences.len(), 1);
532 assert!(differences.contains(&DiffResult::Modified("id".to_string(), json!(1), json!(2))));
533
534 let v3 = json!({ "id": 1, "name": "Alice", "version": "1.0" });
535 let v4 = json!({ "id": 1, "name": "Bob", "version": "1.1" });
536 let regex_name = Regex::new(r"^name$").unwrap();
537 let differences_name = diff(&v3, &v4, Some(®ex_name), None, None);
538 assert_eq!(differences_name.len(), 1);
539 assert!(differences_name.contains(&DiffResult::Modified("version".to_string(), json!("1.0"), json!("1.1"))));
540 }
541
542 #[test]
543 fn test_diff_ignore_keys_regex_nested() {
544 let v1 = json!({ "data": { "id": 1, "_timestamp": "abc" } });
545 let v2 = json!({ "data": { "id": 2, "_timestamp": "def" } });
546 let regex = Regex::new(r"^_.*").unwrap();
547 let differences = diff(&v1, &v2, Some(®ex), None, None);
548 assert_eq!(differences.len(), 1);
549 assert!(differences.contains(&DiffResult::Modified("data.id".to_string(), json!(1), json!(2))));
550 }
551
552 #[test]
553 fn test_diff_epsilon_comparison() {
554 let v1 = json!({ "a": 1.0, "b": 2.000001 });
555 let v2 = json!({ "a": 1.0, "b": 2.000002 });
556 let epsilon = Some(0.00001);
557 let differences = diff(&v1, &v2, None, epsilon, None);
558 assert!(differences.is_empty());
559
560 let v3 = json!({ "a": 1.0, "b": 2.00001 });
561 let v4 = json!({ "a": 1.0, "b": 2.00003 });
562 let epsilon_large = Some(0.00001);
563 let differences_large = diff(&v3, &v4, None, epsilon_large, None);
564 assert_eq!(differences_large.len(), 1);
565 assert_eq!(differences_large[0], DiffResult::Modified("b".to_string(), json!(2.00001), json!(2.00003)));
566 }
567
568 #[test]
569 fn test_diff_epsilon_comparison_type_mismatch() {
570 let v1 = json!({ "a": 1.0 });
571 let v2 = json!({ "a": "1.0" });
572 let epsilon = Some(0.00001);
573 let differences = diff(&v1, &v2, None, epsilon, None);
574 assert_eq!(differences.len(), 1);
575 assert_eq!(differences[0], DiffResult::TypeChanged("a".to_string(), json!(1.0), json!("1.0")));
576 }
577
578 #[test]
579 fn test_diff_array_id_key_modified() {
580 let v1 = json!([
581 {"id": 1, "value": "a"},
582 {"id": 2, "value": "b"}
583 ]);
584 let v2 = json!([
585 {"id": 2, "value": "c"},
586 {"id": 1, "value": "a"}
587 ]);
588 let differences = diff(&v1, &v2, None, None, Some("id"));
589 assert_eq!(differences.len(), 1);
590 assert!(differences.contains(&DiffResult::Modified("[id=2].value".to_string(), json!("b"), json!("c"))));
591 }
592
593 #[test]
594 fn test_diff_array_id_key_added_removed() {
595 let v1 = json!([
596 {"id": 1, "value": "a"},
597 {"id": 2, "value": "b"}
598 ]);
599 let v2 = json!([
600 {"id": 1, "value": "a"},
601 {"id": 3, "value": "c"}
602 ]);
603 let differences = diff(&v1, &v2, None, None, Some("id"));
604 assert_eq!(differences.len(), 2);
605 assert!(differences.contains(&DiffResult::Removed("[id=2]".to_string(), json!({"id": 2, "value": "b"}))));
606 assert!(differences.contains(&DiffResult::Added("[id=3]".to_string(), json!({"id": 3, "value": "c"}))));
607 }
608
609 #[test]
610 fn test_diff_array_id_key_nested_change() {
611 let v1 = json!([
612 {"id": 1, "data": {"name": "A"}},
613 {"id": 2, "data": {"name": "B"}}
614 ]);
615 let v2 = json!([
616 {"id": 2, "data": {"name": "C"}},
617 {"id": 1, "data": {"name": "A"}}
618 ]);
619 let differences = diff(&v1, &v2, None, None, Some("id"));
620 assert_eq!(differences.len(), 1);
621 assert!(differences.contains(&DiffResult::Modified("[id=2].data.name".to_string(), json!("B"), json!("C"))));
622 }
623
624 #[test]
625 fn test_diff_array_id_key_no_id_in_element() {
626 let v1 = json!([
627 {"id": 1, "value": "a"},
628 {"value": "b"}
629 ]);
630 let v2 = json!([
631 {"id": 1, "value": "a"},
632 {"value": "c"}
633 ]);
634 let differences = diff(&v1, &v2, None, None, Some("id"));
636 assert_eq!(differences.len(), 1);
637 assert!(differences.contains(&DiffResult::Modified("[1].value".to_string(), json!("b"), json!("c"))));
638 }
639
640 #[test]
641 fn test_diff_array_id_key_with_epsilon() {
642 let v1 = json!([
643 {"id": 1, "value": 1.000001},
644 {"id": 2, "value": 2.0}
645 ]);
646 let v2 = json!([
647 {"id": 1, "value": 1.000002},
648 {"id": 2, "value": 2.0}
649 ]);
650 let epsilon = Some(0.00001);
651 let differences = diff(&v1, &v2, None, epsilon, Some("id"));
652 assert!(differences.is_empty());
653 }
654
655 #[test]
656 fn test_parse_ini() {
657 let ini_content = r#"
658[section1]
659key1 = value1
660key2 = value2
661
662[section2]
663key3 = value3
664"#;
665 let expected = json!({
666 "section1": {
667 "key1": "value1",
668 "key2": "value2"
669 },
670 "section2": {
671 "key3": "value3"
672 }
673 });
674 let parsed = parse_ini(ini_content).unwrap();
675 assert_eq!(parsed, expected);
676 }
677
678 #[test]
679 fn test_parse_ini_global_section() {
680 let ini_content = r#"
681key_global = value_global
682[section1]
683key1 = value1
684"#;
685 let expected = json!({
686 "key_global": "value_global",
687 "section1": {
688 "key1": "value1"
689 }
690 });
691 let parsed = parse_ini(ini_content).unwrap();
692 assert_eq!(parsed, expected);
693 }
694
695 #[test]
696 fn test_parse_xml() {
697 let xml_content = r#"
698<root>
699 <item id="1">value1</item>
700 <item id="2">value2</item>
701</root>
702"#;
703 let expected = json!({
704 "root": {
705 "item": [
706 {
707 "#text": "value1",
708 "@id": "1"
709 },
710 {
711 "#text": "value2",
712 "@id": "2"
713 }
714 ]
715 }
716 });
717 let parsed = parse_xml(xml_content).unwrap();
718 assert_eq!(parsed, expected);
719 }
720
721 #[test]
722 fn test_parse_xml_single_element() {
723 let xml_content = r#"
724<data>
725 <name>test</name>
726</data>
727"#;
728 let expected = json!({
729 "data": {
730 "name": "test"
731 }
732 });
733 let parsed = parse_xml(xml_content).unwrap();
734 assert_eq!(parsed, expected);
735 }
736
737 #[test]
738 fn test_parse_csv_with_headers() {
739 let csv_content = "header1,header2\nvalueA,valueB\nvalueC,valueD";
740 let expected = json!([
741 {"header1": "valueA", "header2": "valueB"},
742 {"header1": "valueC", "header2": "valueD"}
743 ]);
744 let parsed = parse_csv(csv_content).unwrap();
745 assert_eq!(parsed, expected);
746 }
747
748 #[test]
749 fn test_parse_csv_no_headers() {
750 let csv_content = "valueA,valueB\nvalueC,valueD";
751 let expected = json!([
752 ["valueA", "valueB"],
753 ["valueC", "valueD"]
754 ]);
755 let parsed = parse_csv(csv_content).unwrap();
756 assert_eq!(parsed, expected);
757 }
758}