use std::fs;
use std::io;
use std::path::PathBuf;
use std::process;
use std::process::Command;
use std::thread;
use std::time::Duration;
use clap::Parser;
use git_gamble::message::Message;
use git_gamble::git_time_keeper::cli::Args;
use git_gamble::git_time_keeper::cli::Cli;
use git_gamble::git_time_keeper::cli::DEFAULT_TIMEOUT_COMMAND;
use git_gamble::git_time_keeper::path_buf_extension::FolderIsNotEmptyExt;
fn main() {
#[cfg(feature = "with_log")]
pretty_env_logger::init();
human_panic::setup_panic!(
human_panic::metadata!()
.support("- Issue: https://gitlab.com/pinage404/git-gamble/-/issues/new")
);
let cli = Cli::parse();
match cli {
Cli::Start(args) => start(args).unwrap(), Cli::Background(args) => background(args).unwrap(), Cli::Stop => stop().unwrap(), }
}
fn start(args: Args) -> Result<(), io::Error> {
let git_dir = get_git_dir()?;
let lock_files_folder = git_dir.get_lock_files_folder();
if lock_files_folder.exists() && lock_files_folder.folder_is_not_empty()? {
log::info!("Already started, do nothing");
} else {
let iteration_duration = args.iteration_duration.to_string();
let timeout_command = args.timeout_command;
let child = Command::new(env!("CARGO_BIN_NAME"))
.args([
"background",
"--iteration-duration",
iteration_duration.as_str(),
timeout_command.as_str(),
])
.spawn()?;
let process_id = child.id();
log::info!(
"[{process_id}] Started with an iteration duration of {}s",
args.iteration_duration
);
}
Ok(())
}
fn background(args: Args) -> Result<(), io::Error> {
let git_dir = get_git_dir()?;
let process_id = process::id();
let lock_files_folder = git_dir.get_lock_files_folder();
fs::create_dir_all(lock_files_folder)?;
let lock_file = git_dir.get_lock_file_path_for(process_id);
fs::write(&lock_file, "")?;
log::info!("[{process_id}] Waiting {}s", args.iteration_duration);
thread::sleep(Duration::from_secs(args.iteration_duration.into()));
if lock_file.exists() {
Message::Error("Timed out!".to_string()).display();
let timeout_command = split_timeout_command(args.timeout_command.as_str());
Command::new(&timeout_command[0])
.args(&timeout_command[1..])
.status()?;
if lock_file.exists() {
fs::remove_file(lock_file)?;
}
log::info!("[{process_id}] Stepped back");
} else {
log::info!("[{process_id}] Stopped while waiting, do nothing");
}
Ok(())
}
fn split_timeout_command(maybe_timeout_command: &str) -> Vec<String> {
let timeout_command = if maybe_timeout_command.trim().is_empty() {
DEFAULT_TIMEOUT_COMMAND
} else {
maybe_timeout_command
};
shlex::split(timeout_command).expect("[{process_id}] the timeout command shouldn't be empty") }
fn stop() -> Result<(), io::Error> {
let git_dir = get_git_dir()?;
let lock_files_folder = git_dir.get_lock_files_folder();
if lock_files_folder.exists() && lock_files_folder.folder_is_not_empty()? {
fs::remove_dir_all(lock_files_folder)?;
log::info!("Stopped");
} else {
log::info!("Cannot be stopped because it is not currently running, do nothing");
}
Ok(())
}
fn get_git_dir() -> Result<PathBuf, io::Error> {
let output = Command::new("git")
.args(["rev-parse", "--git-dir"])
.output()?
.stdout;
let git_dir = PathBuf::from(String::from_utf8(output).unwrap().trim());
Ok(git_dir)
}
trait GitDirExt {
fn get_lock_files_folder(&self) -> PathBuf;
fn get_lock_file_path_for(&self, process_id: u32) -> PathBuf;
}
impl GitDirExt for PathBuf {
fn get_lock_files_folder(&self) -> PathBuf {
self.join(env!("CARGO_BIN_NAME").to_string() + ".lock")
}
fn get_lock_file_path_for(&self, process_id: u32) -> PathBuf {
let lock_files_folder = self.get_lock_files_folder();
lock_files_folder.join(process_id.to_string() + ".pid")
}
}