image_diff_review 0.2.0

Reporting tool of image differences for snaphost testing
Documentation
use crate::difference::{compute_differences, Difference, ImageInfoResult, PairResult};
use image::ImageError;
use std::path::{Path, PathBuf};

use crate::pair::pairs_from_paths;
use crate::report::create_html_report;
use thiserror::Error;

mod difference;
mod fs;
mod pair;
mod report;

#[derive(Error, Debug)]
pub enum Error {
    #[error("IO error")]
    IoError(#[from] std::io::Error),

    #[error("Path is a directory: `{0}`")]
    NotDirectory(PathBuf),

    #[error("Image error")]
    ImageError(#[from] ImageError),
}

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Default)]
pub struct CompareConfig {
    ignore_match: bool,
    ignore_left_missing: bool,
    ignore_right_missing: bool,
}

impl CompareConfig {
    pub fn set_ignore_match(&mut self, value: bool) {
        self.ignore_match = value;
    }

    pub fn set_ignore_left_missing(&mut self, value: bool) {
        self.ignore_left_missing = value;
    }

    pub fn set_ignore_right_missing(&mut self, value: bool) {
        self.ignore_right_missing = value;
    }
}

pub struct ReportConfig<'a> {
    left_title: &'a str,
    right_title: &'a str,
}

impl<'a> Default for ReportConfig<'a> {
    fn default() -> Self {
        ReportConfig {
            left_title: "Left image",
            right_title: "Right image",
        }
    }
}

impl<'a> ReportConfig<'a> {
    pub fn set_left_title(&mut self, title: &'a str) {
        self.left_title = title;
    }

    pub fn set_right_title(&mut self, title: &'a str) {
        self.right_title = title;
    }
}

#[derive(Default)]
pub struct ImageDiff {
    diffs: Vec<PairResult>,
}

impl ImageDiff {
    pub fn compare_directories(
        &mut self,
        config: &CompareConfig,
        left_path: &Path,
        right_path: &Path,
    ) -> Result<()> {
        let pairs = pairs_from_paths(left_path, right_path)?;
        let mut diffs = compute_differences(pairs);

        if config.ignore_match {
            diffs.retain(|pair| !matches!(pair.difference, Difference::None));
        }

        if config.ignore_left_missing {
            diffs.retain(|pair| !matches!(pair.left_info, ImageInfoResult::Missing));
        }

        if config.ignore_right_missing {
            diffs.retain(|pair| !matches!(pair.right_info, ImageInfoResult::Missing));
        }
        self.diffs.append(&mut diffs);
        Ok(())
    }

    pub fn create_report(&self, config: &ReportConfig, output: &Path, verbose: bool) -> Result<()> {
        if verbose && self.diffs.is_empty() {
            println!("Nothing to report");
            return Ok(());
        }
        let count = self.diffs.len();
        create_html_report(config, &self.diffs, output)?;
        if verbose {
            println!(
                "Report written into '{}'; found {} images",
                output.display(),
                count,
            );
        }
        Ok(())
    }
}