use anyhow::{Context, Result};
use crate::git::{git, GitRunner, RealGitRunner};
use crate::output::Output;
use crate::types::RepoStatus;
pub fn parse_repo_status(output: &str) -> RepoStatus {
let mut status = RepoStatus::default();
for line in output.lines() {
if line.len() < 3 {
continue;
}
let index_status = line.chars().next().unwrap();
let work_tree_status = line.chars().nth(1).unwrap();
let file = line[3..].to_string();
match (index_status, work_tree_status) {
('?', '?') => status.untracked.push(file),
(idx, _) if idx != ' ' && idx != '?' => status.staged.push(file),
(_, wt) if wt != ' ' && wt != '?' => status.unstaged.push(file),
_ => {}
}
}
status
}
pub fn get_repo_status(git: &dyn GitRunner) -> Result<RepoStatus> {
let output = git
.run_output(&["status", "--porcelain"])
.context("Failed to get repository status")?;
Ok(parse_repo_status(&output))
}
pub fn has_staged_changes() -> Result<bool> {
let runner = RealGitRunner;
let status = get_repo_status(&runner)?;
Ok(status.has_staged())
}
pub fn has_uncommitted_changes() -> Result<bool> {
let runner = RealGitRunner;
let status = get_repo_status(&runner)?;
Ok(status.has_any_changes())
}
pub fn has_changes_to_stage() -> Result<bool> {
let runner = RealGitRunner;
let status = get_repo_status(&runner)?;
Ok(status.has_any_changes())
}
pub fn count_files_to_stage() -> Result<usize> {
let runner = RealGitRunner;
let status = get_repo_status(&runner)?;
Ok(status.total_files())
}
pub fn show_verbose_status(output: &Output) -> Result<()> {
output.info("Current git status:");
git(&["status"])?;
Ok(())
}
pub fn show_short_status(output: &Output) -> Result<()> {
let (stdout, _) = crate::git::git_output_full(&["status", "--short"])?;
if stdout.is_empty() {
return Ok(());
}
output.println("Changes:");
output.println(&stdout);
Ok(())
}
pub fn show_status(verbose: bool, output: &Output) -> Result<()> {
if verbose {
return show_verbose_status(output);
}
show_short_status(output)
}