use std::fmt::Display;
use ahash::HashMap;
use crate::{
Interner, NameId, SolvableId,
internal::{
arena::ArenaId,
id::{SolvableOrRootId, VariableId},
},
};
pub struct VariableMap {
next_id: usize,
solvable_to_variable: HashMap<SolvableId, VariableId>,
origins: Vec<VariableOrigin>,
}
#[derive(Clone, Copy, Debug)]
pub enum VariableOrigin {
Root,
Solvable(SolvableId),
ForbidMultiple(NameId),
AtLeastOne(NameId),
}
impl Default for VariableMap {
fn default() -> Self {
Self {
next_id: 1,
solvable_to_variable: HashMap::default(),
origins: vec![VariableOrigin::Root],
}
}
}
impl VariableMap {
fn alloc(&mut self, origin: VariableOrigin) -> VariableId {
let id = self.next_id;
self.next_id += 1;
debug_assert_eq!(id, self.origins.len());
self.origins.push(origin);
VariableId::from_usize(id)
}
pub fn intern_solvable(&mut self, solvable_id: SolvableId) -> VariableId {
if let Some(&variable_id) = self.solvable_to_variable.get(&solvable_id) {
return variable_id;
}
let variable_id = self.alloc(VariableOrigin::Solvable(solvable_id));
self.solvable_to_variable.insert(solvable_id, variable_id);
variable_id
}
#[cfg(feature = "diagnostics")]
pub fn count(&self) -> usize {
self.next_id
}
#[cfg(feature = "diagnostics")]
pub fn size_in_bytes(&self) -> usize {
self.origins.capacity() * std::mem::size_of::<VariableOrigin>()
+ self.solvable_to_variable.capacity() * std::mem::size_of::<(SolvableId, VariableId)>()
}
pub fn intern_solvable_or_root(&mut self, solvable_or_root_id: SolvableOrRootId) -> VariableId {
match solvable_or_root_id.solvable() {
Some(solvable_id) => self.intern_solvable(solvable_id),
None => VariableId::root(),
}
}
pub fn alloc_forbid_multiple_variable(&mut self, name: NameId) -> VariableId {
self.alloc(VariableOrigin::ForbidMultiple(name))
}
pub fn alloc_at_least_one_variable(&mut self, name: NameId) -> VariableId {
self.alloc(VariableOrigin::AtLeastOne(name))
}
pub fn origin(&self, variable_id: VariableId) -> VariableOrigin {
self.origins[variable_id.to_usize()]
}
}
impl VariableId {
pub fn as_solvable(self, variable_map: &VariableMap) -> Option<SolvableId> {
variable_map.origin(self).as_solvable()
}
pub fn as_solvable_or_root(self, variable_map: &VariableMap) -> Option<SolvableOrRootId> {
variable_map.origin(self).as_solvable_or_root()
}
pub fn display<'i, I: Interner>(
self,
variable_map: &'i VariableMap,
interner: &'i I,
) -> VariableDisplay<'i, I> {
VariableDisplay {
variable: self,
interner,
variable_map,
}
}
}
pub struct VariableDisplay<'i, I: Interner> {
variable: VariableId,
interner: &'i I,
variable_map: &'i VariableMap,
}
impl<I: Interner> Display for VariableDisplay<'_, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.variable_map.origin(self.variable) {
VariableOrigin::Root => write!(f, "root"),
VariableOrigin::Solvable(solvable_id) => {
write!(f, "{}", self.interner.display_solvable(solvable_id))
}
VariableOrigin::ForbidMultiple(name) => {
write!(f, "forbid-multiple({})", self.interner.display_name(name))
}
VariableOrigin::AtLeastOne(name) => {
write!(f, "any-of({})", self.interner.display_name(name))
}
}
}
}
impl VariableOrigin {
pub fn as_solvable(&self) -> Option<SolvableId> {
match self {
VariableOrigin::Solvable(solvable_id) => Some(*solvable_id),
_ => None,
}
}
pub fn as_solvable_or_root(&self) -> Option<SolvableOrRootId> {
match self {
VariableOrigin::Solvable(solvable_id) => Some((*solvable_id).into()),
VariableOrigin::Root => Some(SolvableOrRootId::root()),
_ => None,
}
}
}