git_info/
gitinfo.rs

1//! # gitinfo
2//!
3//! Loads git information.
4//!
5
6#[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
15/// Returns the exit code (-1 if no exit code found)
16fn 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}