1#[cfg(test)]
7#[path = "./gitinfo_test.rs"]
8mod gitinfo_test;
9
10use crate::types::{GitInfo, Head};
11use std::collections::HashMap;
12use std::io::Error;
13use std::process::{Command, ExitStatus};
14
15fn get_exit_code(exit_status: Result<ExitStatus, Error>) -> i32 {
17 match exit_status {
18 Ok(code) => {
19 if !code.success() {
20 match code.code() {
21 Some(value) => value,
22 None => -1,
23 }
24 } else {
25 0
26 }
27 }
28 _ => -1,
29 }
30}
31
32fn get_command_output(command: &mut Command) -> Option<String> {
33 let result = command.output();
34
35 match result {
36 Ok(output) => {
37 let exit_code = get_exit_code(Ok(output.status));
38
39 if exit_code == 0 {
40 let stdout = String::from_utf8_lossy(&output.stdout);
41 let line = stdout.trim();
42
43 Some(line.to_string())
44 } else {
45 None
46 }
47 }
48 Err(_) => None,
49 }
50}
51
52fn load_state(info: &mut GitInfo) {
53 let mut command = Command::new("git");
54 command.arg("status").arg("--short");
55
56 let result = get_command_output(&mut command);
57
58 if let Some(output) = result {
59 let dirty = output.len() > 0;
60
61 info.dirty = Some(dirty);
62 }
63}
64
65fn load_config(info: &mut GitInfo) {
66 let mut command = Command::new("git");
67 command.arg("config").arg("--list");
68
69 let result = get_command_output(&mut command);
70
71 if let Some(output) = result {
72 let lines: Vec<&str> = output.split('\n').collect();
73
74 let mut config = HashMap::new();
75
76 for mut line in lines {
77 line = line.trim();
78
79 let mut line_split = line.splitn(2, '=');
80
81 if let Some(key) = line_split.next() {
82 if let Some(value) = line_split.next() {
83 config.insert(key.to_string(), value.to_string());
84 }
85 }
86 }
87
88 info.config = Some(config);
89 }
90}
91
92fn load_from_config(info: &mut GitInfo) {
93 match info.config {
94 Some(ref config) => {
95 if let Some(value) = config.get("user.name") {
96 info.user_name = Some(value.to_string());
97 }
98 if let Some(value) = config.get("user.email") {
99 info.user_email = Some(value.to_string());
100 }
101 }
102 None => (),
103 };
104}
105
106fn load_branches(info: &mut GitInfo) {
107 let mut command = Command::new("git");
108 command.arg("branch").arg("--list").arg("--no-color");
109
110 let result = get_command_output(&mut command);
111
112 if let Some(output) = result {
113 let lines: Vec<&str> = output.split('\n').collect();
114
115 let mut branches = vec![];
116
117 for mut line in lines {
118 line = line.trim();
119
120 let mut line_split = line.splitn(2, ' ');
121
122 let name = match line_split.next() {
123 Some(marker_or_name) => {
124 if marker_or_name == "*" {
125 match line_split.next() {
126 Some(value) => {
127 info.current_branch = Some(value.to_string());
128 value
129 }
130 None => "",
131 }
132 } else {
133 marker_or_name
134 }
135 }
136 None => "",
137 };
138
139 if name.len() > 0 {
140 branches.push(name.to_string());
141 }
142 }
143
144 info.branches = Some(branches);
145 }
146}
147
148fn load_head(head: &mut Head) {
149 let mut command = Command::new("git");
150 command.arg("rev-parse").arg("HEAD");
151
152 let mut result = get_command_output(&mut command);
153
154 if let Some(output) = result {
155 if !output.is_empty() {
156 head.last_commit_hash = Some(output);
157 }
158 }
159
160 command = Command::new("git");
161 command.arg("rev-parse").arg("--short").arg("HEAD");
162
163 result = get_command_output(&mut command);
164
165 if let Some(output) = result {
166 if !output.is_empty() {
167 head.last_commit_hash_short = Some(output);
168 }
169 }
170}
171
172pub(crate) fn get() -> GitInfo {
173 let mut info = GitInfo::new();
174
175 load_state(&mut info);
176 load_config(&mut info);
177 load_from_config(&mut info);
178 load_branches(&mut info);
179 load_head(&mut info.head);
180
181 info
182}