use std::collections::BTreeMap;
pub(super) fn render_grouped<T, F, G, H>(
output: &mut Vec<String>,
items: &[T],
key: F,
mut emit_subheader: G,
mut emit_item: H,
) where
F: Fn(&T) -> String,
G: FnMut(&mut Vec<String>, &str),
H: FnMut(&mut Vec<String>, &T, bool),
{
let mut grouped: BTreeMap<String, Vec<&T>> = BTreeMap::new();
for item in items {
grouped.entry(key(item)).or_default().push(item);
}
let nested = grouped.len() > 1;
for (header, group) in &grouped {
if nested {
emit_subheader(output, header);
}
for it in group {
emit_item(output, it, nested);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_group_skips_subheader() {
let items = vec!["a", "b", "c"];
let mut out = Vec::new();
render_grouped(
&mut out,
&items,
|_| "G".to_string(),
|o, h| o.push(format!("# {}", h)),
|o, item, nested| o.push(format!("{}- {}", if nested { " " } else { "" }, item)),
);
assert_eq!(out, vec!["- a", "- b", "- c"]);
}
#[test]
fn multi_group_emits_subheaders_and_nested_indent() {
let items = vec![("A", "1"), ("B", "2"), ("A", "3")];
let mut out = Vec::new();
render_grouped(
&mut out,
&items,
|(k, _)| k.to_string(),
|o, h| o.push(format!("# {}", h)),
|o, (_, v), nested| o.push(format!("{}- {}", if nested { " " } else { "" }, v)),
);
assert_eq!(out, vec!["# A", " - 1", " - 3", "# B", " - 2",]);
}
}