use crate::query;
pub(crate) fn rle(values: &[String]) -> Vec<String> {
let mut out = Vec::new();
let mut i = 0;
while i < values.len() {
let mut j = i + 1;
while j < values.len() && values[j] == values[i] {
j += 1;
}
if j - i > 1 {
out.push(format!("{}x{}", values[i], j - i));
} else {
out.push(values[i].clone());
}
i = j;
}
out
}
fn group_by_file<R>(
rows: &[R],
file_of: fn(&R) -> &str,
line_fmt: impl Fn(&R) -> String,
) -> Vec<String> {
let mut out = Vec::new();
let mut i = 0;
while i < rows.len() {
let mut j = i + 1;
while j < rows.len() && file_of(&rows[j]) == file_of(&rows[i]) {
j += 1;
}
let lines: Vec<String> = rows[i..j].iter().map(|r| line_fmt(r)).collect();
out.push(format!("{}:{}", file_of(&rows[i]), rle(&lines).join(",")));
i = j;
}
out
}
pub(crate) fn ref_kind_block<R>(
out: &mut Vec<String>,
label: &str,
table: Option<&query::Table<R>>,
row_fmt: impl Fn(&R) -> String,
) {
let Some(t) = table else { return };
let dropped_note = if t.dropped != 0 {
format!(", {} dropped", t.dropped)
} else {
String::new()
};
out.push(format!(" {label} ({}{dropped_note}):", t.total));
for r in &t.rows {
out.push(format!(" {}", row_fmt(r)));
}
}
pub(crate) fn ref_kind_block_if_any<R>(
out: &mut Vec<String>,
label: &str,
table: &query::Table<R>,
row_fmt: impl Fn(&R) -> String,
) {
if table.total == 0 {
return;
}
ref_kind_block(out, label, Some(table), row_fmt);
}
pub(crate) fn compact_block<R>(
out: &mut Vec<String>,
label: &str,
table: Option<&query::Table<R>>,
file_of: fn(&R) -> &str,
line_fmt: impl Fn(&R) -> String,
) {
let Some(t) = table else { return };
if t.total == 0 {
return;
}
let dropped_note = if t.dropped != 0 {
format!(", {} dropped", t.dropped)
} else {
String::new()
};
out.push(format!("{label} ({}{dropped_note}):", t.total));
for line in group_by_file(&t.rows, file_of, line_fmt) {
out.push(format!(" {line}"));
}
}