pratdiff/
diff.rs

1use std::ops::Range;
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4pub enum Side {
5  Lhs,
6  Rhs,
7}
8
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub enum DiffItem {
11  Match { lhs: Range<usize>, rhs: Range<usize> },
12  Mutation { lhs: Range<usize>, rhs: Range<usize> },
13}
14
15use DiffItem::*;
16
17impl DiffItem {
18  pub fn lhs(&self) -> Range<usize> {
19    match self {
20      Match { lhs, .. } => lhs.clone(),
21      Mutation { lhs, .. } => lhs.clone(),
22    }
23  }
24
25  pub fn rhs(&self) -> Range<usize> {
26    match self {
27      Match { rhs, .. } => rhs.clone(),
28      Mutation { rhs, .. } => rhs.clone(),
29    }
30  }
31
32  pub fn side(&self, side: Side) -> Range<usize> {
33    match side {
34      Side::Lhs => self.lhs(),
35      Side::Rhs => self.rhs(),
36    }
37  }
38}
39
40#[derive(Clone, Debug, Eq, PartialEq)]
41pub struct Hunk {
42  pub diffs: Vec<DiffItem>,
43}
44
45impl Hunk {
46  pub fn build(context: usize, diffs: &[DiffItem]) -> Vec<Hunk> {
47    let mut res = vec![Hunk { diffs: Vec::new() }];
48
49    for d in diffs {
50      res.last_mut().unwrap().diffs.push(d.clone());
51
52      if matches!(d, Match { lhs, .. } if lhs.len() > 2 * context) {
53        res.push(Hunk { diffs: vec![d.clone()] });
54      }
55    }
56
57    res
58      .into_iter()
59      .filter_map(|mut hunk| {
60        if hunk.diffs.len() <= 1 && matches!(hunk.diffs[0], Match { .. }) {
61          return None;
62        }
63
64        if context == 0 {
65          hunk.diffs.retain(|d| matches!(d, Mutation { .. }));
66          return Some(hunk);
67        }
68
69        if let Some(Match { lhs, rhs }) = hunk.diffs.first_mut() {
70          if lhs.len() > context {
71            lhs.start = lhs.end - context;
72            rhs.start = rhs.end - context;
73          }
74        }
75        if let Some(Match { lhs, rhs }) = hunk.diffs.last_mut() {
76          if lhs.len() > context {
77            lhs.end = lhs.start + context;
78            rhs.end = rhs.start + context;
79          }
80        }
81        Some(hunk)
82      })
83      .collect()
84  }
85
86  pub fn side(&self, side: Side) -> Range<usize> {
87    Range {
88      start: self.diffs.first().map_or(0, |d| d.side(side).start),
89      end: self.diffs.last().map_or(0, |d| d.side(side).end),
90    }
91  }
92
93  pub fn lhs(&self) -> Range<usize> {
94    self.side(Side::Lhs)
95  }
96
97  pub fn rhs(&self) -> Range<usize> {
98    self.side(Side::Rhs)
99  }
100}
101
102#[derive(Clone, Debug, Default, Eq, PartialEq)]
103pub struct Diffs {
104  pub(crate) vec: Vec<DiffItem>,
105}
106
107impl Diffs {
108  pub(crate) fn add_match(&mut self, len: usize) {
109    if len == 0 {
110      return;
111    }
112    if let Some(Match { lhs, rhs }) = self.vec.last_mut() {
113      lhs.end += len;
114      rhs.end += len;
115    } else {
116      self.vec.push(Match {
117        lhs: Range {
118          start: self.lhs_pos(),
119          end: self.lhs_pos() + len,
120        },
121        rhs: Range {
122          start: self.rhs_pos(),
123          end: self.rhs_pos() + len,
124        },
125      });
126    }
127  }
128
129  pub(crate) fn add_mutation(&mut self, lhs: usize, rhs: usize) {
130    if lhs == 0 && rhs == 0 {
131      return;
132    }
133    if let Some(Mutation { lhs: l, rhs: r }) = self.vec.last_mut() {
134      l.end += lhs;
135      r.end += rhs;
136    } else {
137      self.vec.push(Mutation {
138        lhs: Range {
139          start: self.lhs_pos(),
140          end: self.lhs_pos() + lhs,
141        },
142        rhs: Range {
143          start: self.rhs_pos(),
144          end: self.rhs_pos() + rhs,
145        },
146      });
147    }
148  }
149
150  fn lhs_pos(&self) -> usize {
151    self.vec.last().map_or(0, |d| d.lhs().end)
152  }
153
154  fn rhs_pos(&self) -> usize {
155    self.vec.last().map_or(0, |d| d.rhs().end)
156  }
157}