image_diff_review/
lib.rs

1use crate::difference::{compute_differences, Difference, ImageInfoResult, PairResult};
2use image::ImageError;
3use std::path::{Path, PathBuf};
4
5use crate::pair::pairs_from_paths;
6use crate::report::create_html_report;
7use thiserror::Error;
8
9mod difference;
10mod fs;
11mod pair;
12mod report;
13
14#[derive(Error, Debug)]
15pub enum Error {
16    #[error("IO error")]
17    IoError(#[from] std::io::Error),
18
19    #[error("Path is a directory: `{0}`")]
20    NotDirectory(PathBuf),
21
22    #[error("Image error")]
23    ImageError(#[from] ImageError),
24}
25
26pub type Result<T> = std::result::Result<T, Error>;
27
28#[derive(Default)]
29pub struct CompareConfig {
30    ignore_match: bool,
31    ignore_left_missing: bool,
32    ignore_right_missing: bool,
33}
34
35impl CompareConfig {
36    pub fn set_ignore_match(&mut self, value: bool) {
37        self.ignore_match = value;
38    }
39
40    pub fn set_ignore_left_missing(&mut self, value: bool) {
41        self.ignore_left_missing = value;
42    }
43
44    pub fn set_ignore_right_missing(&mut self, value: bool) {
45        self.ignore_right_missing = value;
46    }
47}
48
49pub struct ReportConfig<'a> {
50    left_title: &'a str,
51    right_title: &'a str,
52}
53
54impl<'a> Default for ReportConfig<'a> {
55    fn default() -> Self {
56        ReportConfig {
57            left_title: "Left image",
58            right_title: "Right image",
59        }
60    }
61}
62
63impl<'a> ReportConfig<'a> {
64    pub fn set_left_title(&mut self, title: &'a str) {
65        self.left_title = title;
66    }
67
68    pub fn set_right_title(&mut self, title: &'a str) {
69        self.right_title = title;
70    }
71}
72
73#[derive(Default)]
74pub struct ImageDiff {
75    diffs: Vec<PairResult>,
76}
77
78impl ImageDiff {
79    pub fn compare_directories(
80        &mut self,
81        config: &CompareConfig,
82        left_path: &Path,
83        right_path: &Path,
84    ) -> Result<()> {
85        let pairs = pairs_from_paths(left_path, right_path)?;
86        let mut diffs = compute_differences(pairs);
87
88        if config.ignore_match {
89            diffs.retain(|pair| !matches!(pair.difference, Difference::None));
90        }
91
92        if config.ignore_left_missing {
93            diffs.retain(|pair| !matches!(pair.left_info, ImageInfoResult::Missing));
94        }
95
96        if config.ignore_right_missing {
97            diffs.retain(|pair| !matches!(pair.right_info, ImageInfoResult::Missing));
98        }
99        self.diffs.append(&mut diffs);
100        Ok(())
101    }
102
103    pub fn create_report(&self, config: &ReportConfig, output: &Path, verbose: bool) -> Result<()> {
104        if verbose && self.diffs.is_empty() {
105            println!("Nothing to report");
106            return Ok(());
107        }
108        let count = self.diffs.len();
109        create_html_report(config, &self.diffs, output)?;
110        if verbose {
111            println!(
112                "Report written into '{}'; found {} images",
113                output.display(),
114                count,
115            );
116        }
117        Ok(())
118    }
119}