pub fn group_by_submodule(items: Vec<String>) -> Vec<Vec<String>> {
let has_nested_groups = items
.iter()
.any(|item| item.contains('{') && item.contains(','));
let mut groups: Vec<Vec<String>> = Vec::new();
let mut current_group: Vec<String> = Vec::new();
for item in items {
let has_multi_item_braces = item.contains('{') && item.contains(',');
let has_path_separator = item.contains("::");
let needs_own_line = has_multi_item_braces || (has_nested_groups && has_path_separator);
if needs_own_line {
if !current_group.is_empty() {
groups.push(std::mem::take(&mut current_group));
}
groups.push(vec![item]);
} else {
current_group.push(item);
}
}
if !current_group.is_empty() {
groups.push(current_group);
}
groups
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_group_by_submodule_mixed() {
let items = vec![
"attr::AttrsWithOwner".to_string(),
"expr_store::path::Path".to_string(),
"item_scope::ItemInNs".to_string(),
"per_ns::Namespace".to_string(),
"resolver::HasResolver".to_string(),
"resolver::Resolver".to_string(),
"AssocItemId".to_string(),
"AttrDefId".to_string(),
];
let groups = group_by_submodule(items);
assert_eq!(groups.len(), 1);
}
#[test]
fn test_group_by_submodule_same_module() {
let items = vec![
"foo::A".to_string(),
"foo::B".to_string(),
"foo::C".to_string(),
];
let groups = group_by_submodule(items);
assert_eq!(groups.len(), 1);
assert_eq!(groups[0], vec!["foo::A", "foo::B", "foo::C"]);
}
#[test]
fn test_group_by_submodule_root_only() {
let items = vec!["A".to_string(), "B".to_string(), "C".to_string()];
let groups = group_by_submodule(items);
assert_eq!(groups.len(), 1);
assert_eq!(groups[0], vec!["A", "B", "C"]);
}
#[test]
fn test_group_by_submodule_nested_imports() {
let items = vec![
"resolver::{HasResolver, Resolver}".to_string(),
"types::TypeId".to_string(),
];
let groups = group_by_submodule(items);
assert_eq!(groups.len(), 2);
assert_eq!(groups[0], vec!["resolver::{HasResolver, Resolver}"]);
assert_eq!(groups[1], vec!["types::TypeId"]);
}
}