Skip to main content

semdiff_differ_binary/
lib.rs

1use memmap2::Mmap;
2use semdiff_core::fs::FileLeaf;
3use semdiff_core::{Diff, DiffCalculator, MayUnsupported};
4use similar::{ChangeTag, TextDiffConfig};
5use std::convert;
6use std::sync::Arc;
7
8pub mod report_html;
9pub mod report_json;
10pub mod report_summary;
11
12#[cfg(test)]
13mod tests;
14
15pub struct BinaryDiffReporter;
16
17#[derive(Debug)]
18pub struct BinaryDiff {
19    equal: bool,
20    expected: Arc<Mmap>,
21    actual: Arc<Mmap>,
22}
23
24impl Diff for BinaryDiff {
25    fn equal(&self) -> bool {
26        self.equal
27    }
28}
29
30impl BinaryDiff {
31    fn expected(&self) -> &[u8] {
32        &self.expected
33    }
34
35    fn actual(&self) -> &[u8] {
36        &self.actual
37    }
38
39    fn changes(&self) -> similar::TextDiff<'_, '_, '_, [u8]> {
40        binary_diff_changes(&self.expected[..], &self.actual[..])
41    }
42
43    fn stat<'a>(changes: &'a similar::TextDiff<'a, 'a, 'a, [u8]>) -> ChangeStat {
44        binary_change_stat(changes)
45    }
46}
47
48fn binary_diff_changes<'a>(expected: &'a [u8], actual: &'a [u8]) -> similar::TextDiff<'a, 'a, 'a, [u8]> {
49    TextDiffConfig::default()
50        .algorithm(similar::Algorithm::Patience)
51        .diff_chars(expected, actual)
52}
53
54fn binary_change_stat<'a>(changes: &'a similar::TextDiff<'a, 'a, 'a, [u8]>) -> ChangeStat {
55    changes
56        .iter_all_changes()
57        .fold(ChangeStat::default(), |stat, change| match change.tag() {
58            ChangeTag::Delete => stat.deleted(),
59            ChangeTag::Insert => stat.added(),
60            ChangeTag::Equal => stat,
61        })
62}
63
64#[derive(Debug, Default)]
65struct ChangeStat {
66    added: usize,
67    deleted: usize,
68}
69
70impl ChangeStat {
71    fn added(self) -> ChangeStat {
72        ChangeStat {
73            added: self.added + 1,
74            deleted: self.deleted,
75        }
76    }
77
78    fn deleted(self) -> ChangeStat {
79        ChangeStat {
80            added: self.added,
81            deleted: self.deleted + 1,
82        }
83    }
84}
85
86#[derive(Default)]
87pub struct BinaryDiffCalculator;
88
89impl DiffCalculator<FileLeaf> for BinaryDiffCalculator {
90    type Error = convert::Infallible;
91    type Diff = BinaryDiff;
92
93    fn diff(
94        &self,
95        _name: &str,
96        expected: FileLeaf,
97        actual: FileLeaf,
98    ) -> Result<MayUnsupported<Self::Diff>, Self::Error> {
99        Ok(MayUnsupported::Ok(BinaryDiff {
100            equal: <[u8] as PartialEq<[u8]>>::eq(&*expected.content, &*actual.content),
101            expected: expected.content,
102            actual: actual.content,
103        }))
104    }
105}