use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZeroU64;
use crate::identity::{Key, Label, LabelPrefix};
use crate::ids::ElementId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Binding {
pub element: ElementId,
pub label: Label,
pub aliases: Vec<Label>,
pub withdrawn: Vec<Label>,
}
impl Binding {
pub fn labels(&self) -> impl Iterator<Item = Label> + '_ {
std::iter::once(self.label).chain(self.aliases.iter().copied())
}
pub fn every_label(&self) -> impl Iterator<Item = Label> + '_ {
self.labels().chain(self.withdrawn.iter().copied())
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Bindings {
entries: BTreeMap<Key, Binding>,
live_keys: BTreeMap<Label, BTreeSet<Key>>,
alias_keys: BTreeMap<Label, BTreeSet<Key>>,
withdrawn_keys: BTreeMap<Label, BTreeSet<Key>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Citation {
NotWithdrawn,
Rewritten(Label),
HolderMentioned,
Ambiguous,
}
fn index(keys: &mut BTreeMap<Label, BTreeSet<Key>>, label: Label, key: Key) {
keys.entry(label).or_default().insert(key);
}
fn unindex(keys: &mut BTreeMap<Label, BTreeSet<Key>>, label: Label, key: Key) {
if let Some(holders) = keys.get_mut(&label) {
holders.remove(&key);
if holders.is_empty() {
keys.remove(&label);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum BindingError {
#[error("{0} has no label to replace")]
Unbound(ElementId),
#[error("{0} already has a label; a label changes only by relabelling")]
AlreadyBound(ElementId),
#[error("{element} cannot carry label {label}: its kind takes another prefix or none")]
PrefixMismatch { element: ElementId, label: Label },
#[error("label {label} is the live label of both {first} and {second}")]
LabelUnderTwoKeys {
label: Label,
first: ElementId,
second: ElementId,
},
#[error("alias {alias} of {alias_element} is the live label of {label_element}")]
AliasIsLiveLabel {
alias: Label,
alias_element: ElementId,
label_element: ElementId,
},
#[error("alias {alias} is listed under {first} and {second}")]
AliasUnderTwoKeys {
alias: Label,
first: ElementId,
second: ElementId,
},
#[error("label {label} is withdrawn from {element}, which still holds it")]
WithdrawnIsOwnLabel { element: ElementId, label: Label },
}
fn check_prefix(element: ElementId, label: Label) -> Result<(), BindingError> {
if element.label_prefix() == Some(label.prefix()) {
Ok(())
} else {
Err(BindingError::PrefixMismatch { element, label })
}
}
impl Bindings {
pub fn bind(
&mut self,
element: impl Into<ElementId>,
label: Label,
) -> Result<(), BindingError> {
let element = element.into();
check_prefix(element, label)?;
self.bind_as_written(element, label)
}
pub(crate) fn bind_as_written(
&mut self,
element: impl Into<ElementId>,
label: Label,
) -> Result<(), BindingError> {
let element = element.into();
if element.label_prefix().is_none() {
return Err(BindingError::PrefixMismatch { element, label });
}
match self.entries.entry(element.key()) {
std::collections::btree_map::Entry::Occupied(_) => {
Err(BindingError::AlreadyBound(element))
}
std::collections::btree_map::Entry::Vacant(entry) => {
entry.insert(Binding {
element,
label,
aliases: Vec::new(),
withdrawn: Vec::new(),
});
index(&mut self.live_keys, label, element.key());
Ok(())
}
}
}
pub fn relabel(
&mut self,
element: impl Into<ElementId>,
label: Label,
) -> Result<(), BindingError> {
let element = element.into();
check_prefix(element, label)?;
let binding = self
.entries
.get_mut(&element.key())
.ok_or(BindingError::Unbound(element))?;
binding.element = element;
if binding.label != label {
let previous = std::mem::replace(&mut binding.label, label);
binding.aliases.push(previous);
let key = element.key();
unindex(&mut self.live_keys, previous, key);
index(&mut self.live_keys, label, key);
index(&mut self.alias_keys, previous, key);
}
Ok(())
}
pub fn add_alias(
&mut self,
element: impl Into<ElementId>,
alias: Label,
) -> Result<(), BindingError> {
let element = element.into();
let binding = self
.entries
.get_mut(&element.key())
.ok_or(BindingError::Unbound(element))?;
binding.aliases.push(alias);
index(&mut self.alias_keys, alias, element.key());
Ok(())
}
pub fn add_withdrawn(
&mut self,
element: impl Into<ElementId>,
label: Label,
) -> Result<(), BindingError> {
let element = element.into();
let binding = self
.entries
.get_mut(&element.key())
.ok_or(BindingError::Unbound(element))?;
binding.withdrawn.push(label);
index(&mut self.withdrawn_keys, label, element.key());
Ok(())
}
pub fn unbind(&mut self, element: impl Into<ElementId>) -> Option<Binding> {
let key = element.into().key();
let binding = self.entries.remove(&key)?;
unindex(&mut self.live_keys, binding.label, key);
for alias in &binding.aliases {
unindex(&mut self.alias_keys, *alias, key);
}
for withdrawn in &binding.withdrawn {
unindex(&mut self.withdrawn_keys, *withdrawn, key);
}
Some(binding)
}
#[must_use]
pub fn label_of(&self, element: impl Into<ElementId>) -> Option<Label> {
self.label_of_key(element.into().key())
}
#[must_use]
pub fn label_of_key(&self, key: Key) -> Option<Label> {
self.entries.get(&key).map(|binding| binding.label)
}
#[must_use]
pub fn aliases_of(&self, element: impl Into<ElementId>) -> &[Label] {
self.entries
.get(&element.into().key())
.map_or(&[], |binding| binding.aliases.as_slice())
}
#[must_use]
pub fn withdrawn_of(&self, element: impl Into<ElementId>) -> &[Label] {
self.entries
.get(&element.into().key())
.map_or(&[], |binding| binding.withdrawn.as_slice())
}
#[must_use]
pub fn resolve(&self, label: Label) -> Option<ElementId> {
let first = |keys: &BTreeMap<Label, BTreeSet<Key>>| keys.get(&label)?.first().copied();
let key = first(&self.live_keys).or_else(|| first(&self.alias_keys))?;
self.entries.get(&key).map(|binding| binding.element)
}
#[must_use]
pub fn has_withdrawn(&self) -> bool {
!self.withdrawn_keys.is_empty()
}
#[must_use]
pub fn is_withdrawn(&self, label: Label) -> bool {
self.withdrawn_keys.contains_key(&label)
}
#[must_use]
pub fn citation(&self, label: Label, mentioned: impl Fn(Key) -> bool) -> Citation {
let from = self.withdrawn_keys.get(&label).into_iter().flatten();
let mut from = from.copied().filter(|key| mentioned(*key));
let Some(key) = from.next() else {
return Citation::NotWithdrawn;
};
if from.next().is_some() {
return Citation::Ambiguous;
}
if self
.resolve(label)
.is_some_and(|holder| mentioned(holder.key()))
{
return Citation::HolderMentioned;
}
Citation::Rewritten(self.entries[&key].label)
}
pub fn iter(&self) -> impl Iterator<Item = &Binding> {
self.entries.values()
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn validate(&self) -> Vec<BindingError> {
let mut live: BTreeMap<Label, ElementId> = BTreeMap::new();
let mut errors = Vec::new();
for binding in self.iter() {
let first = *live.entry(binding.label).or_insert(binding.element);
if first != binding.element {
errors.push(BindingError::LabelUnderTwoKeys {
label: binding.label,
first,
second: binding.element,
});
}
}
let mut first_owner: BTreeMap<Label, ElementId> = BTreeMap::new();
for (element, alias) in self.aliases() {
if let Some(owner) = live.get(&alias).filter(|owner| **owner != element) {
errors.push(BindingError::AliasIsLiveLabel {
alias,
alias_element: element,
label_element: *owner,
});
}
let first = *first_owner.entry(alias).or_insert(element);
if first != element {
errors.push(BindingError::AliasUnderTwoKeys {
alias,
first,
second: element,
});
}
}
errors.extend(self.iter().flat_map(|binding| {
binding
.withdrawn
.iter()
.filter(|label| binding.labels().any(|held| held == **label))
.map(|label| BindingError::WithdrawnIsOwnLabel {
element: binding.element,
label: *label,
})
}));
errors
}
fn aliases(&self) -> impl Iterator<Item = (ElementId, Label)> {
self.iter().flat_map(|binding| {
binding
.aliases
.iter()
.map(move |alias| (binding.element, *alias))
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Counters(BTreeMap<LabelPrefix, NonZeroU64>);
impl Default for Counters {
fn default() -> Self {
Self(
LabelPrefix::ALL
.into_iter()
.map(|prefix| (prefix, NonZeroU64::MIN))
.collect(),
)
}
}
impl Counters {
#[must_use]
pub fn next(&self, prefix: LabelPrefix) -> NonZeroU64 {
self.0.get(&prefix).copied().unwrap_or(NonZeroU64::MIN)
}
#[must_use]
pub fn above(bindings: &Bindings) -> Self {
let mut counters = Self::default();
counters.raise_all(bindings.iter().flat_map(Binding::every_label));
counters
}
#[must_use]
pub fn has_allocated(&self, label: Label) -> bool {
label.number() < self.next(label.prefix())
}
pub fn set(&mut self, prefix: LabelPrefix, next: NonZeroU64) {
self.0.insert(prefix, next);
}
pub fn raise_all(&mut self, labels: impl IntoIterator<Item = Label>) {
for label in labels {
self.raise(label);
}
}
pub fn raise(&mut self, label: Label) {
let next = label.number().saturating_add(1);
let counter = self.0.entry(label.prefix()).or_insert(NonZeroU64::MIN);
*counter = (*counter).max(next);
}
}