aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-sat-resolver/src/rule.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/mozart-sat-resolver/src/rule.rs')
-rw-r--r--crates/mozart-sat-resolver/src/rule.rs280
1 files changed, 0 insertions, 280 deletions
diff --git a/crates/mozart-sat-resolver/src/rule.rs b/crates/mozart-sat-resolver/src/rule.rs
deleted file mode 100644
index 860ae79..0000000
--- a/crates/mozart-sat-resolver/src/rule.rs
+++ /dev/null
@@ -1,280 +0,0 @@
-use crate::pool::{Literal, PoolLink};
-use std::fmt;
-
-/// Why a rule was created.
-/// Port of Composer Rule::RULE_* constants.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum RuleReason {
- /// Root composer.json requirement.
- RootRequire,
- /// Fixed/locked package.
- Fixed,
- /// Two packages conflict.
- PackageConflict,
- /// Package dependency (requires).
- PackageRequires,
- /// Only one version of a package can be installed.
- PackageSameName,
- /// Learned from conflict analysis.
- Learned,
- /// Alias requires its target.
- PackageAlias,
- /// Target requires its alias.
- PackageInverseAlias,
-}
-
-/// Data explaining why a rule was created.
-#[derive(Debug, Clone)]
-pub enum ReasonData {
- /// For RootRequire: package name + constraint string.
- RootRequire {
- package_name: String,
- constraint: String,
- },
- /// For Fixed: the fixed package ID.
- Fixed { package_id: u32 },
- /// For PackageConflict, PackageRequires: a link.
- Link(PoolLink),
- /// For PackageSameName: the package name.
- PackageName(String),
- /// For Learned: index into the learned pool.
- Learned(usize),
- /// For PackageAlias/InverseAlias: the alias package ID.
- AliasPackage(u32),
- /// No data.
- None,
-}
-
-/// The type assigned by RuleSet (which collection this rule belongs to).
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum RuleType {
- Package = 0,
- Request = 1,
- Learned = 4,
-}
-
-/// A SAT rule (clause). A disjunction of literals: (L1 | L2 | ... | Ln).
-///
-/// Port of Composer's Rule hierarchy (GenericRule, Rule2Literals, MultiConflictRule).
-/// In Rust we use a single enum instead of class inheritance.
-#[derive(Debug, Clone)]
-pub struct Rule {
- /// The literals in this rule (sorted for deduplication).
- literals: Vec<Literal>,
- /// Whether this is a multi-conflict rule.
- pub is_multi_conflict: bool,
- /// Why this rule was created.
- pub reason: RuleReason,
- /// Additional data about why this rule was created.
- pub reason_data: ReasonData,
- /// Which RuleSet type this rule belongs to.
- pub rule_type: RuleType,
- /// Whether this rule is disabled.
- pub disabled: bool,
-}
-
-impl Rule {
- /// Create a generic rule (arbitrary number of literals).
- /// Equivalent to Composer's GenericRule.
- pub fn new(mut literals: Vec<Literal>, reason: RuleReason, reason_data: ReasonData) -> Self {
- literals.sort();
- Rule {
- literals,
- is_multi_conflict: false,
- reason,
- reason_data,
- rule_type: RuleType::Package, // default, set by RuleSet
- disabled: false,
- }
- }
-
- /// Create a 2-literal rule (optimized common case).
- /// Equivalent to Composer's Rule2Literals.
- pub fn two_literals(
- lit1: Literal,
- lit2: Literal,
- reason: RuleReason,
- reason_data: ReasonData,
- ) -> Self {
- let (a, b) = if lit1 <= lit2 {
- (lit1, lit2)
- } else {
- (lit2, lit1)
- };
- Rule {
- literals: vec![a, b],
- is_multi_conflict: false,
- reason,
- reason_data,
- rule_type: RuleType::Package,
- disabled: false,
- }
- }
-
- /// Create a multi-conflict rule (3+ literals, all negative).
- /// Equivalent to Composer's MultiConflictRule.
- /// Acts as if it were multiple binary conflict rules.
- pub fn multi_conflict(
- mut literals: Vec<Literal>,
- reason: RuleReason,
- reason_data: ReasonData,
- ) -> Self {
- assert!(
- literals.len() >= 3,
- "MultiConflictRule requires at least 3 literals"
- );
- literals.sort();
- Rule {
- literals,
- is_multi_conflict: true,
- reason,
- reason_data,
- rule_type: RuleType::Package,
- disabled: false,
- }
- }
-
- /// Get the sorted literals.
- pub fn literals(&self) -> &[Literal] {
- &self.literals
- }
-
- /// Whether this rule has exactly one literal (unit clause / assertion).
- pub fn is_assertion(&self) -> bool {
- self.literals.len() == 1
- }
-
- /// Compute a hash for deduplication.
- pub fn hash_key(&self) -> String {
- if self.is_multi_conflict {
- let parts: Vec<String> = self.literals.iter().map(|l| l.to_string()).collect();
- format!("c:{}", parts.join(","))
- } else {
- let parts: Vec<String> = self.literals.iter().map(|l| l.to_string()).collect();
- parts.join(",")
- }
- }
-
- /// Structural equality check (same literals).
- pub fn equals(&self, other: &Rule) -> bool {
- self.is_multi_conflict == other.is_multi_conflict && self.literals == other.literals
- }
-
- /// Get the required package name, if applicable.
- pub fn required_package(&self) -> Option<&str> {
- match &self.reason_data {
- ReasonData::RootRequire { package_name, .. } => Some(package_name),
- ReasonData::Link(link) => Some(&link.target),
- ReasonData::Fixed { .. } => None, // would need pool access
- _ => None,
- }
- }
-
- /// Disable this rule.
- pub fn disable(&mut self) {
- if self.is_multi_conflict {
- panic!("Cannot disable a MultiConflictRule");
- }
- self.disabled = true;
- }
-
- /// Enable this rule.
- pub fn enable(&mut self) {
- self.disabled = false;
- }
-
- /// Whether this rule is disabled.
- pub fn is_disabled(&self) -> bool {
- self.disabled
- }
-
- /// Whether this rule is enabled.
- pub fn is_enabled(&self) -> bool {
- !self.disabled
- }
-}
-
-impl fmt::Display for Rule {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.disabled {
- write!(f, "disabled(")?;
- }
- if self.is_multi_conflict {
- write!(f, "(multi(")?;
- for (i, lit) in self.literals.iter().enumerate() {
- if i > 0 {
- write!(f, "|")?;
- }
- write!(f, "{lit}")?;
- }
- write!(f, "))")?;
- } else {
- write!(f, "(")?;
- for (i, lit) in self.literals.iter().enumerate() {
- if i > 0 {
- write!(f, "|")?;
- }
- write!(f, "{lit}")?;
- }
- write!(f, ")")?;
- }
- if self.disabled {
- write!(f, ")")?;
- }
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_generic_rule() {
- let rule = Rule::new(vec![3, 1, 2], RuleReason::PackageRequires, ReasonData::None);
- assert_eq!(rule.literals(), &[1, 2, 3]);
- assert!(!rule.is_assertion());
- assert_eq!(rule.to_string(), "(1|2|3)");
- }
-
- #[test]
- fn test_two_literal_rule() {
- let rule = Rule::two_literals(-2, -1, RuleReason::PackageConflict, ReasonData::None);
- assert_eq!(rule.literals(), &[-2, -1]);
- assert!(!rule.is_assertion());
- }
-
- #[test]
- fn test_assertion_rule() {
- let rule = Rule::new(vec![1], RuleReason::Fixed, ReasonData::None);
- assert!(rule.is_assertion());
- }
-
- #[test]
- fn test_multi_conflict_rule() {
- let rule = Rule::multi_conflict(
- vec![-3, -1, -2],
- RuleReason::PackageSameName,
- ReasonData::None,
- );
- assert!(rule.is_multi_conflict);
- assert_eq!(rule.literals(), &[-3, -2, -1]);
- }
-
- #[test]
- fn test_hash_key() {
- let r1 = Rule::new(vec![2, 1], RuleReason::PackageRequires, ReasonData::None);
- let r2 = Rule::new(vec![1, 2], RuleReason::PackageConflict, ReasonData::None);
- assert_eq!(r1.hash_key(), r2.hash_key());
- }
-
- #[test]
- fn test_disable_enable() {
- let mut rule = Rule::new(vec![1, 2], RuleReason::PackageRequires, ReasonData::None);
- assert!(rule.is_enabled());
- rule.disable();
- assert!(rule.is_disabled());
- rule.enable();
- assert!(rule.is_enabled());
- }
-}