use crate::constraint::Constraint;
use crate::package::Link;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum Reason {
RootRequire {
package_name: String,
constraint: Constraint,
pretty: String,
},
Fixed { package: usize },
PackageConflict(Link),
PackageRequires(Link),
PackageSameName(String),
Learned(usize),
PackageAlias { alias: usize },
PackageInverseAlias { package: usize },
LockedFilterListRemoved { package: usize },
}
impl Reason {
pub fn code(&self) -> u8 {
match self {
Reason::RootRequire { .. } => 2,
Reason::Fixed { .. } => 3,
Reason::PackageConflict(_) => 6,
Reason::PackageRequires(_) => 7,
Reason::PackageSameName(_) => 10,
Reason::Learned(_) => 12,
Reason::PackageAlias { .. } => 13,
Reason::PackageInverseAlias { .. } => 14,
Reason::LockedFilterListRemoved { .. } => 15,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RuleKind {
Generic,
TwoLiterals,
MultiConflict,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RuleType {
Package = 0,
Request = 1,
Learned = 4,
}
#[derive(Debug, Clone)]
pub struct Rule {
pub kind: RuleKind,
pub literals: Vec<i64>,
pub reason: Reason,
pub rule_type: Option<RuleType>,
pub disabled: bool,
}
impl Rule {
pub fn generic(mut literals: Vec<i64>, reason: Reason) -> Rule {
literals.sort_unstable();
Rule {
kind: RuleKind::Generic,
literals,
reason,
rule_type: None,
disabled: false,
}
}
pub fn two_literals(l1: i64, l2: i64, reason: Reason) -> Rule {
Rule {
kind: RuleKind::TwoLiterals,
literals: if l1 < l2 { vec![l1, l2] } else { vec![l2, l1] },
reason,
rule_type: None,
disabled: false,
}
}
pub fn multi_conflict(mut literals: Vec<i64>, reason: Reason) -> Rule {
literals.sort_unstable();
Rule {
kind: RuleKind::MultiConflict,
literals,
reason,
rule_type: None,
disabled: false,
}
}
pub fn is_assertion(&self) -> bool {
self.kind == RuleKind::Generic && self.literals.len() == 1
}
pub fn is_enabled(&self) -> bool {
!self.disabled
}
pub fn required_package<'a>(&'a self, arena: &'a [crate::package::Package]) -> Option<&'a str> {
match &self.reason {
Reason::RootRequire { package_name, .. } => Some(package_name),
Reason::Fixed { package } | Reason::LockedFilterListRemoved { package } => {
Some(&arena[*package].name)
}
Reason::PackageRequires(link) => Some(&link.target),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct RuleKey {
kind: RuleKind,
literals: Vec<i64>,
}
#[derive(Debug, Default)]
pub struct RuleSet {
pub rules: Vec<Rule>,
pub rule_by_id: Vec<usize>,
by_type: [Vec<usize>; 3],
keys: HashMap<RuleKey, usize>,
}
impl RuleSet {
pub fn new() -> RuleSet {
RuleSet::default()
}
fn slot(rule_type: RuleType) -> usize {
match rule_type {
RuleType::Package => 0,
RuleType::Request => 1,
RuleType::Learned => 2,
}
}
pub fn add(&mut self, mut rule: Rule, rule_type: RuleType) -> (usize, bool) {
let key = RuleKey {
kind: rule.kind,
literals: rule.literals.clone(),
};
let id = self.rules.len();
if self.keys.contains_key(&key) {
self.rules.push(rule);
return (id, false);
}
rule.rule_type = Some(rule_type);
self.rules.push(rule);
self.rule_by_id.push(id);
self.by_type[Self::slot(rule_type)].push(id);
self.keys.insert(key, id);
(id, true)
}
pub fn len(&self) -> usize {
self.rule_by_id.len()
}
pub fn is_empty(&self) -> bool {
self.rule_by_id.is_empty()
}
pub fn ids_of_type(&self, rule_type: RuleType) -> &[usize] {
&self.by_type[Self::slot(rule_type)]
}
pub fn ids_in_iterator_order(&self) -> Vec<usize> {
let mut out = Vec::with_capacity(self.rules.len());
for slot in &self.by_type {
out.extend(slot.iter().copied());
}
out
}
}