1pub use self::r#impl::Diff;
2
3pub enum Render<'a> {
4 Common(&'a str),
5 Unique(&'a str),
6}
7
8#[cfg(all(feature = "diff", not(windows)))]
9mod r#impl {
10 use super::Render;
11 use dissimilar::Chunk;
12 use std::cmp;
13 use std::panic;
14
15 pub struct Diff<'a> {
16 expected: &'a str,
17 actual: &'a str,
18 diff: Vec<Chunk<'a>>,
19 }
20
21 impl<'a> Diff<'a> {
22 pub fn compute(expected: &'a str, actual: &'a str) -> Option<Self> {
23 if expected.len() + actual.len() > 2048 {
24 // We don't yet trust the dissimilar crate to work well on large
25 // inputs.
26 return None;
27 }
28
29 // Nor on non-ascii inputs.
30 let diff = panic::catch_unwind(|| dissimilar::diff(expected, actual)).ok()?;
31
32 let mut common_len = 0;
33 for chunk in &diff {
34 if let Chunk::Equal(common) = chunk {
35 common_len += common.len();
36 }
37 }
38
39 let bigger_len = cmp::max(expected.len(), actual.len());
40 let worth_printing = 5 * common_len >= 4 * bigger_len;
41 if !worth_printing {
42 return None;
43 }
44
45 Some(Diff {
46 expected,
47 actual,
48 diff,
49 })
50 }
51
52 pub fn iter<'i>(&'i self, input: &str) -> impl Iterator<Item = Render<'a>> + 'i {
53 let expected = input == self.expected;
54 let actual = input == self.actual;
55 self.diff.iter().filter_map(move |chunk| match chunk {
56 Chunk::Equal(common) => Some(Render::Common(common)),
57 Chunk::Delete(unique) if expected => Some(Render::Unique(unique)),
58 Chunk::Insert(unique) if actual => Some(Render::Unique(unique)),
59 _ => None,
60 })
61 }
62 }
63}
64
65#[cfg(any(not(feature = "diff"), windows))]
66mod r#impl {
67 use super::Render;
68
69 pub enum Diff {}
70
71 impl Diff {
72 pub fn compute(_expected: &str, _actual: &str) -> Option<Self> {
73 None
74 }
75
76 pub fn iter(&self, _input: &str) -> Box<dyn Iterator<Item = Render>> {
77 let _ = Render::Common;
78 let _ = Render::Unique;
79 match *self {}
80 }
81 }
82}
83