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}