use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;
use sysinfo::{Pid, Process, ProcessExt, SystemExt};
use lazy_static::lazy_static;
#[derive(Clone, Debug, PartialEq)]
pub enum CallingProcess {
#[allow(unused)]
GitDiff(CommandLine),
#[allow(unused)]
GitShow(CommandLine, Option<String>), // element 2 is file extension
#[allow(unused)]
GitLog(CommandLine),
#[allow(unused)]
GitReflog(CommandLine),
#[allow(unused)]
GitGrep(CommandLine),
#[allow(unused)]
OtherGrep, // rg, grep, ag, ack, etc
}
// TODO: Git blame is currently handled differently
#[derive(Clone, Debug, PartialEq)]
pub struct CommandLine {
pub long_options: HashSet<String>,
pub short_options: HashSet<String>,
last_arg: Option<String>,
}
pub fn calling_process() -> Option<Cow<'static, CallingProcess>> {
#[cfg(not(test))]
{
CACHED_CALLING_PROCESS.as_ref().map(Cow::Borrowed)
}
#[cfg(test)]
{
determine_calling_process().map(|proc| Cow::Owned(proc))
}
}
lazy_static! {
static ref CACHED_CALLING_PROCESS: Option<CallingProcess> = None;
}
#[allow(unused)]
fn determine_calling_process() -> Option<CallingProcess> {
calling_process_cmdline(ProcInfo::new(), describe_calling_process)
}
// Return value of `extract_args(args: &[String]) -> ProcessArgs<T>` function which is
// passed to `calling_process_cmdline()`.
#[derive(Debug, PartialEq)]
pub enum ProcessArgs<T> {
// A result has been successfully extracted from args.
Args(T),
// The extraction has failed.
ArgError,
// The process does not match, others may be inspected.
OtherProcess,
}
pub fn git_blame_filename_extension() -> Option<String> {
calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension)
}
pub fn guess_git_blame_filename_extension(args: &[String]) -> ProcessArgs<String> {
let all_args = args.iter().map(|s| s.as_str());
// See git(1) and git-blame(1). Some arguments separate their parameter with space or '=', e.g.
// --date 2015 or --date=2015.
let git_blame_options_with_parameter =
"-C -c -L --since --ignore-rev --ignore-revs-file --contents --reverse --date";
let selected_args =
skip_uninteresting_args(all_args, git_blame_options_with_parameter.split(' '));
match selected_args.as_slice() {
[git, "blame", .., last_arg] if is_git_binary(git) => match last_arg.split('.').last() {
Some(arg) => ProcessArgs::Args(arg.to_string()),
None => ProcessArgs::ArgError,
},
[git, "blame"] if is_git_binary(git) => ProcessArgs::ArgError,
_ => ProcessArgs::OtherProcess,
}
}
pub fn describe_calling_process(args: &[String]) -> ProcessArgs<CallingProcess>