use std::collections::BTreeMap;
use super::config::Style;
use super::model::{Leaf, LeafItem, self_has_splittable_sibling};
pub(super) fn render(style: Style, leaves: &[Leaf]) -> Vec<String> {
match style {
Style::Crate => render_crate(leaves, false),
Style::Module => render_module(leaves),
Style::Item => render_item(leaves),
}
}
pub(super) fn render_crate_self(leaves: &[Leaf]) -> Vec<String> {
render_crate(leaves, true)
}
pub(super) fn render_crate_split(leaves: &[Leaf]) -> Vec<String> {
let lowered: Vec<Leaf> = leaves
.iter()
.enumerate()
.map(|(index, leaf)| {
if matches!(leaf.item, LeafItem::SelfMod) && self_has_splittable_sibling(leaves, index)
{
let mut module = leaf.module.clone();
let name = module
.pop()
.expect("a `self` leaf always has a module path");
Leaf {
module,
item: LeafItem::Named(name),
rename: leaf.rename.clone(),
}
} else {
leaf.clone()
}
})
.collect();
render_crate(&lowered, false)
}
fn join(segments: &[String]) -> String {
segments.join("::")
}
fn rename_suffix(rename: &Option<String>) -> String {
match rename {
Some(name) => format!(" as {name}"),
None => String::new(),
}
}
fn sort_entries(entries: &mut Vec<String>) {
entries.sort_by(|left, right| entry_key(left).cmp(&entry_key(right)));
entries.dedup();
}
fn entry_key(entry: &str) -> (u8, String, &str) {
let group = if entry == "self" || entry.starts_with("self ") {
0
} else if entry == "*" {
2
} else {
1
};
(group, entry.to_ascii_lowercase(), entry)
}
fn wrap(prefix: &str, entries: &[String]) -> String {
if entries.len() == 1 && !needs_brace(&entries[0]) {
format!("{prefix}::{}", entries[0])
} else {
format!("{prefix}::{{{}}}", entries.join(", "))
}
}
fn needs_brace(entry: &str) -> bool {
entry == "self" || entry.starts_with("self ")
}
fn crate_item_body(leaf: &Leaf) -> String {
let LeafItem::Named(name) = &leaf.item else {
unreachable!("crate item is always a named leaf")
};
format!("{name}{}", rename_suffix(&leaf.rename))
}
fn item_entry(leaf: &Leaf) -> String {
match &leaf.item {
LeafItem::Named(name) => format!("{name}{}", rename_suffix(&leaf.rename)),
LeafItem::Glob => "*".to_owned(),
LeafItem::SelfMod => format!("self{}", rename_suffix(&leaf.rename)),
}
}
fn render_item(leaves: &[Leaf]) -> Vec<String> {
let mut out: Vec<String> = leaves
.iter()
.map(|leaf| {
if leaf.is_crate_item() {
crate_item_body(leaf)
} else {
let entry = item_entry(leaf);
wrap(&join(&leaf.module), std::slice::from_ref(&entry))
}
})
.collect();
out.sort();
out.dedup();
out
}
fn render_module(leaves: &[Leaf]) -> Vec<String> {
let mut groups: BTreeMap<&[String], Vec<&Leaf>> = BTreeMap::new();
for leaf in leaves {
groups.entry(leaf.module.as_slice()).or_default().push(leaf);
}
let mut out = Vec::new();
for (module, members) in groups {
if module.is_empty() {
for leaf in members {
out.push(crate_item_body(leaf));
}
continue;
}
let mut entries: Vec<String> = members.iter().map(|leaf| item_entry(leaf)).collect();
sort_entries(&mut entries);
out.push(wrap(&join(module), &entries));
}
out.sort();
out.dedup();
out
}
#[derive(Default)]
struct Node {
items: Vec<Leaf>,
children: BTreeMap<String, Node>,
}
fn insert(node: &mut Node, module: &[String], leaf: &Leaf) {
match module.split_first() {
None => node.items.push(leaf.clone()),
Some((head, rest)) => insert(node.children.entry(head.clone()).or_default(), rest, leaf),
}
}
fn node_entries(node: &Node, synthesize_self: bool) -> Vec<String> {
let mut entries: Vec<String> = Vec::new();
for item in &node.items {
if synthesize_self
&& let LeafItem::Named(name) = &item.item
&& node.children.contains_key(name)
{
continue;
}
entries.push(item_entry(item));
}
for (segment, child) in &node.children {
let mut sub = node_entries(child, synthesize_self);
if synthesize_self {
for item in &node.items {
if let LeafItem::Named(name) = &item.item
&& name == segment
{
sub.push(format!("self{}", rename_suffix(&item.rename)));
}
}
sort_entries(&mut sub);
}
entries.push(wrap(segment, &sub));
}
sort_entries(&mut entries);
entries
}
fn render_crate(leaves: &[Leaf], synthesize_self: bool) -> Vec<String> {
let mut root = Node::default();
for leaf in leaves {
insert(&mut root, &leaf.module, leaf);
}
let mut out = node_entries(&root, synthesize_self);
out.sort();
out.dedup();
out
}