use balls::git;
use balls::store::Store;
use balls::task::Task;
use std::collections::HashSet;
use std::path::Path;
pub struct ClaimStatus {
pub main_ahead: usize,
pub overlap_files: Vec<String>,
}
pub fn for_task(store: &Store, task: &Task, main_branch: Option<&str>) -> ClaimStatus {
let Some((main, branch)) = main_branch.zip(task.branch.as_deref()) else {
return ClaimStatus { main_ahead: 0, overlap_files: Vec::new() };
};
ClaimStatus {
main_ahead: count_ahead(&store.root, branch, main),
overlap_files: overlap(&store.root, branch, main),
}
}
fn count_ahead(dir: &Path, base: &str, head: &str) -> usize {
let range = format!("{base}..{head}");
git::clean_git_command(dir)
.args(["rev-list", "--count", &range])
.output()
.ok()
.filter(|o| o.status.success())
.and_then(|o| String::from_utf8_lossy(&o.stdout).trim().parse().ok())
.unwrap_or(0)
}
fn overlap(dir: &Path, work: &str, main: &str) -> Vec<String> {
let main_files: HashSet<String> = changed_files(dir, work, main).into_iter().collect();
if main_files.is_empty() {
return Vec::new();
}
let mut overlap: Vec<String> = changed_files(dir, main, work)
.into_iter()
.filter(|f| main_files.contains(f))
.collect();
overlap.sort();
overlap
}
fn changed_files(dir: &Path, base: &str, head: &str) -> Vec<String> {
let range = format!("{base}...{head}");
git::clean_git_command(dir)
.args(["diff", "--name-only", &range])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.lines()
.filter(|l| !l.is_empty())
.map(String::from)
.collect()
})
.unwrap_or_default()
}