diff options
Diffstat (limited to 'crates/shirabe/src/dependency_resolver/rule.rs')
| -rw-r--r-- | crates/shirabe/src/dependency_resolver/rule.rs | 173 |
1 files changed, 121 insertions, 52 deletions
diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs index bf117ad..90223e2 100644 --- a/crates/shirabe/src/dependency_resolver/rule.rs +++ b/crates/shirabe/src/dependency_resolver/rule.rs @@ -1,19 +1,24 @@ //! ref: composer/src/Composer/DependencyResolver/Rule.php use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{ - LogicException, PhpMixed, abs, array_filter, array_keys, array_shift, array_values, implode, - is_object, + LogicException, PhpMixed, RuntimeException, abs, array_filter, array_keys, array_shift, + array_values, implode, is_object, }; use shirabe_semver::constraint::Constraint; use shirabe_semver::constraint::ConstraintInterface; +use crate::dependency_resolver::GenericRule; +use crate::dependency_resolver::MultiConflictRule; use crate::dependency_resolver::Pool; use crate::dependency_resolver::Problem; use crate::dependency_resolver::Request; +use crate::dependency_resolver::Rule2Literals; use crate::dependency_resolver::RuleSet; use crate::package::AliasPackage; use crate::package::BasePackage; @@ -23,8 +28,6 @@ use crate::package::version::VersionParser; use crate::repository::PlatformRepository; use crate::repository::RepositorySet; -/// PHP: @phpstan-type ReasonData = Link|BasePackage|string|int|array{...}|array{...} -/// We model this as an enum. #[derive(Debug)] pub enum ReasonData { Link(Link), @@ -70,41 +73,98 @@ pub const BITFIELD_TYPE: i64 = 0; pub const BITFIELD_REASON: i64 = 8; pub const BITFIELD_DISABLED: i64 = 16; -pub trait Rule: std::fmt::Display + std::fmt::Debug { - fn bitfield(&self) -> i64; - fn bitfield_mut(&mut self) -> &mut i64; - fn request(&self) -> Option<&Request>; - fn request_mut(&mut self) -> Option<&mut Request>; - fn reason_data(&self) -> Option<&ReasonData>; - fn reason_data_mut(&mut self) -> Option<&mut ReasonData>; +#[derive(Debug)] +pub enum Rule { + Generic(GenericRule), + MultiConflict(MultiConflictRule), + TwoLiterals(Rule2Literals), +} + +impl Rule { + fn base(&self) -> &RuleBase { + match self { + Rule::Generic(r) => r.base(), + Rule::MultiConflict(r) => r.base(), + Rule::TwoLiterals(r) => r.base(), + } + } + + fn base_mut(&mut self) -> &mut RuleBase { + match self { + Rule::Generic(r) => r.base_mut(), + Rule::MultiConflict(r) => r.base_mut(), + Rule::TwoLiterals(r) => r.base_mut(), + } + } + + fn bitfield(&self) -> i64 { + self.base().bitfield + } + + fn bitfield_mut(&mut self) -> &mut i64 { + &mut self.base_mut().bitfield + } + + fn reason_data(&self) -> Option<&ReasonData> { + self.base().reason_data.as_ref() + } + + pub fn get_literals(&self) -> Vec<i64> { + match self { + Rule::Generic(r) => r.get_literals().clone(), + Rule::MultiConflict(r) => r.get_literals().clone(), + // PHP Rule2Literals::getLiterals returns [$literal1, $literal2]. + Rule::TwoLiterals(r) => vec![r.literal1, r.literal2], + } + } + + pub fn get_hash(&self) -> Result<PhpMixed> { + match self { + Rule::Generic(r) => Ok(PhpMixed::Int(r.get_hash()?)), + Rule::MultiConflict(r) => Ok(PhpMixed::Int(r.get_hash()?)), + Rule::TwoLiterals(r) => Ok(PhpMixed::String(r.get_hash())), + } + } - fn get_literals(&self) -> Vec<i64>; - fn get_hash(&self) -> PhpMixed; - fn equals(&self, rule: &dyn Rule) -> bool; - fn is_assertion(&self) -> bool; + pub fn equals(&self, rule: &Rule) -> bool { + match self { + Rule::Generic(r) => r.equals(rule), + Rule::MultiConflict(r) => r.equals(rule), + Rule::TwoLiterals(r) => r.equals(rule), + } + } + + pub fn is_assertion(&self) -> bool { + match self { + Rule::Generic(r) => r.is_assertion(), + Rule::MultiConflict(r) => r.is_assertion(), + Rule::TwoLiterals(r) => r.is_assertion(), + } + } - fn clone_box(&self) -> Box<dyn Rule> { - todo!() + pub fn is_multi_conflict_rule(&self) -> bool { + matches!(self, Rule::MultiConflict(_)) } - /// PHP: `$rule instanceof MultiConflictRule`. Returns a borrow of the - /// underlying `MultiConflictRule` when this rule is one, otherwise `None`. - fn as_multi_conflict(&self) -> Option<&crate::dependency_resolver::MultiConflictRule> { - None + pub fn as_multi_conflict(&self) -> Option<&MultiConflictRule> { + match self { + Rule::MultiConflict(r) => Some(r), + _ => None, + } } /// @return self::RULE_* - fn get_reason(&self) -> i64 { + pub fn get_reason(&self) -> i64 { (self.bitfield() & (255 << BITFIELD_REASON)) >> BITFIELD_REASON } /// @phpstan-return ReasonData - fn get_reason_data(&self) -> &ReasonData { + pub fn get_reason_data(&self) -> &ReasonData { // TODO(phase-b): reason_data() returns Option; PHP getReasonData unconditional self.reason_data().unwrap() } - fn get_required_package(&self) -> Option<String> { + pub fn get_required_package(&self) -> Option<String> { match self.get_reason() { r if r == RULE_ROOT_REQUIRE => match self.get_reason_data() { ReasonData::RootRequire { package_name, .. } => Some(package_name.clone()), @@ -123,33 +183,41 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { } /// @param RuleSet::TYPE_* $type - fn set_type(&mut self, r#type: i64) { + pub fn set_type(&mut self, r#type: i64) { *self.bitfield_mut() = (self.bitfield() & !(255i64 << BITFIELD_TYPE)) | ((255 & r#type) << BITFIELD_TYPE); } - fn get_type(&self) -> i64 { + pub fn get_type(&self) -> i64 { (self.bitfield() & (255 << BITFIELD_TYPE)) >> BITFIELD_TYPE } - fn disable(&mut self) { + pub fn disable(&mut self) -> Result<()> { + if let Rule::MultiConflict(_) = self { + return Err(RuntimeException { + message: "Disabling multi conflict rules is not possible. Please contact composer at https://github.com/composer/composer to let us debug what lead to this situation.".to_string(), + code: 0, + } + .into()); + } *self.bitfield_mut() = (self.bitfield() & !(255i64 << BITFIELD_DISABLED)) | (1i64 << BITFIELD_DISABLED); + Ok(()) } - fn enable(&mut self) { + pub fn enable(&mut self) { *self.bitfield_mut() &= !(255i64 << BITFIELD_DISABLED); } - fn is_disabled(&self) -> bool { + pub fn is_disabled(&self) -> bool { 0 != ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED) } - fn is_enabled(&self) -> bool { + pub fn is_enabled(&self) -> bool { 0 == ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED) } - fn is_caused_by_lock( + pub fn is_caused_by_lock( &self, _repository_set: &RepositorySet, request: &Request, @@ -221,7 +289,7 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { } /// @internal - fn get_source_package(&self, pool: &Pool) -> Result<Box<dyn BasePackage>> { + pub fn get_source_package(&self, pool: &Pool) -> Result<Box<dyn BasePackage>> { let literals = self.get_literals(); match self.get_reason() { @@ -263,14 +331,14 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { /// @param BasePackage[] $installedMap /// @param array<Rule[]> $learnedPool - fn get_pretty_string( + pub fn get_pretty_string( &self, repository_set: &RepositorySet, request: &Request, pool: &mut Pool, is_verbose: bool, installed_map: &IndexMap<String, Box<dyn BasePackage>>, - _learned_pool: &Vec<Vec<Box<dyn Rule>>>, + _learned_pool: &Vec<Vec<Rc<RefCell<Rule>>>>, ) -> String { let mut literals = self.get_literals(); @@ -295,7 +363,6 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { ); } - // PHP: array_values(array_filter($packages, fn ($p) => !($p instanceof AliasPackage))) let packages_non_alias: Vec<Box<dyn BasePackage>> = packages .iter() .filter(|p| p.as_any().downcast_ref::<AliasPackage>().is_none()) @@ -317,7 +384,7 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { "Root composer.json requires {} {} -> satisfiable by {}.", package_name, constraint.get_pretty_string(), - self.format_packages_unique( + self.format_packages_unique_from_packages( pool, packages, is_verbose, @@ -437,7 +504,7 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { format!( "{} -> satisfiable by {}.", text, - self.format_packages_unique( + self.format_packages_unique_from_packages( pool, requires, is_verbose, @@ -467,7 +534,6 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { let package = pool.literal_to_package(*literal); package_names.insert(package.get_name().to_string(), true); } - // PHP: unset($literal); let replaced_name = match self.get_reason_data() { ReasonData::String(s) => s.clone(), _ => String::new(), @@ -510,14 +576,14 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { if installed_packages.len() > 0 && removable_packages.len() > 0 { return format!( "{} cannot be installed as that would require removing {}. {}", - self.format_packages_unique( + self.format_packages_unique_from_packages( pool, removable_packages, is_verbose, None, true, ), - self.format_packages_unique( + self.format_packages_unique_from_packages( pool, installed_packages, is_verbose, @@ -576,7 +642,7 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { "{}{} {}", group, if packages.len() > 1 { " one of" } else { "" }, - self.format_packages_unique( + self.format_packages_unique_from_packages( pool, packages.iter().map(|p| p.clone_box()).collect(), is_verbose, @@ -640,22 +706,15 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { } } - /// @param array<int|BasePackage> $literalsOrPackages An array containing packages or literals - fn format_packages_unique( + // Corresponds the variant formatPackagesUnique() that takes an array of BasePackages. + fn format_packages_unique_from_packages( &self, pool: &Pool, - literals_or_packages: Vec<Box<dyn BasePackage>>, + packages: Vec<Box<dyn BasePackage>>, is_verbose: bool, constraint: Option<&dyn ConstraintInterface>, use_removed_version_group: bool, ) -> String { - let mut packages: Vec<Box<dyn BasePackage>> = vec![]; - for package in literals_or_packages { - // PHP: \is_object($package) ? $package : $pool->literalToPackage($package); - // In Rust we already have BasePackage, so no conversion needed. - packages.push(package); - } - Problem::get_package_list( &packages, is_verbose, @@ -665,7 +724,7 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { ) } - /// Helper for cases where literals come as int IDs (PHP supports both via union). + // Corresponds the variant formatPackagesUnique() that takes an array of integers. fn format_packages_unique_from_literals( &self, pool: &Pool, @@ -701,6 +760,16 @@ pub trait Rule: std::fmt::Display + std::fmt::Debug { } } +impl std::fmt::Display for Rule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Rule::Generic(r) => write!(f, "{}", r), + Rule::MultiConflict(r) => write!(f, "{}", r), + Rule::TwoLiterals(r) => write!(f, "{}", r), + } + } +} + #[derive(Debug)] pub struct RuleBase { pub(crate) bitfield: i64, |
