git/
delta.rs

1use crate::DiffLine;
2
3/// Represents a single set of changes
4#[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	/// Create a new `Delta`.
16	#[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	/// Add a `DiffLine`.
37	#[inline]
38	pub fn add_line(&mut self, diff_line: DiffLine) {
39		self.lines.push(diff_line);
40	}
41
42	/// Get the diff context.
43	#[inline]
44	#[must_use]
45	pub fn context(&self) -> &str {
46		self.context.as_str()
47	}
48
49	/// Get the lines.
50	#[inline]
51	#[must_use]
52	pub const fn lines(&self) -> &Vec<DiffLine> {
53		&self.lines
54	}
55
56	/// Get the old lines start.
57	#[inline]
58	#[must_use]
59	pub const fn old_lines_start(&self) -> u32 {
60		self.old_lines_start
61	}
62
63	/// Get the old number of lines
64	#[inline]
65	#[must_use]
66	pub const fn old_number_lines(&self) -> u32 {
67		self.old_number_lines
68	}
69
70	/// Get the new lines start.
71	#[inline]
72	#[must_use]
73	pub const fn new_lines_start(&self) -> u32 {
74		self.new_lines_start
75	}
76
77	/// Get the new number of lines.
78	#[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		// using err return to ensure assert ran
165		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}