1use crate::DiffLine;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Delta {
6 old_lines_start: u32,
7 old_number_lines: u32,
8 new_lines_start: u32,
9 new_number_lines: u32,
10 context: String,
11 lines: Vec<DiffLine>,
12}
13
14impl Delta {
15 #[inline]
17 #[must_use]
18 pub fn new(
19 header: &str,
20 old_lines_start: u32,
21 new_lines_start: u32,
22 old_number_lines: u32,
23 new_number_lines: u32,
24 ) -> Self {
25 let context = header.splitn(3, "@@").nth(2).unwrap_or("").trim();
26 Self {
27 old_lines_start,
28 old_number_lines,
29 new_lines_start,
30 new_number_lines,
31 context: String::from(context),
32 lines: vec![],
33 }
34 }
35
36 #[inline]
38 pub fn add_line(&mut self, diff_line: DiffLine) {
39 self.lines.push(diff_line);
40 }
41
42 #[inline]
44 #[must_use]
45 pub fn context(&self) -> &str {
46 self.context.as_str()
47 }
48
49 #[inline]
51 #[must_use]
52 pub const fn lines(&self) -> &Vec<DiffLine> {
53 &self.lines
54 }
55
56 #[inline]
58 #[must_use]
59 pub const fn old_lines_start(&self) -> u32 {
60 self.old_lines_start
61 }
62
63 #[inline]
65 #[must_use]
66 pub const fn old_number_lines(&self) -> u32 {
67 self.old_number_lines
68 }
69
70 #[inline]
72 #[must_use]
73 pub const fn new_lines_start(&self) -> u32 {
74 self.new_lines_start
75 }
76
77 #[inline]
79 #[must_use]
80 pub const fn new_number_lines(&self) -> u32 {
81 self.new_number_lines
82 }
83
84 pub(crate) fn from(diff_hunk: &git2::DiffHunk<'_>) -> Self {
85 Self::new(
86 std::str::from_utf8(diff_hunk.header()).unwrap_or("<INVALID UTF8>"),
87 diff_hunk.old_start(),
88 diff_hunk.new_start(),
89 diff_hunk.old_lines(),
90 diff_hunk.new_lines(),
91 )
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use claim::assert_err;
98
99 use super::{super::origin::Origin, *};
100
101 #[test]
102 fn new_with_correctly_formatted_context() {
103 let delta = Delta::new("@@ path/to/file.rs:56 @@ impl Delta {", 10, 12, 3, 4);
104 assert_eq!(delta.context(), "impl Delta {");
105 assert_eq!(delta.old_lines_start(), 10);
106 assert_eq!(delta.new_lines_start(), 12);
107 assert_eq!(delta.old_number_lines(), 3);
108 assert_eq!(delta.new_number_lines(), 4);
109 }
110
111 #[test]
112 fn new_with_at_symbol_in_context() {
113 let delta = Delta::new("@@ path:1 @@ Context@@", 10, 12, 3, 4);
114 assert_eq!(delta.context(), "Context@@");
115 assert_eq!(delta.old_lines_start(), 10);
116 assert_eq!(delta.new_lines_start(), 12);
117 assert_eq!(delta.old_number_lines(), 3);
118 assert_eq!(delta.new_number_lines(), 4);
119 }
120
121 #[test]
122 fn new_with_incorrectly_formatted_context() {
123 let delta = Delta::new("@@invalid", 10, 12, 3, 4);
124 assert_eq!(delta.context(), "");
125 assert_eq!(delta.old_lines_start(), 10);
126 assert_eq!(delta.new_lines_start(), 12);
127 assert_eq!(delta.old_number_lines(), 3);
128 assert_eq!(delta.new_number_lines(), 4);
129 }
130
131 #[test]
132 fn add_line() {
133 let mut delta = Delta::new("@@ path/to/file.rs:56 @@ impl Delta {", 10, 12, 3, 4);
134 delta.add_line(DiffLine::new(
135 Origin::Addition,
136 "this is a line",
137 Some(10),
138 Some(12),
139 false,
140 ));
141 assert_eq!(delta.lines().len(), 1);
142 }
143
144 #[test]
145 fn from_diff_hunk() {
146 let diff = git2::Diff::from_buffer(
147 [
148 "diff --git a/src/git/src/status.rs b/src/git/src/status.rs",
149 "index 493c0dd..4e07a6e 100644",
150 "--- a/src/git/src/status.rs",
151 "+++ b/src/git/src/status.rs",
152 "@@ -4,3 +4,3 @@ use git2::Delta;",
153 " #[derive(Debug, Copy, Clone, PartialEq)]",
154 "-#[allow(clippy::exhaustive_enums)]",
155 "+#[non_exhaustive]",
156 " pub enum Status {",
157 "",
158 ]
159 .join("\n")
160 .as_bytes(),
161 )
162 .unwrap();
163
164 assert_err!(diff.print(git2::DiffFormat::Patch, |_, diff_hunk, _| {
166 if diff_hunk.is_none() {
167 return true;
168 }
169 assert_eq!(
170 Delta::from(&diff_hunk.unwrap()),
171 Delta::new("@@ -4,3 +4,3 @@ use git2::Delta;", 4, 4, 3, 3)
172 );
173 false
174 }));
175 }
176}