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}