1use std::path::PathBuf;
2
3use crate::ParseError;
4
5#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
6pub struct GitStat {
7 pub insertions: usize,
8 pub deletions: usize,
9 pub new_files: usize,
10 pub is_worktree: bool,
11}
12
13impl GitStat {
14 pub fn parse_line(line: &str) -> Result<(PathBuf, Self), ParseError> {
15 let mut parts = line.rsplitn(5, ' ');
16 let is_worktree = parts
17 .next()
18 .ok_or(ParseError::Missing("worktree field"))
19 .and_then(|v| {
20 v.parse::<u8>().map_err(|_| ParseError::Invalid {
21 field: "worktree",
22 value: format!("{v:?}"),
23 })
24 })?
25 != 0;
26 let new_files = parts
27 .next()
28 .ok_or(ParseError::Missing("new_files field"))
29 .and_then(|v| {
30 v.parse().map_err(|_| ParseError::Invalid {
31 field: "new_files",
32 value: format!("{v:?}"),
33 })
34 })?;
35 let deletions = parts
36 .next()
37 .ok_or(ParseError::Missing("deletions field"))
38 .and_then(|v| {
39 v.parse().map_err(|_| ParseError::Invalid {
40 field: "deletions",
41 value: format!("{v:?}"),
42 })
43 })?;
44 let insertions = parts
45 .next()
46 .ok_or(ParseError::Missing("insertions field"))
47 .and_then(|v| {
48 v.parse().map_err(|_| ParseError::Invalid {
49 field: "insertions",
50 value: format!("{v:?}"),
51 })
52 })?;
53 let path = PathBuf::from(parts.next().ok_or(ParseError::Missing("path"))?);
54 Ok((
55 path,
56 Self {
57 insertions,
58 deletions,
59 new_files,
60 is_worktree,
61 },
62 ))
63 }
64}
65
66impl std::fmt::Display for GitStat {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(
69 f,
70 "{} {} {} {}",
71 self.insertions,
72 self.deletions,
73 self.new_files,
74 u8::from(self.is_worktree),
75 )
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use rstest::rstest;
82
83 use super::*;
84
85 #[rstest]
86 #[case("/home/user/project 10 5 2 1", "/home/user/project", 10, 5, 2, true)]
87 #[case("/home/user/my project 10 5 2 0", "/home/user/my project", 10, 5, 2, false)]
88 fn test_git_stat_parse_line_works_as_expected(
89 #[case] line: &str,
90 #[case] expected_path: &str,
91 #[case] insertions: usize,
92 #[case] deletions: usize,
93 #[case] new_files: usize,
94 #[case] is_worktree: bool,
95 ) {
96 let expected_stat = GitStat {
97 insertions,
98 deletions,
99 new_files,
100 is_worktree,
101 };
102 assert2::assert!(let Ok((path, stat)) = GitStat::parse_line(line));
103 pretty_assertions::assert_eq!((path, stat), (PathBuf::from(expected_path), expected_stat));
104 }
105}