use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use lazy_static::lazy_static;
use sysinfo::{Pid, Process, ProcessExt, ProcessRefreshKind, SystemExt};
#[derive(Clone, Debug, PartialEq)]
pub enum CallingProcess {
GitDiff(CommandLine),
GitShow(CommandLine, Option<String>), // element 2 is file extension
GitLog(CommandLine),
GitReflog(CommandLine),
GitGrep(CommandLine),
OtherGrep, // rg, grep, ag, ack, etc
None, // no matching process could be found
Pending, // calling process is currently being determined
}
// 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>,
}
lazy_static! {
static ref CALLER: Arc<(Mutex<CallingProcess>, Condvar)> =
Arc::new((Mutex::new(CallingProcess::Pending), Condvar::new()));
}
pub fn start_determining_calling_process_in_thread() {
// The handle is neither kept nor returned nor joined but dropped, so the main
// thread can exit early if it does not need to know its parent process.
std::thread::Builder::new()
.name("find_calling_process".into())
.spawn(move || {
let calling_process = determine_calling_process();
let (caller_mutex, determine_done) = &**CALLER;
let mut caller = caller_mutex.lock().unwrap();
*caller = calling_process;
determine_done.notify_all();
})
.unwrap();
}
#[cfg(not(test))]
pub fn calling_process() -> MutexGuard<'static, CallingProcess> {
let (caller_mutex, determine_done) = &**CALLER;
determine_done
.wait_while(caller_mutex.lock().unwrap(), |caller| {
*caller == CallingProcess::Pending
})
.unwrap()
}
// The return value is duck-typed to work in place of a MutexGuard when testing.
#[cfg(test)]
pub fn calling_process() -> Box<CallingProcess> {
type _UnusedImport = MutexGuard<'static, i8>;
Box::new(determine_calling_process())
}
fn determine_calling_process() -> CallingProcess {
calling_process_cmdline(ProcInfo::new(), describe_calling_process)
.unwrap_or(CallingProcess::None)
}
// Return value of `extract_args(args: &[String]) -> ProcessArgs<T>` function which is
// passed to `calling_process_cmdline()`.
#[derive(Debug, PartialEq)]
pub enum Proce