use crate::bindings::Binding;
use crate::document::Document;
use crate::identity::{Label, LabelPrefix};
use crate::ids::ElementId;
use crate::node::Node;
use crate::validate::decisions::all_former_labels;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Allocated {
pub label: Label,
pub element: ElementId,
}
pub fn allocate(document: &mut Document) -> Vec<Allocated> {
raise_counters(document);
let mut allocated = Vec::new();
for element in elements_in_order(document) {
let Some(prefix) = element.label_prefix() else {
continue;
};
let current = document.bindings.label_of(element);
if current.is_some_and(|label| label.prefix() == prefix) {
continue;
}
let label = next_label(document, prefix);
let bound = match current {
Some(_) => document.bindings.relabel(element, label),
None => document.bindings.bind(element, label),
};
bound.expect("a fresh label of the element's own prefix binds");
allocated.push(Allocated { label, element });
}
allocated
}
fn raise_counters(document: &mut Document) {
let Document {
bindings,
counters,
edges,
decisions,
..
} = document;
let live = bindings.iter().flat_map(Binding::every_label);
counters.raise_all(live.chain(all_former_labels(edges, decisions)));
}
fn next_label(document: &mut Document, prefix: LabelPrefix) -> Label {
let label = Label::new(prefix, document.counters.next(prefix));
document.counters.raise(label);
label
}
pub(super) fn elements_in_order(document: &Document) -> Vec<ElementId> {
let mut elements = Vec::new();
let mut visit = |node: Node<'_>| {
if let Some(id) = node.id() {
elements.push(ElementId::Node(id));
}
};
for product in &document.products {
Node::Product(product).walk(&mut visit);
}
let mut components: Vec<_> = document.components.iter().collect();
components.sort_by(|a, b| a.slug.as_str().cmp(b.slug.as_str()));
for root in components {
Node::Component(&root.component).walk(&mut visit);
}
let mut decisions: Vec<_> = document.decisions.iter().collect();
decisions.sort_by(|a, b| a.slug.as_str().cmp(b.slug.as_str()));
elements.extend(
decisions
.into_iter()
.map(|decision| ElementId::Decision(decision.id)),
);
elements
}