use crate::internal::assignment::Assignment::{self, Decision, Derivation};
use crate::package::Package;
use crate::range::Range;
use crate::term::Term;
use crate::type_aliases::{Map, SelectedDependencies};
use crate::version::Version;
#[derive(Clone)]
pub struct Memory<P: Package, V: Version> {
assignments: Map<P, PackageAssignments<V>>,
}
#[derive(Clone)]
enum PackageAssignments<V: Version> {
Decision((V, Term<V>)),
Derivations {
intersected: Term<V>,
not_intersected_yet: Vec<Term<V>>,
},
}
impl<P: Package, V: Version> Memory<P, V> {
pub fn empty() -> Self {
Self {
assignments: Map::default(),
}
}
pub fn term_intersection_for_package(&mut self, package: &P) -> Option<&Term<V>> {
self.assignments
.get_mut(package)
.map(|pa| pa.assignment_intersection())
}
pub fn add_assignment(&mut self, assignment: &Assignment<P, V>) {
match assignment {
Decision { package, version } => self.add_decision(package.clone(), version.clone()),
Derivation { package, cause } => {
self.add_derivation(package.clone(), cause.get(&package).unwrap().negate())
}
}
}
fn add_decision(&mut self, package: P, version: V) {
if cfg!(debug_assertions) {
match self.assignments.get_mut(&package) {
None => panic!("Assignments must already exist"),
Some(PackageAssignments::Decision(_)) => panic!("Already existing decision"),
Some(pa) => debug_assert!(pa.assignment_intersection().contains(&version)),
}
}
self.assignments.insert(
package,
PackageAssignments::Decision((version.clone(), Term::exact(version))),
);
}
fn add_derivation(&mut self, package: P, term: Term<V>) {
use std::collections::hash_map::Entry;
match self.assignments.entry(package) {
Entry::Occupied(mut o) => match o.get_mut() {
PackageAssignments::Decision(_) => debug_assert!(false),
PackageAssignments::Derivations {
intersected: _,
not_intersected_yet,
} => {
not_intersected_yet.push(term);
}
},
Entry::Vacant(v) => {
v.insert(PackageAssignments::Derivations {
intersected: term,
not_intersected_yet: Vec::new(),
});
}
}
}
pub fn potential_packages(&mut self) -> impl Iterator<Item = (&P, &Range<V>)> {
self.assignments
.iter_mut()
.filter_map(|(p, pa)| pa.potential_package_filter(p))
}
pub fn extract_solution(&self) -> Option<SelectedDependencies<P, V>> {
let mut solution = Map::default();
for (p, pa) in &self.assignments {
match pa {
PackageAssignments::Decision((v, _)) => {
solution.insert(p.clone(), v.clone());
}
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
if intersected.is_positive()
|| not_intersected_yet.iter().any(|t| t.is_positive())
{
return None;
}
}
}
}
Some(solution)
}
}
impl<V: Version> PackageAssignments<V> {
fn assignment_intersection(&mut self) -> &Term<V> {
match self {
PackageAssignments::Decision((_, term)) => term,
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
for derivation in not_intersected_yet.iter() {
*intersected = intersected.intersection(&derivation);
}
not_intersected_yet.clear();
intersected
}
}
}
fn potential_package_filter<'a, P: Package>(
&'a mut self,
package: &'a P,
) -> Option<(&'a P, &'a Range<V>)> {
match self {
PackageAssignments::Decision(_) => None,
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
if intersected.is_positive() || not_intersected_yet.iter().any(|t| t.is_positive())
{
Some((package, self.assignment_intersection().unwrap_positive()))
} else {
None
}
}
}
}
}