use std::fmt; use console::{style, Style}; use similar::{Algorithm, ChangeTag, TextDiff}; pub struct Diff { left: String, right: String, } fn to_debug(d: &D) -> String { use fmt::Write; let mut buf = String::new(); buf.write_fmt(format_args!("{:#?}", d)) .expect("a Debug implementation returned an error unexpectedly"); buf } impl Diff { pub fn from_debug(left: &Left, right: &Right) -> Diff { let left = to_debug(left).into(); let right = to_debug(right).into(); Diff { left, right } } } impl fmt::Display for Diff { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.left == self.right { writeln!( f, "{}: {}", style("Invisible differences").bold(), "the two values are the same in string form." )?; return Ok(()); } let diff = TextDiff::configure() .algorithm(Algorithm::Patience) .diff_lines(&self.left, &self.right); writeln!( f, " {} ({}|{}):", style("Differences").bold(), style("right").green(), )?; for (idx, group) in diff.grouped_ops(4).into_iter().enumerate() { if idx > 0 { writeln!(f, " ~~~")?; } for op in group { for change in diff.iter_inline_changes(&op) { let (marker, style) = match change.tag() { ChangeTag::Delete => ('<', Style::new().red()), ChangeTag::Insert => ('>', Style::new().green()), ChangeTag::Equal => (' ', Style::new().dim()), }; write!(f, " {}", style.apply_to(marker).dim().bold())?; #[cfg(feature = "inline")] { for &(emphasized, value) in change.values() { if emphasized { write!(f, "{}", style.clone().underlined().bold().apply_to(value))?; } else { write!(f, "{}", style.apply_to(value))?; } } } #[cfg(not(feature = "inline"))] { write!(f, "{}", style.apply_to(change.value()))?; } if change.missing_newline() { writeln!(f)?; } } } } Ok(()) } } #[macro_export] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => ({ match (&($left), &($right)) { (left_val, right_val) => { if !(*left_val == *right_val) { panic!("assertion failed: `(left == right)`'\ \n left: `{:?}`\ \n right: `{:?}`\ \n\n{}\n", &*left_val, &*right_val, $crate::Diff::from_debug(&*left_val, &*right_val)); } } } }); ($left:expr, $right:expr, $($arg:tt)*) => ({ match (&($left), &($right)) { (left_val, right_val) => { if !(*left_val == *right_val) { panic!("assertion failed: `(left == right)`: {}'\ \n left: `{:?}`\ \n right: `{:?}`\ \n\n{}\n", format_args!($($arg)*), &*left_val, &*right_val, $crate::Diff::from_debug(&*left_val, &*right_val)); } } } }); } fn main() { let vec1: Vec<_> = (1..100).collect(); let mut vec2 = vec1.clone(); vec2[52] = 2; vec2[12] = 33; assert_eq!(vec1, vec2); }