use std::path::Path;
use crate::error::CliError;
use crate::output;
use crate::output::diff_prefix;
use evorule_reactor::Fact;
pub fn run(a: &Path, b: &Path) -> Result<(), CliError> {
let facts_a = fact_log::read_facts(a)?;
let facts_b = fact_log::read_facts(b)?;
println!("=== Diff {} <-> {} ===", a.display(), b.display());
println!("A: {} facts", facts_a.len());
println!("B: {} facts", facts_b.len());
println!();
let differences = compare_facts(&facts_a, &facts_b);
if differences == 0 {
println!("(identical)");
} else {
println!();
println!("=== {} difference(s) ===", differences);
}
Ok(())
}
fn compare_facts(facts_a: &[Fact], facts_b: &[Fact]) -> usize {
let max_len = facts_a.len().max(facts_b.len());
let mut differences = 0;
for i in 0..max_len {
match (facts_a.get(i), facts_b.get(i)) {
(Some(fa), Some(fb)) => {
if fa != fb {
println!(
"{}",
output::format_diff_line(diff_prefix::CHANGED, &output::fact_to_human(fa))
);
println!(
"{}",
output::format_diff_line(diff_prefix::CHANGED, &output::fact_to_human(fb))
);
differences += 1;
}
}
(Some(fa), None) => {
println!(
"{}",
output::format_diff_line(diff_prefix::ONLY_A, &output::fact_to_human(fa))
);
differences += 1;
}
(None, Some(fb)) => {
println!(
"{}",
output::format_diff_line(diff_prefix::ONLY_B, &output::fact_to_human(fb))
);
differences += 1;
}
(None, None) => break,
}
}
differences
}
use crate::fact_log;
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::useless_vec)]
use evorule_tcb::JsonValue;
use evorule_reactor::{Fact, FactId};
#[test]
fn test_compare_identical() {
let facts = vec![Fact::Stable {
id: FactId(1),
final_snapshot: JsonValue::empty_object(),
}];
assert_eq!(facts.len(), 1);
}
#[test]
fn test_compare_different_lengths() {
let a = vec![
Fact::Command {
id: FactId(1),
instruction: JsonValue::empty_object(),
},
Fact::Stable {
id: FactId(2),
final_snapshot: JsonValue::empty_object(),
},
];
let b = vec![Fact::Command {
id: FactId(1),
instruction: JsonValue::empty_object(),
}];
assert!(a.len() > b.len());
}
}