use std::{
path::Path,
process::{Command, Output},
};
use crate::error::AppError;
mod diff;
mod history;
mod target;
pub(crate) use diff::load_diff;
pub(crate) use history::load_commits;
pub(crate) use target::resolve_target_file;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Commit {
pub(crate) hash: String,
pub(crate) subject: String,
pub(crate) description: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct DiffLine {
pub(crate) text: String,
pub(crate) kind: DiffLineKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DiffLineKind {
Add,
Remove,
Hunk,
Metadata,
Context,
}
fn format_command_failure(command_name: &'static str, output: &Output) -> AppError {
let stderr = String::from_utf8_lossy(&output.stderr);
let stderr = stderr.trim();
let stdout = String::from_utf8_lossy(&output.stdout);
let stdout = stdout.trim();
if !stderr.is_empty() {
AppError::git_command_failed(command_name, output.status, stderr.to_string())
} else if !stdout.is_empty() {
AppError::git_command_failed(command_name, output.status, stdout.to_string())
} else {
AppError::git_command_failed_without_output(command_name, output.status)
}
}
fn run_git(
current_dir: &Path,
command_name: &'static str,
args: &[&str],
) -> crate::error::Result<Output> {
let output = Command::new("git")
.current_dir(current_dir)
.args(args)
.output()
.map_err(AppError::git_spawn)?;
if !output.status.success() {
return Err(format_command_failure(command_name, &output));
}
Ok(output)
}
fn run_git_pathspec(
current_dir: &Path,
command_name: &'static str,
args: &[&str],
repo_path: &Path,
) -> crate::error::Result<Output> {
let output = Command::new("git")
.current_dir(current_dir)
.env("GIT_LITERAL_PATHSPECS", "1")
.args(args)
.arg("--")
.arg(repo_path)
.output()
.map_err(AppError::git_spawn)?;
if !output.status.success() {
return Err(format_command_failure(command_name, &output));
}
Ok(output)
}