use atty;
use clap::ArgMatches;
use rayon;
use regex::Regex;
use std::str::FromStr;
use termcolor::StandardStream;
use std::default::Default;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::mpsc::channel;
use std::sync::Arc;
mod cli;
mod lib;
use lib::Giterator;
mod output;
const COMMANDS: &[&'static str] = &[
"branch",
"diff",
"diff-index",
"format-patch",
"grep",
"log",
"reflog",
"rev-list",
"shortlog",
"show",
];
#[derive(Debug)]
pub struct GitResult {
directory_name: PathBuf,
output: Output,
}
fn main() {
let matches: ArgMatches<'static> = cli::build_cli().get_matches();
let git_command_args = matches.values_of("cmd").unwrap();
let git_command: Arc<Vec<String>> = Arc::new(git_command_args.map(String::from).collect());
let git_color_arg: Arc<String> = Arc::new(format!("--color={}", cli::get_color_mode(&matches)));
let parent_dir = matches.value_of_os("dir").unwrap_or(OsStr::new("."));
let mut g = Giterator::default()
.root(parent_dir)
.follow_links(matches.is_present("follow_links"));
if let Some(max_depth) = matches.value_of("max_depth") {
g = g.max_depth(
usize::from_str_radix(max_depth, 10)
.expect("max-depth option could not be parsed as a base-10 number"),
);
}
let regex_str = matches.value_of("regex").unwrap(); let regex = Regex::new(regex_str).expect("regex option is not a valid regular expression (see https://docs.rs/regex/1/regex/#syntax for syntax)");
g = g.regex(regex);
g = g.match_full_path(matches.is_present("full_path"));
let program = matches.value_of_os("executable").unwrap().to_owned();
let threads = matches.value_of("workers").map_or(num_cpus::get(), |f| {
usize::from_str(f).expect("threads option could not be parsed as a base-10 number")
});
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build()
.unwrap();
let (tx, rx) = channel::<GitResult>();
for entry in g {
let tx = tx.clone();
let args = git_command.clone();
let color_arg = git_color_arg.clone();
let program = program.clone();
pool.spawn(move || {
let mut command = Command::new(&program);
command.current_dir(entry.path()).args(args.as_ref());
if program == "git" && should_set_git_color_arg(args.as_ref()) {
command.arg(color_arg.as_ref());
}
let output = command.output().expect("failed to execute command");
tx.send(GitResult {
directory_name: entry.into_path(),
output,
})
.unwrap();
});
}
drop(tx);
let color_mode = cli::get_color_mode(&matches);
let stdout = StandardStream::stdout(output::color_choice(&color_mode, &atty::Stream::Stdout));
let stderr = StandardStream::stderr(output::color_choice(&color_mode, &atty::Stream::Stderr));
{
let mut stdout_l = stdout.lock();
let mut stderr_l = stderr.lock();
for gr in rx {
output::print_git_result(&mut stdout_l, &mut stderr_l, gr).unwrap();
}
}
}
fn should_set_git_color_arg(cmd: &Vec<String>) -> bool {
cmd.iter().any(|term| COMMANDS.contains(&term.as_str()))
}