git/
file_status.rs

1use std::path::{Path, PathBuf};
2
3use super::{delta::Delta, status::Status};
4use crate::file_mode::FileMode;
5
6/// Represents a file change within a Git repository
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct FileStatus {
9	pub(crate) deltas: Vec<Delta>,
10	pub(crate) destination_is_binary: bool,
11	pub(crate) destination_mode: FileMode,
12	pub(crate) destination_path: PathBuf,
13	pub(crate) largest_new_line_number: u32,
14	pub(crate) largest_old_line_number: u32,
15	pub(crate) source_is_binary: bool,
16	pub(crate) source_mode: FileMode,
17	pub(crate) source_path: PathBuf,
18	pub(crate) status: Status,
19}
20
21impl FileStatus {
22	/// Create a new `FileStat`.
23	#[inline]
24	#[must_use]
25	pub(crate) fn new<F: AsRef<Path>>(
26		source_path: F,
27		source_mode: FileMode,
28		source_is_binary: bool,
29		destination_path: F,
30		destination_mode: FileMode,
31		destination_is_binary: bool,
32		status: Status,
33	) -> Self {
34		Self {
35			deltas: vec![],
36			destination_is_binary,
37			destination_mode,
38			destination_path: PathBuf::from(destination_path.as_ref()),
39			largest_new_line_number: 0,
40			largest_old_line_number: 0,
41			source_is_binary,
42			source_mode,
43			source_path: PathBuf::from(source_path.as_ref()),
44			status,
45		}
46	}
47
48	/// Add a delta to the change.
49	#[inline]
50	pub fn add_delta(&mut self, delta: Delta) {
51		let last_old_line_number = delta.old_lines_start() + delta.old_number_lines();
52		if self.largest_old_line_number < last_old_line_number {
53			self.largest_old_line_number = last_old_line_number;
54		}
55		let last_new_line_number = delta.new_lines_start() + delta.new_number_lines();
56		if self.largest_new_line_number < last_new_line_number {
57			self.largest_new_line_number = last_new_line_number;
58		}
59		self.deltas.push(delta);
60	}
61
62	/// Get the status of this file change.
63	#[inline]
64	#[must_use]
65	pub const fn status(&self) -> Status {
66		self.status
67	}
68
69	/// Get the destination file path for this change.
70	#[inline]
71	#[must_use]
72	pub fn destination_path(&self) -> &Path {
73		self.destination_path.as_path()
74	}
75
76	/// Get the destination file mode for this change.
77	#[inline]
78	#[must_use]
79	pub const fn destination_mode(&self) -> FileMode {
80		self.destination_mode
81	}
82
83	/// Is the destination file a binary file.
84	#[inline]
85	#[must_use]
86	pub const fn destination_is_binary(&self) -> bool {
87		self.destination_is_binary
88	}
89
90	/// Get the source file path for this change.
91	#[inline]
92	#[must_use]
93	pub fn source_path(&self) -> &Path {
94		self.source_path.as_path()
95	}
96
97	/// Get the source file mode for this change.
98	#[inline]
99	#[must_use]
100	pub const fn source_mode(&self) -> FileMode {
101		self.source_mode
102	}
103
104	/// Is the source file a binary file.
105	#[inline]
106	#[must_use]
107	pub const fn source_is_binary(&self) -> bool {
108		self.source_is_binary
109	}
110
111	/// Get the deltas for this change.
112	#[inline]
113	#[must_use]
114	pub const fn deltas(&self) -> &Vec<Delta> {
115		&self.deltas
116	}
117
118	/// Get the line number of the last old changed line.
119	#[inline]
120	#[must_use]
121	pub const fn last_old_line_number(&self) -> u32 {
122		self.largest_old_line_number
123	}
124
125	/// Get the line number of the last new changed line.
126	#[inline]
127	#[must_use]
128	pub const fn last_new_line_number(&self) -> u32 {
129		self.largest_new_line_number
130	}
131}
132
133#[cfg(test)]
134mod tests {
135	use testutils::assert_empty;
136
137	use super::*;
138
139	fn create_file_stat() -> FileStatus {
140		FileStatus::new(
141			Path::new("/from/path"),
142			FileMode::Normal,
143			false,
144			Path::new("/to/path"),
145			FileMode::Executable,
146			false,
147			Status::Modified,
148		)
149	}
150
151	#[test]
152	fn status() {
153		assert_eq!(create_file_stat().status(), Status::Modified);
154	}
155
156	#[test]
157	fn destination_path() {
158		assert_eq!(create_file_stat().destination_path(), PathBuf::from("/to/path"));
159	}
160
161	#[test]
162	fn destination_mode() {
163		assert_eq!(create_file_stat().destination_mode(), FileMode::Executable);
164	}
165
166	#[test]
167	fn destination_is_binary() {
168		assert!(!create_file_stat().destination_is_binary());
169	}
170
171	#[test]
172	fn source_path() {
173		assert_eq!(create_file_stat().source_path(), PathBuf::from("/from/path"));
174	}
175
176	#[test]
177	fn source_mode() {
178		assert_eq!(create_file_stat().source_mode(), FileMode::Normal);
179	}
180
181	#[test]
182	fn source_is_binary() {
183		assert!(!create_file_stat().source_is_binary());
184	}
185
186	#[test]
187	fn deltas_empty() {
188		let file_stat = create_file_stat();
189		assert_empty!(file_stat.deltas());
190		assert_eq!(file_stat.last_old_line_number(), 0);
191		assert_eq!(file_stat.last_new_line_number(), 0);
192	}
193
194	#[test]
195	fn deltas_single() {
196		let mut file_stat = create_file_stat();
197		let delta = Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 12, 3, 4);
198		file_stat.add_delta(delta.clone());
199		assert_eq!(file_stat.deltas(), &vec![delta]);
200		assert_eq!(file_stat.last_old_line_number(), 13);
201		assert_eq!(file_stat.last_new_line_number(), 16);
202	}
203
204	#[test]
205	fn deltas_multiple() {
206		let mut file_stat = create_file_stat();
207		let delta1 = Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 12, 3, 4);
208		let delta2 = Delta::new("@ path/to/file.rs:156 @ impl Delta {", 110, 2, 10, 3);
209		file_stat.add_delta(delta1.clone());
210		file_stat.add_delta(delta2.clone());
211		assert_eq!(file_stat.deltas(), &vec![delta1, delta2]);
212		assert_eq!(file_stat.last_old_line_number(), 120);
213		assert_eq!(file_stat.last_new_line_number(), 16);
214	}
215
216	#[test]
217	fn deltas_with_second_delta_with_larger_old_line_number() {
218		let mut file_stat = create_file_stat();
219		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 20, 5, 5));
220		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 20, 20, 5, 5));
221		assert_eq!(file_stat.last_old_line_number(), 25);
222	}
223
224	#[test]
225	fn deltas_with_first_delta_with_larger_old_line_number() {
226		let mut file_stat = create_file_stat();
227		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 20, 20, 5, 5));
228		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 20, 5, 5));
229		assert_eq!(file_stat.last_old_line_number(), 25);
230	}
231
232	#[test]
233	fn deltas_with_second_delta_with_larger_new_line_number() {
234		let mut file_stat = create_file_stat();
235		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 10, 5, 5));
236		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 20, 5, 5));
237		assert_eq!(file_stat.last_new_line_number(), 25);
238	}
239
240	#[test]
241	fn deltas_with_first_delta_with_larger_new_line_number() {
242		let mut file_stat = create_file_stat();
243		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 20, 5, 5));
244		file_stat.add_delta(Delta::new("@ path/to/file.rs:56 @ impl Delta {", 10, 10, 5, 5));
245		assert_eq!(file_stat.last_new_line_number(), 25);
246	}
247}