use core::fmt;
use yansi::Color::Green;
use yansi::Color::Red;
use yansi::Style;
const SIGN_LEFT: &str = "Expected:\n";
const SIGN_RIGHT: &str = "Received:\n";
struct InlineWriter<'a, Writer> {
f: &'a mut Writer,
style: Style,
}
impl<'a, Writer> InlineWriter<'a, Writer>
where
Writer: fmt::Write,
{
fn new(f: &'a mut Writer) -> Self {
InlineWriter {
f,
style: Style::new(),
}
}
fn write_with_style<T: Into<Style>>(
&mut self,
c: &impl fmt::Display,
style: T,
) -> fmt::Result {
let style = style.into();
if style == self.style {
write!(self.f, "{}", c)?;
} else {
self.style.fmt_suffix(self.f)?;
style.fmt_prefix(self.f)?;
write!(self.f, "{}", c)?;
self.style = style;
}
Ok(())
}
fn finish(&mut self) -> fmt::Result {
self.style.fmt_suffix(self.f)?;
writeln!(self.f)?;
self.style = Style::new();
Ok(())
}
}
pub fn inline_diff(expected: &str, received: &str) -> String {
let mut output = String::new();
write_inline_diff(&mut output, expected, received)
.expect("inline diff failed");
output
}
fn write_inline_diff<TWrite: fmt::Write>(
f: &mut TWrite,
left: &str,
right: &str,
) -> fmt::Result {
let diff = ::diff::chars(left, right);
let mut writer = InlineWriter::new(f);
let light = Green;
let green_background = 22;
let heavy = Green.on_fixed(green_background).bold();
write!(writer.f, "{SIGN_LEFT}\n")?;
for change in diff.iter() {
match change {
::diff::Result::Both(value, _) => {
writer.write_with_style(value, light)?
}
::diff::Result::Left(value) => {
writer.write_with_style(value, heavy)?
}
_ => (),
}
}
writer.finish()?;
let light = Red;
let red_background = 52;
let heavy = Red.on_fixed(red_background).bold();
write!(writer.f, "\n{SIGN_RIGHT}\n")?;
for change in diff.iter() {
match change {
::diff::Result::Both(value, _) => {
writer.write_with_style(value, light)?
}
::diff::Result::Right(value) => {
writer.write_with_style(value, heavy)?
}
_ => (),
}
}
writer.finish()
}
#[cfg(test)]
mod test {
use super::*;
const RED_LIGHT: &str = "\u{1b}[31m";
const GREEN_LIGHT: &str = "\u{1b}[32m";
const RED_HEAVY: &str = "\u{1b}[1;48;5;52;31m";
const GREEN_HEAVY: &str = "\u{1b}[1;48;5;22;32m";
const RESET: &str = "\u{1b}[0m";
fn check_printer<TPrint>(
printer: TPrint,
left: &str,
right: &str,
expected: &str,
) where
TPrint: Fn(&mut String, &str, &str) -> fmt::Result,
{
let mut actual = String::new();
printer(&mut actual, left, right).expect("printer function failed");
assert_eq!(actual, expected);
}
#[test]
fn write_inline_diff_empty() {
let left = "";
let right = "";
let expected = format!("{SIGN_LEFT}\n\n\n{SIGN_RIGHT}\n\n");
check_printer(write_inline_diff, left, right, &expected);
}
#[test]
fn write_inline_diff_added() {
let left = "";
let right = "polymerase";
let expected = format!(
"{SIGN_LEFT}\n\n\n{SIGN_RIGHT}\n{red_heavy}polymerase{reset}\n",
red_heavy = RED_HEAVY,
reset = RESET,
);
check_printer(write_inline_diff, left, right, &expected);
}
#[test]
fn write_inline_diff_removed() {
let left = "polyacrylamide";
let right = "";
let expected = format!(
"{SIGN_LEFT}\n{green_heavy}polyacrylamide{reset}\n\n{SIGN_RIGHT}\n\n",
green_heavy = GREEN_HEAVY,
reset = RESET,
);
check_printer(write_inline_diff, left, right, &expected);
}
#[test]
fn write_inline_diff_changed() {
let left = "polymerase";
let right = "polyacrylamide";
let expected = format!(
"{SIGN_LEFT}\n{green_light}poly{reset}{green_heavy}me{reset}{green_light}ra{reset}{green_heavy}s{reset}{green_light}e{reset}\n\n{SIGN_RIGHT}\n{red_light}poly{reset}{red_heavy}ac{reset}{red_light}r{reset}{red_heavy}yl{reset}{red_light}a{reset}{red_heavy}mid{reset}{red_light}e{reset}\n",
red_light = RED_LIGHT,
green_light = GREEN_LIGHT,
red_heavy = RED_HEAVY,
green_heavy = GREEN_HEAVY,
reset = RESET,
);
check_printer(write_inline_diff, left, right, &expected);
}
}