Skip to content

Commit eea14eb

Browse files
committed
Move the porcelain parser out into its own function
1 parent 85cae7f commit eea14eb

File tree

1 file changed

+72
-57
lines changed

1 file changed

+72
-57
lines changed

src/main.rs

Lines changed: 72 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@
55
use std::error::Error;
66
use std::process;
77

8-
const PPERROR: &str = "Unexpected end of data while parsing Git output";
8+
struct GitStatus {
9+
branch: Option<String>,
10+
ahead: i64,
11+
behind: i64,
12+
13+
staged: i64,
14+
modified: i64,
15+
deleted: i64,
16+
unmerged: i64,
17+
untracked: i64,
18+
}
919

1020
fn color(c: i32) {
1121
if c >= 0 {
@@ -23,78 +33,83 @@ fn bold(b: bool) {
2333
}
2434
}
2535

26-
fn main() -> Result<(), Box<Error>> {
27-
let output = process::Command::new("git")
28-
.args(&["status", "--porcelain=v2", "-z", "--branch", "--untracked-files=all"])
29-
.stdin(process::Stdio::null())
30-
.stderr(process::Stdio::null())
31-
.output()?;
32-
if !output.status.success() {
33-
// We're most likely not in a Git repo
34-
return Ok(())
35-
}
36-
let status = String::from_utf8(output.stdout)
37-
.ok().ok_or("Invalid UTF-8 while decoding Git output")?;
38-
39-
// Details on the current branch
40-
let mut branch = None;
41-
let mut ahead = 0;
42-
let mut behind = 0;
43-
44-
// File counters
45-
let mut staged = 0;
46-
let mut modified = 0;
47-
let mut deleted = 0;
48-
let mut unmerged = 0;
49-
let mut untracked = 0;
36+
fn parse_porcelain2(data: String) -> Option<GitStatus> {
37+
let mut status = GitStatus {
38+
branch: None,
39+
ahead: 0,
40+
behind: 0,
5041

42+
staged: 0,
43+
modified: 0,
44+
deleted: 0,
45+
unmerged: 0,
46+
untracked: 0,
47+
};
5148
// Simple parser for the porcelain v2 format
52-
for entry in status.split('\0') {
49+
for entry in data.split('\0') {
5350
let mut entry = entry.split(' ');
5451
match entry.next() {
5552
// Header lines
5653
Some("#") => {
57-
match entry.next().ok_or(PPERROR)? {
54+
match entry.next()? {
5855
"branch.head" => {
59-
let head = entry.next().ok_or(PPERROR)?;
56+
let head = entry.next()?;
6057
if head != "(detached)" {
61-
branch = Some(head);
58+
status.branch = Some(String::from(head));
6259
}
6360
},
6461
"branch.ab" => {
65-
let a = entry.next().ok_or(PPERROR)?;
66-
let b = entry.next().ok_or(PPERROR)?;
67-
ahead = a.parse::<i64>()?.abs();
68-
behind = b.parse::<i64>()?.abs();
62+
let a = entry.next()?;
63+
let b = entry.next()?;
64+
status.ahead = a.parse::<i64>().ok()?.abs();
65+
status.behind = b.parse::<i64>().ok()?.abs();
6966
},
7067
_ => {},
7168
}
7269
},
7370
// File entries
7471
Some("1") | Some("2") => {
75-
let mut xy = entry.next().ok_or(PPERROR)?.chars();
76-
let x = xy.next().ok_or(PPERROR)?;
77-
let y = xy.next().ok_or(PPERROR)?;
72+
let mut xy = entry.next()?.chars();
73+
let x = xy.next()?;
74+
let y = xy.next()?;
7875
if x != '.' {
79-
staged += 1;
76+
status.staged += 1;
8077
}
8178
match y {
82-
'M' => modified += 1,
83-
'D' => deleted += 1,
79+
'M' => status.modified += 1,
80+
'D' => status.deleted += 1,
8481
_ => {},
8582
}
8683
}
87-
Some("u") => unmerged += 1,
88-
Some("?") => untracked += 1,
84+
Some("u") => status.unmerged += 1,
85+
Some("?") => status.untracked += 1,
8986
_ => {},
9087
}
9188
}
89+
Some(status)
90+
}
91+
92+
fn main() -> Result<(), Box<Error>> {
93+
let output = process::Command::new("git")
94+
.args(&["status", "--porcelain=v2", "-z", "--branch", "--untracked-files=all"])
95+
.stdin(process::Stdio::null())
96+
.stderr(process::Stdio::null())
97+
.output()?;
98+
if !output.status.success() {
99+
// We're most likely not in a Git repo
100+
return Ok(())
101+
}
102+
let status = String::from_utf8(output.stdout)
103+
.ok().ok_or("Invalid UTF-8 while decoding Git output")?;
104+
105+
let status = parse_porcelain2(status)
106+
.ok_or("Error while parsing Git output")?;
92107

93108
print!("(");
94109

95110
color(15);
96111
bold(true);
97-
if let Some(branch) = branch {
112+
if let Some(branch) = status.branch {
98113
print!("{}", branch);
99114
} else {
100115
// Detached head
@@ -104,35 +119,35 @@ fn main() -> Result<(), Box<Error>> {
104119
color(-1);
105120

106121
// Divergence with remote branch
107-
if ahead != 0 {
108-
print!("↑{}", ahead);
122+
if status.ahead != 0 {
123+
print!("↑{}", status.ahead);
109124
}
110-
if behind != 0 {
111-
print!("↓{}", behind);
125+
if status.behind != 0 {
126+
print!("↓{}", status.behind);
112127
}
113128

114-
if untracked + modified + deleted + unmerged + staged > 0 {
129+
if status.untracked + status.modified + status.deleted + status.unmerged + status.staged > 0 {
115130
print!("|");
116131
}
117-
if untracked != 0 {
132+
if status.untracked != 0 {
118133
color(2);
119-
print!("+{}", untracked);
134+
print!("+{}", status.untracked);
120135
}
121-
if modified != 0 {
136+
if status.modified != 0 {
122137
color(5);
123-
print!("~{}", modified);
138+
print!("~{}", status.modified);
124139
}
125-
if deleted != 0 {
140+
if status.deleted != 0 {
126141
color(1);
127-
print!("-{}", deleted);
142+
print!("-{}", status.deleted);
128143
}
129-
if unmerged != 0 {
144+
if status.unmerged != 0 {
130145
color(3);
131-
print!("x{}", unmerged);
146+
print!("x{}", status.unmerged);
132147
}
133-
if staged != 0 {
148+
if status.staged != 0 {
134149
color(4);
135-
print!("•{}", staged);
150+
print!("•{}", status.staged);
136151
}
137152

138153
color(-1);

0 commit comments

Comments
 (0)