use std::fmt::Display;
use crate::{
DenseIndex, Interner, VariableId, VersionSetId,
internal::solver_id::SolvableIdOrRoot,
solver_id::{IdMap, SolverId},
};
pub(crate) struct VariableMap<N: SolverId, S: SolverId> {
solvable_to_variable: S::Map<Option<VariableId>>,
origins: Vec<VariableOrigin<N, S>>,
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum VariableOrigin<N, S> {
Root,
Solvable(S),
ForbidMultiple(N),
AtLeastOne(N),
ConstrainsViolation(VersionSetId),
}
impl<N: SolverId, S: SolverId> Default for VariableMap<N, S> {
fn default() -> Self {
Self {
solvable_to_variable: Default::default(),
origins: vec![VariableOrigin::Root],
}
}
}
impl<N: SolverId, S: SolverId> VariableMap<N, S> {
fn alloc(&mut self, origin: VariableOrigin<N, S>) -> VariableId {
let id = self.origins.len();
self.origins.push(origin);
VariableId::from_index(id)
}
pub fn intern_solvable(&mut self, solvable_id: S) -> 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));
debug_assert!(
!variable_id.is_root(),
"intern_solvable must never hand out the root variable id"
);
self.solvable_to_variable
.set(solvable_id, Some(variable_id));
variable_id
}
pub fn lookup_solvable(&self, solvable_id: S) -> Option<VariableId> {
self.solvable_to_variable.get(solvable_id)
}
#[cfg(feature = "diagnostics")]
pub fn count(&self) -> usize {
self.origins.len()
}
#[cfg(feature = "diagnostics")]
pub fn size_in_bytes(&self) -> usize {
self.origins.capacity() * std::mem::size_of::<VariableOrigin<N, S>>()
}
pub fn intern_solvable_or_root(
&mut self,
solvable_or_root_id: SolvableIdOrRoot<S>,
) -> 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: N) -> VariableId {
self.alloc(VariableOrigin::ForbidMultiple(name))
}
pub fn alloc_at_least_one_variable(&mut self, name: N) -> VariableId {
self.alloc(VariableOrigin::AtLeastOne(name))
}
pub fn alloc_constrains_violation_variable(&mut self, version_set: VersionSetId) -> VariableId {
self.alloc(VariableOrigin::ConstrainsViolation(version_set))
}
#[inline]
pub fn origin(&self, variable_id: VariableId) -> VariableOrigin<N, S> {
self.origins[variable_id.to_index()]
}
}
impl VariableId {
pub(crate) fn as_solvable<N: SolverId, S: SolverId>(
self,
variable_map: &VariableMap<N, S>,
) -> Option<S> {
variable_map.origin(self).as_solvable()
}
pub(crate) fn as_solvable_or_root<N: SolverId, S: SolverId>(
self,
variable_map: &VariableMap<N, S>,
) -> Option<SolvableIdOrRoot<S>> {
variable_map.origin(self).as_solvable_or_root()
}
pub(crate) fn display<'i, I: Interner>(
self,
variable_map: &'i VariableMap<I::NameId, I::SolvableId>,
interner: &'i I,
) -> VariableDisplay<'i, I> {
VariableDisplay {
variable: self,
interner,
variable_map,
}
}
}
pub(crate) struct VariableDisplay<'i, I: Interner> {
variable: VariableId,
interner: &'i I,
variable_map: &'i VariableMap<I::NameId, I::SolvableId>,
}
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))
}
VariableOrigin::ConstrainsViolation(version_set) => {
write!(
f,
"constrains-violation({} {})",
self.interner
.display_name(self.interner.version_set_name(version_set)),
self.interner.display_version_set(version_set)
)
}
}
}
}
impl<N: SolverId, S: SolverId> VariableOrigin<N, S> {
pub(crate) fn as_solvable(&self) -> Option<S> {
match self {
VariableOrigin::Solvable(solvable_id) => Some(*solvable_id),
_ => None,
}
}
pub(crate) fn as_solvable_or_root(&self) -> Option<SolvableIdOrRoot<S>> {
match self {
VariableOrigin::Solvable(solvable_id) => Some((*solvable_id).into()),
VariableOrigin::Root => Some(SolvableIdOrRoot::root()),
_ => None,
}
}
}