extern crate alloc;
use alloc::collections::{BTreeMap, BTreeSet};
use crate::metis::Dot;
use super::DotStore;
mod collision;
mod iter;
mod store;
pub use collision::DotCollision;
pub use iter::DotMapIter;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DotMap<K, S> {
entries: BTreeMap<K, S>,
}
impl<K, S> Default for DotMap<K, S> {
fn default() -> Self {
Self {
entries: BTreeMap::new(),
}
}
}
impl<K: Ord + Clone, S: DotStore> DotMap<K, S> {
#[must_use]
pub const fn new() -> Self {
Self {
entries: BTreeMap::new(),
}
}
#[must_use]
pub fn singleton(key: K, store: S) -> Self {
let mut entries = BTreeMap::new();
if !store.is_bottom() {
let _ = entries.insert(key, store);
}
Self { entries }
}
#[must_use = "insertion is refused (returns false) on a bottom store or a cross-key dot collision, which the caller must handle to keep the disjointness invariant"]
pub fn insert(&mut self, key: K, store: S) -> bool {
if store.is_bottom() {
return false;
}
let incoming: BTreeSet<Dot> = store.dots().collect();
let collides = self.entries.iter().any(|(other_key, held)| {
*other_key != key && held.dots().any(|dot| incoming.contains(&dot))
});
if collides {
return false;
}
let _ = self.entries.insert(key, store);
true
}
pub fn from_disjoint(entries: impl IntoIterator<Item = (K, S)>) -> Result<Self, DotCollision> {
let mut seen: BTreeMap<Dot, K> = BTreeMap::new();
let mut map: BTreeMap<K, S> = BTreeMap::new();
for (key, store) in entries {
if store.is_bottom() {
continue;
}
if let Some(previous) = map.remove(&key) {
for dot in previous.dots() {
let _ = seen.remove(&dot);
}
}
for dot in store.dots() {
if let Some(other_key) = seen.get(&dot)
&& *other_key != key
{
return Err(DotCollision { dot });
}
let _ = seen.insert(dot, key.clone());
}
let _ = map.insert(key, store);
}
Ok(Self { entries: map })
}
#[must_use]
pub fn get(&self, key: &K) -> Option<&S> {
self.entries.get(key)
}
#[must_use]
pub fn iter(&self) -> DotMapIter<'_, K, S> {
DotMapIter::new(self.entries.iter())
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}