use anyhow::Result;
#[derive(Debug, Default, Clone)]
pub struct StatusSummary {
pub staged: usize,
pub unstaged: usize,
pub untracked: usize,
}
#[derive(Debug, Clone)]
pub struct StatusEntry {
pub staged: bool,
pub unstaged: bool,
pub conflict: bool,
pub path: String,
}
pub fn parse_status(raw: &str) -> Result<StatusSummary> {
let mut summary = StatusSummary::default();
for line in raw.lines() {
if line.starts_with("1 ") {
let bytes = line.as_bytes();
if bytes.len() >= 4 {
let x = bytes[2] as char;
let y = bytes[3] as char;
if x != '.' {
summary.staged += 1;
}
if y != '.' {
summary.unstaged += 1;
}
}
} else if line.starts_with("? ") {
summary.untracked += 1;
}
}
Ok(summary)
}
pub fn parse_status_entries(raw: &str) -> Vec<StatusEntry> {
let mut entries = Vec::new();
for line in raw.lines() {
if line.starts_with("1 ") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 4 {
let x = parts[1].chars().nth(0).unwrap_or('.');
let y = parts[1].chars().nth(1).unwrap_or('.');
let path = parts.last().unwrap_or(&"").to_string();
let conflict = matches!(
(x, y),
('U', _)
| (_, 'U')
| ('A', 'A')
| ('D', 'D')
| ('A', 'D')
| ('D', 'A')
);
entries.push(StatusEntry {
staged: x != '.',
unstaged: y != '.',
conflict,
path,
});
}
} else if line.starts_with("? ") {
let path = line.trim_start_matches("? ").to_string();
entries.push(StatusEntry {
staged: false,
unstaged: false,
conflict: false,
path,
});
}
}
entries
}