semdiff_differ_binary/
lib.rs1use 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}