diff --git a/Cargo.toml b/Cargo.toml index b990bab..ef5e04a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,10 @@ console = "0.14.0" unicode-segmentation = { version = "1.7.1", optional = true } bstr = { version = "0.2.14", optional = true, default-features = false } +[[example]] +name = "assert" +required-features = ["text", "inline"] + [[example]] name = "terminal" required-features = ["text"] diff --git a/examples/assert.rs b/examples/assert.rs new file mode 100644 index 0000000..d7b78d6 --- /dev/null +++ b/examples/assert.rs @@ -0,0 +1,126 @@ +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); +}