evorule_cli/commands/
diff.rs1use std::path::Path;
19
20use crate::error::CliError;
21use crate::output;
22use crate::output::diff_prefix;
23use evorule_reactor::Fact;
24
25pub fn run(a: &Path, b: &Path) -> Result<(), CliError> {
27 let facts_a = fact_log::read_facts(a)?;
28 let facts_b = fact_log::read_facts(b)?;
29
30 println!("=== Diff {} <-> {} ===", a.display(), b.display());
31 println!("A: {} facts", facts_a.len());
32 println!("B: {} facts", facts_b.len());
33 println!();
34
35 let differences = compare_facts(&facts_a, &facts_b);
36
37 if differences == 0 {
38 println!("(identical)");
39 } else {
40 println!();
41 println!("=== {} difference(s) ===", differences);
42 }
43
44 Ok(())
45}
46
47fn compare_facts(facts_a: &[Fact], facts_b: &[Fact]) -> usize {
51 let max_len = facts_a.len().max(facts_b.len());
52 let mut differences = 0;
53
54 for i in 0..max_len {
55 match (facts_a.get(i), facts_b.get(i)) {
56 (Some(fa), Some(fb)) => {
57 if fa != fb {
58 println!(
59 "{}",
60 output::format_diff_line(diff_prefix::CHANGED, &output::fact_to_human(fa))
61 );
62 println!(
63 "{}",
64 output::format_diff_line(diff_prefix::CHANGED, &output::fact_to_human(fb))
65 );
66 differences += 1;
67 }
68 }
69 (Some(fa), None) => {
70 println!(
71 "{}",
72 output::format_diff_line(diff_prefix::ONLY_A, &output::fact_to_human(fa))
73 );
74 differences += 1;
75 }
76 (None, Some(fb)) => {
77 println!(
78 "{}",
79 output::format_diff_line(diff_prefix::ONLY_B, &output::fact_to_human(fb))
80 );
81 differences += 1;
82 }
83 (None, None) => break,
84 }
85 }
86
87 differences
88}
89
90use crate::fact_log;
92
93#[cfg(test)]
94mod tests {
95 #![allow(clippy::unwrap_used, clippy::useless_vec)]
96 use evorule_reactor::{Fact, FactId};
97 use evorule_tcb::JsonValue;
98
99 #[test]
100 fn test_compare_identical() {
101 let facts = vec![Fact::Stable {
102 id: FactId(1),
103 version: 1,
104 }];
105 assert_eq!(facts.len(), 1);
108 }
109
110 #[test]
111 fn test_compare_different_lengths() {
112 let a = vec![
113 Fact::Command {
114 id: FactId(1),
115 instruction: JsonValue::empty_object(),
116 },
117 Fact::Stable {
118 id: FactId(2),
119 version: 1,
120 },
121 ];
122 let b = vec![Fact::Command {
123 id: FactId(1),
124 instruction: JsonValue::empty_object(),
125 }];
126 assert!(a.len() > b.len());
128 }
129}