use crate::component::Component;
use crate::engine::command;
use crate::git;
#[derive(Debug, Clone)]
pub struct TagGap {
pub tag: String,
pub ahead: u32,
pub commits: Vec<String>,
}
pub fn detect_tag_gap(component: &Component) -> Option<TagGap> {
let path = &component.local_path;
let tag = git::get_latest_tag(path).ok().flatten()?;
let ahead_str = command::run_in_optional(
path,
"git",
&["rev-list", "--count", &format!("{}..HEAD", tag)],
)?;
let ahead = ahead_str.trim().parse::<u32>().ok()?;
if ahead == 0 {
return None;
}
let log_output = command::run_in_optional(
path,
"git",
&[
"log",
"--oneline",
"--format=%h %s",
"-10",
&format!("{}..HEAD", tag),
],
)
.unwrap_or_default();
let commits: Vec<String> = log_output
.lines()
.filter(|l| !l.is_empty())
.map(|l| l.to_string())
.collect();
Some(TagGap {
tag,
ahead,
commits,
})
}
pub fn format_tag_gap(component_id: &str, gap: &TagGap, context: &str) -> String {
let mut lines = vec![format!(
"[{}] '{}': HEAD is {} commit(s) ahead of latest tag {}",
context, component_id, gap.ahead, gap.tag
)];
for commit in &gap.commits {
lines.push(format!("[{}] {}", context, commit));
}
if gap.ahead > 10 {
lines.push(format!(
"[{}] ... and {} more",
context,
gap.ahead - gap.commits.len() as u32
));
}
lines.join("\n")
}
pub fn warn_tag_gap(component_id: &str, gap: &TagGap, context: &str) {
eprintln!("{}", format_tag_gap(component_id, gap, context));
}