use crate::reply::diff::{Churn, Diff};
use super::parts::{clause, line, line_over, listing, things, when};
pub(super) fn work(rows: &[Diff]) -> String {
let painted = rows.iter().map(row).collect();
listing("work", painted, "nothing has changed")
}
fn row(row: &Diff) -> String {
let head = line(vec![
Some(row.ball_id.clone()),
Some(row.project.clone()),
Some(row.state.clone()),
clause("→", row.target.as_deref()),
clause("from", row.source.as_deref()),
clause("at", ends(row).as_deref()),
when(
!row.missing.is_empty(),
&format!("missing {}", row.missing.join(", ")),
),
clause("delivered", row.delivered.as_deref()),
]);
line_over(&head, body(row))
}
fn ends(row: &Diff) -> Option<String> {
let (target, source) = (row.target_oid.as_ref()?, row.source_oid.as_ref()?);
Some(format!("{}..{}", short(target), short(source)))
}
fn short(oid: &str) -> String {
oid.chars().take(SHORT_OID).collect()
}
const SHORT_OID: usize = 12;
fn body(row: &Diff) -> Option<String> {
if row.truncated.is_some() && row.files.is_empty() {
return Some("nothing changed on this branch yet".to_owned());
}
let lines = std::iter::once(totals(&row.files))
.chain(row.files.iter().map(changed))
.chain(when(row.truncated == Some(true), CUT))
.filter(|said| !said.is_empty())
.collect::<Vec<String>>();
(!lines.is_empty()).then(|| lines.join("\n"))
}
const CUT: &str = "… the engine stopped listing here — how many more files \
changed is not on the wire";
fn totals(files: &[Churn]) -> String {
let sum = |added: bool| -> u64 {
files
.iter()
.filter_map(|churn| if added { churn.added } else { churn.removed })
.sum()
};
let binaries = files
.iter()
.filter(|churn| churn.binary == Some(true))
.count() as u64;
line(vec![
things(files.len() as u64, "file"),
when(
files
.iter()
.any(|churn| churn.added.is_some() || churn.removed.is_some()),
&format!("+{} -{}", sum(true), sum(false)),
),
things(binaries, "binary"),
])
}
fn changed(churn: &Churn) -> String {
line(vec![
Some(churn.path.clone()),
counts(churn),
when(churn.binary == Some(true), "binary"),
])
}
fn counts(churn: &Churn) -> Option<String> {
let said = [
churn.added.map(|n| format!("+{n}")),
churn.removed.map(|n| format!("-{n}")),
]
.into_iter()
.flatten()
.collect::<Vec<String>>()
.join(" ");
(!said.is_empty()).then_some(said)
}
#[cfg(test)]
mod tests;