1use std::collections::BTreeSet;
2use std::io::Error as IOError;
3use std::process::{Command, ExitStatus, Output, Stdio};
4
5use branches::Branches;
6use error::Error;
7use options::Options;
8
9pub fn run_command_with_no_output(args: &[&str]) {
10 Command::new(args[0])
11 .args(&args[1..])
12 .stdin(Stdio::null())
13 .stdout(Stdio::null())
14 .stderr(Stdio::null())
15 .output()
16 .unwrap_or_else(|e| panic!("Error with command: {}", e));
17}
18
19pub fn output(args: &[&str]) -> String {
20 let result = run_command(args);
21 String::from_utf8(result.stdout).unwrap().trim().to_owned()
22}
23
24pub fn run_command(args: &[&str]) -> Output {
25 run_command_with_result(args).unwrap_or_else(|e| panic!("Error with command: {}", e))
26}
27
28pub fn run_command_with_result(args: &[&str]) -> Result<Output, IOError> {
29 Command::new(args[0]).args(&args[1..]).output()
30}
31
32pub fn run_command_with_status(args: &[&str]) -> Result<ExitStatus, IOError> {
33 Command::new(args[0])
34 .args(&args[1..])
35 .stdin(Stdio::null())
36 .stdout(Stdio::null())
37 .stderr(Stdio::null())
38 .status()
39}
40
41pub fn validate_git_installation() -> Result<(), Error> {
42 match Command::new("git").output() {
43 Ok(_) => Ok(()),
44 Err(_) => Err(Error::GitInstallation),
45 }
46}
47
48pub fn delete_local_branches(branches: &Branches) -> String {
49 if branches.vec.is_empty() {
55 String::default()
56 } else {
57 let delete_branches_args =
58 branches
59 .vec
60 .iter()
61 .fold(vec!["git", "branch", "-D"], |mut acc, b| {
62 acc.push(b);
63 acc
64 });
65 let delete_branches_cmd = run_command(&delete_branches_args);
66 String::from_utf8(delete_branches_cmd.stdout).unwrap()
67 }
68}
69
70pub fn delete_remote_branches(branches: &Branches, options: &Options) -> String {
71 let remote_branches_cmd = run_command(&["git", "branch", "-r"]);
72
73 let s = String::from_utf8(remote_branches_cmd.stdout).unwrap();
74 let all_remote_branches = s.split('\n').collect::<Vec<&str>>();
75 let origin_for_trim = &format!("{}/", &options.remote)[..];
76 let b_tree_remotes = all_remote_branches
77 .iter()
78 .map(|b| b.trim().trim_start_matches(origin_for_trim).to_owned())
79 .collect::<BTreeSet<String>>();
80
81 let mut b_tree_branches = BTreeSet::new();
82
83 for branch in branches.vec.clone() {
84 b_tree_branches.insert(branch);
85 }
86
87 let intersection: Vec<_> = b_tree_remotes
88 .intersection(&b_tree_branches)
89 .cloned()
90 .collect();
91
92 let stderr = if intersection.is_empty() {
93 String::default()
94 } else {
95 let delete_branches_args = intersection.iter().fold(
96 vec!["git", "push", &options.remote, "--delete"],
97 |mut acc, b| {
98 acc.push(b);
99 acc
100 },
101 );
102 let delete_remote_branches_cmd = run_command(&delete_branches_args);
103 String::from_utf8(delete_remote_branches_cmd.stderr).unwrap()
104 };
105
106 let split = stderr.split('\n');
108 let vec: Vec<&str> = split.collect();
109 let mut output = vec![];
110 for s in vec {
111 if s.contains("error: unable to delete '") {
112 let branch = s
113 .trim_start_matches("error: unable to delete '")
114 .trim_end_matches("': remote ref does not exist");
115
116 output.push(branch.to_owned() + " was already deleted in the remote.");
117 } else if s.contains(" - [deleted]") {
118 output.push(s.to_owned());
119 }
120 }
121
122 output.join("\n")
123}
124
125#[cfg(test)]
126mod test {
127
128 use regex::Regex;
129
130 #[test]
133 fn test_spawn_piped() {
134 let echo = Regex::new("foo\n").unwrap();
135 assert_eq!(
136 echo.captures_iter("foo\nbar\nbaz")
137 .fold(String::new(), |mut acc, e| {
138 acc.push_str(&e[0]);
139 acc
140 }),
141 "foo\n"
142 );
143 }
144}