goldentests 1.4.1

A golden file testing library where tests can be configured within the same test file
Documentation
use colored::{Color, ColoredString, Colorize};
use similar::{Change, ChangeTag, DiffOp, TextDiff};
use std::fmt::{Display, Error, Formatter};

pub struct DiffPrinter<'a>(pub TextDiff<'a, 'a, 'a, str>);

fn print_line_number(index: Option<usize>, f: &mut Formatter, colorizer: Colorizer) -> Result<(), Error> {
    let line_number = index.map_or_else(String::new, |line| (line + 1).to_string());
    let line_number_string = format!("{:>3}| ", line_number);

    write!(f, "{}", colorizer.color(false, &line_number_string))
}

fn fmt_line(f: &mut Formatter, index: Option<usize>, change: Change<&str>) -> Result<(), Error> {
    let colorizer = match change.tag() {
        ChangeTag::Delete => Colorizer::colored(Color::Red),
        ChangeTag::Equal => Colorizer::normal(),
        ChangeTag::Insert => Colorizer::colored(Color::Green),
    };
    print_line_number(index, f, colorizer)?;

    writeln!(
        f,
        "{}",
        colorizer.color(false, change.to_string().strip_suffix('\n').unwrap())
    )
}

#[derive(Copy, Clone)]
struct Colorizer {
    color: Color,
    pass: bool,
}

impl Colorizer {
    const fn colored(color: Color) -> Colorizer {
        Colorizer { color, pass: false }
    }

    const fn normal() -> Colorizer {
        Colorizer {
            color: Color::Black,
            pass: true,
        }
    }

    fn color(&self, background: bool, character: &str) -> ColoredString {
        if self.pass {
            character.normal()
        } else if background {
            character.on_color(self.color)
        } else {
            character.color(self.color)
        }
    }
}

impl Display for DiffPrinter<'_> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        for op in self.0.ops() {
            match op {
                DiffOp::Delete { .. } | DiffOp::Equal { .. } | DiffOp::Insert { .. } => {
                    for change in self.0.iter_changes(op) {
                        fmt_line(f, change.new_index(), change)?;
                    }
                }
                DiffOp::Replace {
                    new_index: start,
                    new_len: len,
                    ..
                } => {
                    let mut iter = self.0.iter_changes(op);
                    for (line, change) in (*start..).zip(iter.by_ref().take(*len)) {
                        fmt_line(f, Some(line), change)?;
                    }

                    for change in iter {
                        fmt_line(f, None, change)?;
                    }
                }
            }
        }
        Ok(())
    }
}