126 lines
4 KiB
Rust
126 lines
4 KiB
Rust
use std::fmt;
|
|
|
|
use console::{style, Style};
|
|
use similar::{Algorithm, ChangeTag, TextDiff};
|
|
|
|
pub struct Diff {
|
|
left: String,
|
|
right: String,
|
|
}
|
|
|
|
fn to_debug<D: fmt::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: fmt::Debug, Right: fmt::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("<left").red(),
|
|
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);
|
|
}
|