use std::ffi::OsString;
use std::io::{Error, ErrorKind};
use std::path::PathBuf;
use std::process::Command;
#[derive(Debug, PartialEq, Clone)]
pub enum OpType {
Status, Read, Bypass, Xargs, }
#[derive(Debug, PartialEq, Clone)]
pub struct Opts {
pub xargs_cmd: Option<OsString>,
pub op: OpType,
pub git_root: Option<PathBuf>,
pub arg_dir: PathBuf,
}
impl Opts {
fn git_cmd(&self) -> Command {
let mut git = Command::new("git");
git.current_dir(&self.arg_dir);
git
}
fn xargs_cmd(&self) -> Result<Command, Error> {
let cmd = self.xargs_cmd.as_ref().ok_or(Error::new(
ErrorKind::NotFound,
"xargs command not found",
))?;
let mut cmd = Command::new(cmd);
cmd.current_dir(&self.arg_dir);
Ok(cmd)
}
pub fn cmd(&self) -> Result<Command, Error> {
use OpType::*;
match self.op {
Read | Status | Bypass => Ok(self.git_cmd()),
Xargs => self.xargs_cmd(),
}
}
fn open_repo(&self) -> Result<git2::Repository, Error> {
Ok(git2::Repository::open_ext(
&self.arg_dir,
git2::RepositoryOpenFlags::empty(),
Vec::<PathBuf>::new(),
)
.ok()
.ok_or(Error::new(ErrorKind::NotFound, "repository not found"))?)
}
pub fn cache_file(&self) -> Result<PathBuf, Error> {
Ok(self.open_repo()?.path().to_path_buf().join("gitnu.txt"))
}
fn set_git_root(&mut self) {
if let Ok(repo) = self.open_repo() {
self.git_root = repo.workdir().map(|v| v.to_path_buf());
}
}
}
pub fn get(args: &Vec<OsString>) -> (Opts, Vec<OsString>) {
let mut opts = Opts {
op: OpType::Bypass,
xargs_cmd: None,
arg_dir: PathBuf::from("."),
git_root: None,
};
let mut set_op_once = |new_op: OpType| {
if opts.op == OpType::Bypass {
opts.op = new_op;
}
};
let mut res: Vec<OsString> = Vec::new();
let mut it = args.iter();
while let Some(arg) = it.next() {
let mut push = || res.push(OsString::from(arg));
match arg.to_str().unwrap_or("") {
"add" | "reset" | "diff" | "checkout" => {
set_op_once(OpType::Read);
push()
}
"status" => {
set_op_once(OpType::Status);
push()
}
"-c" => match it.next() {
Some(cmd) => {
set_op_once(OpType::Xargs);
opts.xargs_cmd = Some(cmd.to_owned());
}
None => push(),
},
"-C" => match it.next() {
Some(dir) => opts.arg_dir = PathBuf::from(dir),
None => push(),
},
_ => push(),
}
}
opts.set_git_root();
return (opts, res);
}
#[cfg(test)]
fn expected(
arg_dir: Option<&str>,
xargs_cmd: Option<&str>,
op: OpType,
) -> Opts {
let stringify = |v: Option<&str>| match v {
None => None,
Some(v) => Some(OsString::from(v)),
};
Opts {
arg_dir: PathBuf::from(arg_dir.unwrap_or(".")),
xargs_cmd: stringify(xargs_cmd),
op,
git_root: None,
}
}
#[cfg(test)]
fn received(args: &[&str]) -> Opts {
let a: Vec<OsString> = args.iter().map(|v| v.into()).collect();
let (opts, _) = get(&a);
opts
}
#[test]
fn test_get_opts() {
fn assert_eq(rec: &Opts, exp: &Opts) {
assert_eq!(rec.arg_dir, exp.arg_dir);
assert_eq!(rec.xargs_cmd, exp.xargs_cmd);
assert_eq!(rec.op, exp.op);
}
let rec = received(&["-C", "/dev/null"]);
let exp = expected(Some("/dev/null"), None, OpType::Bypass);
assert_eq(&rec, &exp);
let rec = received(&["-c", "nvim"]);
let exp = expected(None, Some("nvim"), OpType::Xargs);
assert_eq(&rec, &exp);
let rec = received(&["-C", "/etc", "-c", "nvim"]);
let exp = expected(Some("/etc"), Some("nvim"), OpType::Xargs);
assert_eq(&rec, &exp);
let rec = received(&["-c", "nvim", "-C", "/etc"]);
let exp = expected(Some("/etc"), Some("nvim"), OpType::Xargs);
assert_eq(&rec, &exp);
let rec = received(&["status", "--short"]);
let exp = expected(None, None, OpType::Status);
assert_eq(&rec, &exp);
let rec = received(&["add", "2-4"]);
let exp = expected(None, None, OpType::Read);
assert_eq(&rec, &exp);
let rec = received(&["-C", "/tmp", "add", "2-4"]);
let exp = expected(Some("/tmp"), None, OpType::Read);
assert_eq(&rec, &exp);
let rec = received(&["-C"]);
let exp = expected(None, None, OpType::Bypass);
assert_eq(&rec, &exp);
let rec = received(&["-C", "status"]);
let exp = expected(Some("status"), None, OpType::Bypass);
assert_eq(&rec, &exp);
let rec = received(&["-c"]);
let exp = expected(None, None, OpType::Bypass);
assert_eq(&rec, &exp);
let rec = received(&["-c", "status"]);
let exp = expected(None, Some("status"), OpType::Xargs);
assert_eq(&rec, &exp);
}