diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-19 21:46:01 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-19 21:46:08 +0900 |
| commit | 5e31fa33c3b5cf726a57a063b8e7a070869250fe (patch) | |
| tree | 98522466966fa7df483cad174ab5fc03db39bc09 /crates/shirabe/src/dependency_resolver | |
| parent | c839244d8d09f3036ebfee8eef7eb6b147e593ab (diff) | |
| download | php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.tar.gz php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.tar.zst php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.zip | |
fix(compile): fix more random compile errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/dependency_resolver')
23 files changed, 345 insertions, 234 deletions
diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs index 9d8f96c..5d37855 100644 --- a/crates/shirabe/src/dependency_resolver/default_policy.rs +++ b/crates/shirabe/src/dependency_resolver/default_policy.rs @@ -51,12 +51,8 @@ impl DefaultPolicy { ignore_replace: bool, ) -> i64 { if PackageInterface::get_name(a) == PackageInterface::get_name(b) { - let a_aliased = (a.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); - let b_aliased = (b.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); + let a_aliased = a.as_any().downcast_ref::<AliasPackage>().is_some(); + let b_aliased = b.as_any().downcast_ref::<AliasPackage>().is_some(); if a_aliased && !b_aliased { return -1; } @@ -87,11 +83,11 @@ impl DefaultPolicy { } } - if a.id == b.id { + if a.id() == b.id() { return 0; } - if a.id < b.id { -1 } else { 1 } + if a.id() < b.id() { -1 } else { 1 } } pub(crate) fn group_literals_by_name( @@ -147,7 +143,7 @@ impl DefaultPolicy { for &literal in &literals { let package = pool.literal_to_package(literal); - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { if alias_pkg.is_root_package_alias() { has_local_alias = true; break; @@ -162,7 +158,7 @@ impl DefaultPolicy { let mut selected = vec![]; for &literal in &literals { let package = pool.literal_to_package(literal); - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { if alias_pkg.is_root_package_alias() { selected.push(literal); } @@ -221,7 +217,7 @@ impl PolicyInterface for DefaultPolicy { CompilingMatcher::r#match( &Constraint::new(operator, b.get_version()), Constraint::OP_EQ, - a.get_version(), + a.get_version().to_string(), ) } diff --git a/crates/shirabe/src/dependency_resolver/generic_rule.rs b/crates/shirabe/src/dependency_resolver/generic_rule.rs index 19974b6..c0381ef 100644 --- a/crates/shirabe/src/dependency_resolver/generic_rule.rs +++ b/crates/shirabe/src/dependency_resolver/generic_rule.rs @@ -6,6 +6,7 @@ use shirabe_php_shim::{PHP_VERSION_ID, PhpMixed, RuntimeException, hash_raw, imp use super::{request::Request, rule::ReasonData}; +#[derive(Debug)] pub struct GenericRule { inner: RuleBase, pub(crate) literals: Vec<i64>, @@ -13,7 +14,7 @@ pub struct GenericRule { impl GenericRule { pub fn new(mut literals: Vec<i64>, reason: PhpMixed, reason_data: PhpMixed) -> Self { - let inner = RuleBase::new(reason, reason_data); + let inner = RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data)); literals.sort(); Self { inner, literals } } @@ -70,6 +71,20 @@ pub trait RuleLiterals { fn is_multi_conflict_rule(&self) -> bool { false } + fn is_assertion(&self) -> bool { + false + } + fn is_disabled(&self) -> bool { + false + } + fn as_any(&self) -> &dyn std::any::Any { + todo!() + } + /// Clone this rule into an owned `Box<dyn Rule>` so callers like + /// `RuleWatchGraph::propagate_literal` can hand it to `Decisions::decide`. + fn clone_rule_box(&self) -> Box<dyn Rule> { + todo!() + } } impl RuleLiterals for GenericRule { diff --git a/crates/shirabe/src/dependency_resolver/lock_transaction.rs b/crates/shirabe/src/dependency_resolver/lock_transaction.rs index db9eb90..aa49d17 100644 --- a/crates/shirabe/src/dependency_resolver/lock_transaction.rs +++ b/crates/shirabe/src/dependency_resolver/lock_transaction.rs @@ -116,10 +116,7 @@ impl LockTransaction { .map(|v| v.as_slice()) .unwrap_or_default(); for package in source { - if (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some() - { + if package.as_any().downcast_ref::<AliasPackage>().is_some() { continue; } @@ -152,9 +149,7 @@ impl LockTransaction { continue; } - if let Some(concrete_pkg) = - (present_package.as_any() as &dyn Any).downcast_ref::<Package>() - { + if let Some(concrete_pkg) = present_package.as_any().downcast_ref::<Package>() { concrete_pkg.set_source_url(package.get_source_url()); concrete_pkg.set_source_mirrors(package.get_source_mirrors()); } @@ -171,9 +166,9 @@ impl LockTransaction { r"{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i", present_package.get_dist_reference().unwrap(), package.get_dist_url().unwrap(), - -1, - ).unwrap_or_else(|_| package.get_dist_url().unwrap().to_string()); - present_package.set_dist_url(&new_dist_url); + ) + .unwrap_or_else(|_| package.get_dist_url().unwrap().to_string()); + present_package.set_dist_url(Some(new_dist_url)); } present_package.set_dist_mirrors(package.get_dist_mirrors()); @@ -192,10 +187,7 @@ impl LockTransaction { if let Some(all_packages) = self.result_packages.get("all") { for package in all_packages { - if (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some() - { + if package.as_any().downcast_ref::<AliasPackage>().is_some() { let mut i = 0; while i < remaining_aliases.len() { if remaining_aliases[i].get("package").map(|s| s.as_str()) diff --git a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs index d198a27..14ece50 100644 --- a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs +++ b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs @@ -32,7 +32,7 @@ impl MultiConflictRule { literals.sort(); Ok(Self { - inner: RuleBase::new(reason, reason_data), + inner: RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data)), literals, }) } diff --git a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs index 1835636..53b5f99 100644 --- a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs @@ -34,6 +34,10 @@ impl SolverOperation for InstallOperation { } impl OperationInterface for InstallOperation { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_operation_type(&self) -> String { Self::TYPE.to_string() } @@ -45,4 +49,8 @@ impl OperationInterface for InstallOperation { fn to_string(&self) -> String { self.show(true) } + + fn as_install_operation(&self) -> Option<&InstallOperation> { + Some(self) + } } diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs index e5a7df9..fa6f13d 100644 --- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs @@ -25,6 +25,10 @@ impl SolverOperation for MarkAliasInstalledOperation { } impl OperationInterface for MarkAliasInstalledOperation { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_operation_type(&self) -> String { Self::TYPE.to_string() } diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs index 21e257d..b9d5d26 100644 --- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs @@ -25,6 +25,10 @@ impl SolverOperation for MarkAliasUninstalledOperation { } impl OperationInterface for MarkAliasUninstalledOperation { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_operation_type(&self) -> String { Self::TYPE.to_string() } diff --git a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs index d93bd0f..f90649d 100644 --- a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs +++ b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs @@ -1,6 +1,12 @@ //! ref: composer/src/Composer/DependencyResolver/Operation/OperationInterface.php +use crate::dependency_resolver::operation::install_operation::InstallOperation; +use crate::dependency_resolver::operation::uninstall_operation::UninstallOperation; +use crate::dependency_resolver::operation::update_operation::UpdateOperation; + pub trait OperationInterface: std::fmt::Debug { + fn as_any(&self) -> &dyn std::any::Any; + fn get_operation_type(&self) -> String; fn show(&self, lock: bool) -> String; @@ -10,4 +16,16 @@ pub trait OperationInterface: std::fmt::Debug { fn clone_box(&self) -> Box<dyn OperationInterface> { todo!() } + + fn as_install_operation(&self) -> Option<&InstallOperation> { + None + } + + fn as_update_operation(&self) -> Option<&UpdateOperation> { + None + } + + fn as_uninstall_operation(&self) -> Option<&UninstallOperation> { + None + } } diff --git a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs index 138a8c8..be7f6f1 100644 --- a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs @@ -33,6 +33,10 @@ impl SolverOperation for UninstallOperation { } impl OperationInterface for UninstallOperation { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_operation_type(&self) -> String { Self::TYPE.to_string() } @@ -44,4 +48,8 @@ impl OperationInterface for UninstallOperation { fn to_string(&self) -> String { self.show(true) } + + fn as_uninstall_operation(&self) -> Option<&UninstallOperation> { + Some(self) + } } diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs index c1498ad..9adb248 100644 --- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs +++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs @@ -79,6 +79,10 @@ impl SolverOperation for UpdateOperation { } impl OperationInterface for UpdateOperation { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_operation_type(&self) -> String { Self::TYPE.to_string() } @@ -94,4 +98,8 @@ impl OperationInterface for UpdateOperation { fn to_string(&self) -> String { self.show(true) } + + fn as_update_operation(&self) -> Option<&UpdateOperation> { + Some(self) + } } diff --git a/crates/shirabe/src/dependency_resolver/policy_interface.rs b/crates/shirabe/src/dependency_resolver/policy_interface.rs index f3cc1a0..606386f 100644 --- a/crates/shirabe/src/dependency_resolver/policy_interface.rs +++ b/crates/shirabe/src/dependency_resolver/policy_interface.rs @@ -3,7 +3,7 @@ use crate::dependency_resolver::pool::Pool; use crate::package::package_interface::PackageInterface; -pub trait PolicyInterface { +pub trait PolicyInterface: std::fmt::Debug { fn version_compare( &self, a: &dyn PackageInterface, diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs index 96e608b..a194d15 100644 --- a/crates/shirabe/src/dependency_resolver/pool.rs +++ b/crates/shirabe/src/dependency_resolver/pool.rs @@ -193,7 +193,7 @@ impl Pool { let mut id: i64 = 1; for mut package in packages { - package.id = id; + *package.id_mut() = id; id += 1; for provided in package.get_names(true) { @@ -285,7 +285,7 @@ impl Pool { ) -> String { let package = self.literal_to_package(literal); - let prefix = if installed_map.contains_key(&package.id) { + let prefix = if installed_map.contains_key(&package.id()) { if literal > 0 { "keep" } else { "remove" } } else { if literal > 0 { @@ -391,7 +391,7 @@ impl fmt::Display for Pool { for package in &self.packages { str.push_str(&format!( "- {}: {}\n", - str_pad(&package.id.to_string(), 6, " ", STR_PAD_LEFT), + str_pad(&package.id().to_string(), 6, " ", STR_PAD_LEFT), package.get_name() )); } diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index ea860f2..3043aaa 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -152,19 +152,12 @@ impl PoolBuilder { .into()); } - for locked_package in request.get_locked_repository().unwrap().get_packages() { + for locked_package in + CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap()) + { if !self.is_update_allowed(&*locked_package) { - // remember which packages we skipped loading remote content for in this partial update - self.skipped_load - .entry(locked_package.get_name().to_string()) - .or_insert_with(Vec::new) - .push(locked_package.clone_box()); - for (_k, link) in &locked_package.get_replaces() { - self.skipped_load - .entry(link.get_target().to_string()) - .or_insert_with(Vec::new) - .push(locked_package.clone_box()); - } + // TODO(phase-b): PackageInterface lacks clone_box; PHP shares references. + // skipped_load population needs shared-ownership Rc<dyn PackageInterface>. // Path repo packages are never loaded from lock, to force them to always remain in sync // unless symlinking is disabled in which case we probably should rather treat them like @@ -188,7 +181,7 @@ impl PoolBuilder { } } - for package in request.get_fixed_or_locked_packages() { + for (_, package) in request.get_fixed_or_locked_packages() { // using MatchAllConstraint here because fixed packages do not need to retrigger // loading any packages self.loaded_packages.insert( @@ -369,8 +362,9 @@ impl PoolBuilder { &mut self, request: &Request, name: &str, - constraint: Box<dyn ConstraintInterface>, + constraint: &dyn ConstraintInterface, ) { + let constraint = constraint.clone_box(); // Skip platform requires at this stage if PlatformRepository::is_platform_package(name) { return; @@ -389,7 +383,7 @@ impl PoolBuilder { let root_requires = request.get_requires(); let mut constraint = constraint; if let Some(root_constraint) = root_requires.get(name) { - if !Intervals::is_subset_of(&*constraint, &**root_constraint) { + if !Intervals::is_subset_of(&*constraint, &**root_constraint)? { constraint = root_constraint.clone_box(); } } @@ -401,7 +395,7 @@ impl PoolBuilder { // MultiConstraint::create() will optimize anyway) if let Some(existing) = self.packages_to_load.get(name) { // Already marked for loading and this does not expand the constraint to be loaded, nothing to do - if Intervals::is_subset_of(&*constraint, &**existing) { + if Intervals::is_subset_of(&*constraint, &**existing).unwrap_or(false) { return; } @@ -419,7 +413,9 @@ impl PoolBuilder { // No need to load this package with this constraint because it is // a subset of the constraint with which we have already loaded packages - if Intervals::is_subset_of(&*constraint, &**self.loaded_packages.get(name).unwrap()) { + if Intervals::is_subset_of(&*constraint, &**self.loaded_packages.get(name).unwrap()) + .unwrap_or(false) + { return; } @@ -762,7 +758,7 @@ impl PoolBuilder { fn is_update_allowed(&self, package: &dyn BasePackage) -> bool { for pattern in &self.update_allow_list { let pattern_regexp = base_package::package_name_to_regexp(pattern); - if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None) + if Preg::is_match3(&pattern_regexp, PackageInterface::get_name(package), None) .unwrap_or(false) { return true; @@ -786,8 +782,10 @@ impl PoolBuilder { let pattern_regexp = base_package::package_name_to_regexp(pattern); // update pattern matches a locked package? => all good - for package in request.get_locked_repository().unwrap().get_packages() { - if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None) + for package in + CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap()) + { + if Preg::is_match3(&pattern_regexp, PackageInterface::get_name(package), None) .unwrap_or(false) { continue 'outer; @@ -795,7 +793,7 @@ impl PoolBuilder { } // update pattern matches a root require? => all good, probably a new package for (package_name, _constraint) in &request.get_requires() { - if Preg::is_match(&pattern_regexp, package_name, None).unwrap_or(false) { + if Preg::is_match3(&pattern_regexp, package_name, None).unwrap_or(false) { if PlatformRepository::is_platform_package(package_name) { matched_platform_package = true; continue; @@ -855,7 +853,7 @@ impl PoolBuilder { self.mark_package_name_for_loading( request, &replacer_name, - Box::new(MatchAllConstraint::new()), + &MatchAllConstraint::new(), ); } else { let pkgs: Vec<Box<dyn BasePackage>> = @@ -895,7 +893,7 @@ impl PoolBuilder { // remove locked package by this name which was already initialized let locked_packages: Vec<Box<dyn BasePackage>> = request .get_locked_packages() - .iter() + .values() .map(|p| p.clone_box()) .collect(); for locked_package in &locked_packages { @@ -914,7 +912,7 @@ impl PoolBuilder { // that we load that replaced package in case an update to this package removes the replacement let fixed_or_locked: Vec<Box<dyn BasePackage>> = request .get_fixed_or_locked_packages() - .iter() + .values() .map(|p| p.clone_box()) .collect(); for fixed_or_locked_package in &fixed_or_locked { @@ -1049,7 +1047,7 @@ impl PoolBuilder { return pool; } - self.io.write_with_verbosity( + self.io.write3( &sprintf( "Pool optimizer completed in %.3f seconds", &[(microtime(true) - before).into()], @@ -1057,7 +1055,7 @@ impl PoolBuilder { true, io_interface::VERY_VERBOSE, ); - self.io.write_with_verbosity( + self.io.write3( &sprintf( "<info>Found %s package versions referenced in your dependency graph. %s (%d%%) were optimized away.</info>", &[ @@ -1100,7 +1098,7 @@ impl PoolBuilder { return pool; } - self.io.write_with_verbosity( + self.io.write3( &sprintf( "Security advisory pool filter completed in %.3f seconds", &[(microtime(true) - before).into()], @@ -1108,7 +1106,7 @@ impl PoolBuilder { true, io_interface::VERY_VERBOSE, ); - self.io.write_with_verbosity( + self.io.write3( &sprintf( "<info>Found %s package versions referenced in your dependency graph. %s (%d%%) were filtered away.</info>", &[ diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs index 5307a1c..3da36b1 100644 --- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs +++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs @@ -130,9 +130,9 @@ impl PoolOptimizer { // Keep track of alias packages for every package so if either the alias or aliased is kept // we keep the others as they are a unit of packages really - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { self.aliases_per_package - .entry(alias_pkg.get_alias_of().id) + .entry(alias_pkg.get_alias_of().id()) .or_insert_with(Vec::new) .push(package.clone_box()); } @@ -175,8 +175,8 @@ impl PoolOptimizer { } fn mark_package_irremovable(&mut self, package: &dyn BasePackage) { - self.irremovable_packages.insert(package.id, true); - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + self.irremovable_packages.insert(package.id(), true); + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { // recursing here so aliasesPerPackage for the aliasOf can be checked // and all its aliases marked as irremovable as well self.mark_package_irremovable(alias_pkg.get_alias_of()); @@ -184,8 +184,8 @@ impl PoolOptimizer { // PHP: foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) let alias_ids: Vec<i64> = self .aliases_per_package - .get(&package.id) - .map(|aliases| aliases.iter().map(|a| a.id).collect()) + .get(&package.id()) + .map(|aliases| aliases.iter().map(|a| a.id()).collect()) .unwrap_or_default(); for alias_id in alias_ids { self.irremovable_packages.insert(alias_id, true); @@ -197,7 +197,7 @@ impl PoolOptimizer { let mut packages: Vec<Box<dyn BasePackage>> = vec![]; let mut removed_versions: IndexMap<String, IndexMap<String, String>> = IndexMap::new(); for package in pool.get_packages() { - if !self.packages_to_remove.contains_key(&package.id) { + if !self.packages_to_remove.contains_key(&package.id()) { packages.push(package.clone_box()); } else { removed_versions @@ -238,11 +238,11 @@ impl PoolOptimizer { for package in pool.get_packages() { // If that package was already marked irremovable, we can skip // the entire process for it - if self.irremovable_packages.contains_key(&package.id) { + if self.irremovable_packages.contains_key(&package.id()) { continue; } - self.mark_package_for_removal(package.id)?; + self.mark_package_for_removal(package.id())?; let dependency_hash = self.calculate_dependency_hash(package.as_ref()); @@ -311,7 +311,7 @@ impl PoolOptimizer { .or_insert_with(Vec::new) .push(package.clone_box()); package_identical_definition_lookup - .entry(package.id) + .entry(package.id()) .or_insert_with(IndexMap::new) .insert( package_name.clone(), @@ -344,7 +344,7 @@ impl PoolOptimizer { let mut literals: Vec<i64> = vec![]; for package in packages { - literals.push(package.id); + literals.push(package.id()); } for preferred_literal in @@ -439,13 +439,13 @@ impl PoolOptimizer { >, ) { // Already marked to keep - if !self.packages_to_remove.contains_key(&package.id) { + if !self.packages_to_remove.contains_key(&package.id()) { return; } - self.packages_to_remove.shift_remove(&package.id); + self.packages_to_remove.shift_remove(&package.id()); - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { // recursing here so aliasesPerPackage for the aliasOf can be checked // and all its aliases marked to be kept as well self.keep_package( @@ -457,7 +457,7 @@ impl PoolOptimizer { // record all the versions of the package group so we can list them later in Problem output for name in package.get_names(false) { - if let Some(per_name) = package_identical_definition_lookup.get(&package.id) { + if let Some(per_name) = package_identical_definition_lookup.get(&package.id()) { if let Some(package_group_pointers) = per_name.get(&name) { let package_group = identical_definitions_per_package .get(&name) @@ -466,7 +466,7 @@ impl PoolOptimizer { if let Some(package_group) = package_group { for pkg in package_group { let pkg = if let Some(alias_pkg) = - (pkg.as_any() as &dyn Any).downcast_ref::<AliasPackage>() + pkg.as_any().downcast_ref::<AliasPackage>() { if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS @@ -493,8 +493,13 @@ impl PoolOptimizer { let alias_info: Vec<(i64, Vec<String>)> = self .aliases_per_package - .get(&package.id) - .map(|aliases| aliases.iter().map(|a| (a.id, a.get_names(false))).collect()) + .get(&package.id()) + .map(|aliases| { + aliases + .iter() + .map(|a| (a.id(), a.get_names(false))) + .collect() + }) .unwrap_or_default(); for (alias_id, alias_names) in alias_info { self.packages_to_remove.shift_remove(&alias_id); @@ -510,7 +515,7 @@ impl PoolOptimizer { if let Some(package_group) = package_group { for pkg in package_group { let pkg = if let Some(alias_pkg) = - (pkg.as_any() as &dyn Any).downcast_ref::<AliasPackage>() + pkg.as_any().downcast_ref::<AliasPackage>() { if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS @@ -549,7 +554,7 @@ impl PoolOptimizer { IndexMap::new(); for package in pool.get_packages() { - let id = package.id; + let id = package.id(); // Do not remove irremovable packages if self.irremovable_packages.contains_key(&id) { @@ -557,9 +562,7 @@ impl PoolOptimizer { } // Do not remove a package aliased by another package, nor aliases if self.aliases_per_package.contains_key(&id) - || (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some() + || package.as_any().downcast_ref::<AliasPackage>().is_some() { continue; } @@ -573,7 +576,7 @@ impl PoolOptimizer { package_index .entry(PackageInterface::get_name(package.as_ref()).to_string()) .or_insert_with(IndexMap::new) - .insert(package.id, package.clone_box()); + .insert(package.id(), package.clone_box()); } for (_, package) in request.get_locked_packages() { @@ -671,10 +674,9 @@ impl PoolOptimizer { &self, constraint: Box<dyn ConstraintInterface>, ) -> Vec<Box<dyn ConstraintInterface>> { - // TODO(phase-b): Intervals::compactConstraint expects/returns ConstraintInterface - let constraint = Intervals::compact_constraint(constraint); + let constraint = Intervals::compact_constraint(&*constraint).unwrap_or(constraint); - if let Some(multi) = (constraint.as_ref() as &dyn Any).downcast_ref::<MultiConstraint>() { + if let Some(multi) = constraint.as_any().downcast_ref::<MultiConstraint>() { if multi.is_disjunctive() { // No need to call ourselves recursively here because Intervals::compactConstraint() ensures that there // are no nested disjunctive MultiConstraint instances possible diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs index 8424941..e78ee14 100644 --- a/crates/shirabe/src/dependency_resolver/problem.rs +++ b/crates/shirabe/src/dependency_resolver/problem.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg}; use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter; use shirabe_php_shim::{ LogicException, PhpMixed, defined, extension_loaded, implode, in_array, php_to_string, @@ -212,7 +212,8 @@ impl Problem { installed_map, learned_pool, ); - let m_opt = if in_array( + let mut m: IndexMap<CaptureKey, String> = IndexMap::new(); + let matched = if in_array( PhpMixed::Int(rule.get_reason()), &PhpMixed::List( deduplicatable_rule_types @@ -222,27 +223,29 @@ impl Problem { ), true, ) { - Preg::is_match_strict_groups( + Preg::is_match_strict_groups3( r"{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}", &message, + Some(&mut m), ) - .unwrap_or(None) + .unwrap_or(false) } else { - None + false }; - if let Some(m) = m_opt { + if matched { message = str_replace("%", "%%", &message); let template = Preg::replace(r"{^\S+ \S+ }", "%s%s ", &message).unwrap_or(message.clone()); messages.push(template.clone()); - let pkg_key = m[1].clone(); - let version_key = parser.normalize(&m[2], "").unwrap_or_default(); + let pkg_key = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(); + let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(); + let version_key = parser.normalize(&m2, Some("")).unwrap_or_default(); templates .entry(template.clone()) .or_insert_with(IndexMap::new) .entry(pkg_key.clone()) .or_insert_with(IndexMap::new) - .insert(version_key, m[2].clone()); + .insert(version_key, m2.clone()); let source_package = rule.get_source_package(pool); for (version, pretty_version) in pool.get_removed_versions_by_package(&spl_object_hash(&source_package)) @@ -533,7 +536,7 @@ impl Problem { } } - let mut locked_package: Option<BasePackage> = None; + let mut locked_package: Option<Box<dyn BasePackage>> = None; for package in request.get_locked_packages() { if package.get_name() == package_name { locked_package = Some(package.clone()); @@ -554,7 +557,7 @@ impl Problem { if let Some(c) = constraint { if c.is_constraint() && c.get_operator() == Constraint::STR_OP_EQ - && Preg::is_match(r"{^dev-.*#.*}", &c.get_pretty_string(), None).unwrap_or(false) + && Preg::is_match3(r"{^dev-.*#.*}", &c.get_pretty_string(), None).unwrap_or(false) { let new_constraint = Preg::replace(r"{ +as +([^,\s|]+)$}", "", &c.get_pretty_string()) @@ -606,7 +609,7 @@ impl Problem { let filtered: Vec<&Box<dyn PackageInterface>> = packages .iter() .filter(|p| { - root_reqs[package_name].matches(&Constraint::new("==", &p.get_version())) + root_reqs[package_name].matches(&Constraint::new("==", p.get_version())) }) .collect(); if filtered.len() == 0 { @@ -638,12 +641,12 @@ impl Problem { let temp_reqs = repository_set.get_temporary_constraints(); let first_pkg = packages.first().unwrap(); - for name in first_pkg.get_names() { + for name in first_pkg.get_names(true) { if temp_reqs.contains_key(&name) { let filtered: Vec<&Box<dyn PackageInterface>> = packages .iter() .filter(|p| { - temp_reqs[&name].matches(&Constraint::new("==", &p.get_version())) + temp_reqs[&name].matches(&Constraint::new("==", p.get_version())) }) .collect(); if filtered.len() == 0 { @@ -676,10 +679,10 @@ impl Problem { } if let Some(ref lp) = locked_package { - let fixed_constraint = Constraint::new("==", &lp.get_version()); + let fixed_constraint = Constraint::new("==", lp.get_version()); let filtered: Vec<&Box<dyn PackageInterface>> = packages .iter() - .filter(|p| fixed_constraint.matches(&Constraint::new("==", &p.get_version()))) + .filter(|p| fixed_constraint.matches(&Constraint::new("==", p.get_version()))) .collect(); if filtered.len() == 0 { return ( @@ -763,22 +766,22 @@ impl Problem { return format!( "<href={}>{}</>", OutputFormatter::escape(advisory.link.as_ref().unwrap()), - advisory.advisory_id + advisory.inner.advisory_id ); } - if str_starts_with(&advisory.advisory_id, "PKSA-") { + if str_starts_with(&advisory.inner.advisory_id, "PKSA-") { return format!( "<href={}>{}</>", OutputFormatter::escape(&format!( "https://packagist.org/security-advisories/{}", - advisory.advisory_id + advisory.inner.advisory_id )), - advisory.advisory_id + advisory.inner.advisory_id ); } - advisory.advisory_id.clone() + advisory.inner.advisory_id.clone() }) .collect() } else { @@ -961,7 +964,7 @@ impl Problem { ); } - if !Preg::is_match(r"{^[A-Za-z0-9_./-]+$}", package_name, None).unwrap_or(false) { + if !Preg::is_match3(r"{^[A-Za-z0-9_./-]+$}", package_name, None).unwrap_or(false) { let illegal_chars = Preg::replace(r"{[A-Za-z0-9_./-]+}", "", package_name).unwrap_or_default(); @@ -1365,7 +1368,7 @@ impl Problem { && c.get_operator() == Constraint::STR_OP_EQ && !str_starts_with(&c.get_version(), "dev-") { - if !Preg::is_match(r"{^\d+(?:\.\d+)*$}", &c.get_pretty_string(), None) + if !Preg::is_match3(r"{^\d+(?:\.\d+)*$}", &c.get_pretty_string(), None) .unwrap_or(false) { return format!(" {} (exact version match)", c.get_pretty_string()); diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs index 23a5981..e54df18 100644 --- a/crates/shirabe/src/dependency_resolver/rule.rs +++ b/crates/shirabe/src/dependency_resolver/rule.rs @@ -37,6 +37,21 @@ pub enum ReasonData { Fixed { package: Box<dyn BasePackage>, }, + /// Phase B placeholder for an arbitrary PHP-side value not yet mapped to a real variant. + Mixed(PhpMixed), +} + +impl From<PhpMixed> for ReasonData { + fn from(value: PhpMixed) -> Self { + // TODO(phase-b): callers should construct the appropriate variant directly; + // this catch-all keeps the rule constructors building while reason_data threading + // through PhpMixed in the resolver is still in transition. + match value { + PhpMixed::String(s) => ReasonData::String(s), + PhpMixed::Int(i) => ReasonData::Int(i), + other => ReasonData::Mixed(other), + } + } } // reason constants and // their reason data contents @@ -54,7 +69,7 @@ pub const BITFIELD_TYPE: i64 = 0; pub const BITFIELD_REASON: i64 = 8; pub const BITFIELD_DISABLED: i64 = 16; -pub trait Rule: std::fmt::Display { +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>; @@ -67,14 +82,19 @@ pub trait Rule: std::fmt::Display { fn equals(&self, rule: &dyn Rule) -> bool; fn is_assertion(&self) -> bool; + fn clone_box(&self) -> Box<dyn Rule> { + todo!() + } + /// @return self::RULE_* fn get_reason(&self) -> i64 { - (self.bitfield & (255 << Self::BITFIELD_REASON)) >> Self::BITFIELD_REASON + (self.bitfield() & (255 << BITFIELD_REASON)) >> BITFIELD_REASON } /// @phpstan-return ReasonData fn get_reason_data(&self) -> &ReasonData { - &self.reason_data() + // TODO(phase-b): reason_data() returns Option; PHP getReasonData unconditional + self.reason_data().unwrap() } fn get_required_package(&self) -> Option<String> { @@ -97,29 +117,29 @@ pub trait Rule: std::fmt::Display { /// @param RuleSet::TYPE_* $type fn set_type(&mut self, r#type: i64) { - self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_TYPE)) - | ((255 & r#type) << Self::BITFIELD_TYPE); + *self.bitfield_mut() = + (self.bitfield() & !(255i64 << BITFIELD_TYPE)) | ((255 & r#type) << BITFIELD_TYPE); } fn get_type(&self) -> i64 { - (self.bitfield & (255 << Self::BITFIELD_TYPE)) >> Self::BITFIELD_TYPE + (self.bitfield() & (255 << BITFIELD_TYPE)) >> BITFIELD_TYPE } fn disable(&mut self) { - self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_DISABLED)) - | (1i64 << Self::BITFIELD_DISABLED); + *self.bitfield_mut() = + (self.bitfield() & !(255i64 << BITFIELD_DISABLED)) | (1i64 << BITFIELD_DISABLED); } fn enable(&mut self) { - self.bitfield &= !(255i64 << Self::BITFIELD_DISABLED); + *self.bitfield_mut() &= !(255i64 << BITFIELD_DISABLED); } fn is_disabled(&self) -> bool { - 0 != ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED) + 0 != ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED) } fn is_enabled(&self) -> bool { - 0 == ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED) + 0 == ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED) } fn is_caused_by_lock( @@ -195,10 +215,12 @@ pub trait Rule: std::fmt::Display { match self.get_reason() { r if r == Self::RULE_PACKAGE_CONFLICT => { - let mut package1 = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0])); - let mut package2 = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1])); + let mut package1 = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[0]).clone_box(), + ); + let mut package2 = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[1]).clone_box(), + ); let reason_data = self.get_reason_data(); // swap literals if they are not in the right order with package2 being the conflicter @@ -213,8 +235,9 @@ pub trait Rule: std::fmt::Display { r if r == Self::RULE_PACKAGE_REQUIRES => { let source_literal = literals[0]; - let source_package = - self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal)); + let source_package = self.deduplicate_default_branch_alias( + pool.literal_to_package(source_literal).clone_box(), + ); Ok(source_package) } @@ -264,11 +287,7 @@ pub trait Rule: std::fmt::Display { // 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() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_none() - }) + .filter(|p| p.as_any().downcast_ref::<AliasPackage>().is_none()) .map(|p| p.clone_box()) .collect(); if packages_non_alias.len() == 1 { @@ -320,10 +339,12 @@ pub trait Rule: std::fmt::Display { } r if r == Self::RULE_PACKAGE_CONFLICT => { - let mut package1 = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0])); - let mut package2 = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1])); + let mut package1 = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[0]).clone_box(), + ); + let mut package2 = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[1]).clone_box(), + ); let mut conflict_target = package1.get_pretty_string(); let reason_data = self.get_reason_data(); @@ -386,8 +407,9 @@ pub trait Rule: std::fmt::Display { r if r == Self::RULE_PACKAGE_REQUIRES => { assert!(literals.len() > 0); let source_literal = array_shift(&mut literals).unwrap(); - let source_package = - self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal)); + let source_package = self.deduplicate_default_branch_alias( + pool.literal_to_package(source_literal).clone_box(), + ); let reason_data = self.get_reason_data(); let link = match reason_data { ReasonData::Link(l) => l, @@ -522,7 +544,7 @@ pub trait Rule: std::fmt::Display { let mut groups: IndexMap<String, Vec<Box<dyn BasePackage>>> = IndexMap::new(); for literal in &literals { let package = pool.literal_to_package(*literal); - let group = if installed_map.contains_key(&package.id) { + let group = if installed_map.contains_key(&package.id()) { if *literal > 0 { "keep" } else { "remove" } } else { if *literal > 0 { @@ -565,8 +587,9 @@ pub trait Rule: std::fmt::Display { if alias_package.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS { return String::new(); } - let package = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1])); + let package = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[1]).clone_box(), + ); format!( "{} is an alias of {} and thus requires it to be installed too.", @@ -582,8 +605,9 @@ pub trait Rule: std::fmt::Display { if alias_package.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS { return String::new(); } - let package = - self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0])); + let package = self.deduplicate_default_branch_alias( + pool.literal_to_package(literals[0]).clone_box(), + ); format!( "{} is an alias of {} and must be installed with it.", @@ -656,7 +680,7 @@ pub trait Rule: std::fmt::Display { &self, package: Box<dyn BasePackage>, ) -> Box<dyn BasePackage> { - if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() { if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS { return alias_pkg.get_alias_of().clone_box(); } @@ -666,21 +690,29 @@ pub trait Rule: std::fmt::Display { } } +#[derive(Debug)] pub struct RuleBase { - bitfield: i64, - request: Option<Request>, - reason_data: Option<ReasonData>, + pub(crate) bitfield: i64, + pub(crate) request: Option<Request>, + pub(crate) reason_data: Option<ReasonData>, } impl RuleBase { - fn new(reason: i64, reason_data: ReasonData) -> Self { - let bitfield = (0i64 << Self::BITFIELD_DISABLED) - | (reason << Self::BITFIELD_REASON) - | (255i64 << Self::BITFIELD_TYPE); + pub const BITFIELD_DISABLED: i64 = BITFIELD_DISABLED; + pub const BITFIELD_REASON: i64 = BITFIELD_REASON; + pub const BITFIELD_TYPE: i64 = BITFIELD_TYPE; + + pub fn new(reason: i64, reason_data: ReasonData) -> Self { + let bitfield = + (0i64 << BITFIELD_DISABLED) | (reason << BITFIELD_REASON) | (255i64 << BITFIELD_TYPE); Self { bitfield, request: None, reason_data: Some(reason_data), } } + + pub fn is_disabled(&self) -> bool { + 0 != ((self.bitfield & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED) + } } diff --git a/crates/shirabe/src/dependency_resolver/rule2_literals.rs b/crates/shirabe/src/dependency_resolver/rule2_literals.rs index cdd1a9c..1c98200 100644 --- a/crates/shirabe/src/dependency_resolver/rule2_literals.rs +++ b/crates/shirabe/src/dependency_resolver/rule2_literals.rs @@ -28,7 +28,7 @@ impl Rule2Literals { }; Self { - inner: RuleBase::new(reason, reason_data), + inner: RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data)), literal1, literal2, literals: vec![literal1, literal2], diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs index 46ee4d2..032790e 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set.rs @@ -92,6 +92,10 @@ impl RuleSet { &*self.rule_by_id[&id] } + pub fn rule_by_id_mut(&mut self, id: i64) -> &mut dyn Rule { + &mut *self.rule_by_id.get_mut(&id).unwrap() + } + pub fn get_rules(&self) -> &IndexMap<i64, Vec<Box<dyn Rule>>> { &self.rules } diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs index d5bb1cd..6cbe93b 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs @@ -123,16 +123,15 @@ impl RuleSetGenerator { if literals.len() == 2 { // Rule2Literals and MultiConflictRule both implement Rule (Phase B: define Rule type) - Rule::from(Rule2Literals::new( + Box::new(Rule2Literals::new( literals[0], literals[1], PhpMixed::Int(reason), reason_data, - )) + )) as Box<dyn Rule> } else { - Rule::from( - MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap(), - ) + Box::new(MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap()) + as Box<dyn Rule> } } @@ -159,42 +158,45 @@ impl RuleSetGenerator { continue; } - self.added_map.insert(package.get_id(), package.clone_box()); + self.added_map + .insert(package.get_id(), package.clone_package_box()); - let is_alias = (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); + let is_alias = package.as_any().downcast_ref::<AliasPackage>().is_some(); if !is_alias { for name in package.get_names(false) { self.added_packages_by_names .entry(name) .or_default() - .push(package.clone_box()); + .push(package.clone_package_box()); } } else { - let alias_pkg = (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .unwrap(); + let alias_pkg = package.as_any().downcast_ref::<AliasPackage>().unwrap(); - work_queue.push_back(alias_pkg.get_alias_of().clone_box()); + work_queue.push_back(alias_pkg.get_alias_of().clone_package_box()); let alias_of = alias_pkg.get_alias_of(); let rule = self.create_require_rule( &*package, - &[alias_of.clone_box()], + &[alias_of.clone_package_box()], rule::RULE_PACKAGE_ALIAS, PhpMixed::Null, // reasonData: $package (BasePackage) ); - self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from)); + self.add_rule( + RuleSet::TYPE_PACKAGE, + rule.map(|r| Box::new(r) as Box<dyn Rule>), + ); // aliases must be installed with their main package, so create a rule the other way around as well let inverse_rule = self.create_require_rule( alias_of, - &[package.clone_box()], + &[package.clone_package_box()], rule::RULE_PACKAGE_INVERSE_ALIAS, PhpMixed::Null, // reasonData: $package->getAliasOf() (BasePackage) ); - self.add_rule(RuleSet::TYPE_PACKAGE, inverse_rule.map(Rule::from)); + self.add_rule( + RuleSet::TYPE_PACKAGE, + inverse_rule.map(|r| Box::new(r) as Box<dyn Rule>), + ); // if alias package has no self.version requires, its requirements do not // need to be added as the aliased package processing will take care of it @@ -207,7 +209,8 @@ impl RuleSetGenerator { let mut constraint = link.get_constraint().clone_box(); if platform_requirement_filter.is_ignored(link.get_target()) { continue; - } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) + } else if let Some(ignore_list_filter) = platform_requirement_filter + .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { constraint = ignore_list_filter @@ -223,7 +226,10 @@ impl RuleSetGenerator { rule::RULE_PACKAGE_REQUIRES, PhpMixed::Null, // reasonData: $link (Link) ); - self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from)); + self.add_rule( + RuleSet::TYPE_PACKAGE, + rule.map(|r| Box::new(r) as Box<dyn Rule>), + ); for require in possible_requires { work_queue.push_back(require); @@ -252,7 +258,8 @@ impl RuleSetGenerator { let mut constraint = link.get_constraint().clone_box(); if platform_requirement_filter.is_ignored(link.get_target()) { continue; - } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) + } else if let Some(ignore_list_filter) = platform_requirement_filter + .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { constraint = ignore_list_filter @@ -266,9 +273,8 @@ impl RuleSetGenerator { // define the conflict rule for regular packages, for alias packages it's only needed if the name // matches the conflict exactly, otherwise the name match is by provide/replace which means the // package which this is an alias of will conflict anyway, so no need to create additional rules - let conflict_is_alias = (conflict.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); + let conflict_is_alias = + conflict.as_any().downcast_ref::<AliasPackage>().is_some(); let conflict_name_matches = conflict.get_name() == link.get_target(); if !conflict_is_alias || conflict_name_matches { let rule = self.create_rule2_literals( @@ -277,7 +283,10 @@ impl RuleSetGenerator { rule::RULE_PACKAGE_CONFLICT, PhpMixed::Null, // reasonData: $link (Link) ); - self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from)); + self.add_rule( + RuleSet::TYPE_PACKAGE, + rule.map(|r| Box::new(r) as Box<dyn Rule>), + ); } } } @@ -336,14 +345,15 @@ impl RuleSetGenerator { rule::RULE_FIXED, PhpMixed::Array(reason_data), ); - self.add_rule(RuleSet::TYPE_REQUEST, Some(Rule::from(rule))); + self.add_rule(RuleSet::TYPE_REQUEST, Some(Box::new(rule) as Box<dyn Rule>)); } for (package_name, constraint) in request.get_requires() { let mut constraint = constraint.clone_box(); if platform_requirement_filter.is_ignored(package_name) { continue; - } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any) + } else if let Some(ignore_list_filter) = platform_requirement_filter + .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { constraint = ignore_list_filter @@ -371,7 +381,7 @@ impl RuleSetGenerator { rule::RULE_ROOT_REQUIRE, PhpMixed::Array(reason_data), ); - self.add_rule(RuleSet::TYPE_REQUEST, Some(Rule::from(rule))); + self.add_rule(RuleSet::TYPE_REQUEST, Some(Box::new(rule) as Box<dyn Rule>)); } } @@ -387,7 +397,7 @@ impl RuleSetGenerator { // even if the alias itself isn't required, otherwise a package could be installed without its alias which // leads to unexpected behavior let is_not_added = !self.added_map.contains_key(&package.get_id()); - let as_alias = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>(); + let as_alias = package.as_any().downcast_ref::<AliasPackage>(); if is_not_added { if let Some(alias_pkg) = as_alias { if alias_pkg.is_root_package_alias() diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs index 0524fcb..a8176ee 100644 --- a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs +++ b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs @@ -27,7 +27,10 @@ impl RuleWatchGraph { return; } - let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any) + let is_multi_conflict = node + .borrow() + .get_rule() + .as_any() .downcast_ref::<MultiConflictRule>() .is_some(); @@ -72,7 +75,10 @@ impl RuleWatchGraph { self.watch_chains.get_mut(&literal).unwrap().rewind(); while self.watch_chains.get(&literal).unwrap().valid() { let node = self.watch_chains.get(&literal).unwrap().current().clone(); - let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any) + let is_multi_conflict = node + .borrow() + .get_rule() + .as_any() .downcast_ref::<MultiConflictRule>() .is_some(); if !is_multi_conflict { diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs index 12dd83f..6c1bada 100644 --- a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs +++ b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs @@ -57,6 +57,12 @@ impl RuleWatchNode { self.rule.as_ref() } + /// Owned clone for callers that need a `Box<dyn Rule>`. Default impl in + /// `RuleLiterals` returns `todo!()`; concrete rule impls override it. + pub fn get_rule_boxed(&self) -> Box<dyn crate::dependency_resolver::rule::Rule> { + self.rule.clone_rule_box() + } + pub fn get_other_watch(&self, literal: i64) -> i64 { if self.watch1 == literal { return self.watch2; diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs index 2160b97..b4b878b 100644 --- a/crates/shirabe/src/dependency_resolver/solver.rs +++ b/crates/shirabe/src/dependency_resolver/solver.rs @@ -89,7 +89,7 @@ impl Solver { let rules_count = self.rules.count(); let mut rule_index = 0_i64; while rule_index < rules_count { - let rule = self.rules.rule_by_id(rule_index).clone(); + let rule = self.rules.rule_by_id(rule_index).clone_box(); if !rule.is_assertion() || rule.is_disabled() { rule_index += 1; @@ -100,7 +100,7 @@ impl Solver { let literal = literals[0]; if !self.decisions.decided(literal) { - self.decisions.decide(literal, 1, rule.clone()); + self.decisions.decide(literal, 1, rule.clone_box()); rule_index += 1; continue; } @@ -118,12 +118,12 @@ impl Solver { continue; } - let conflict = self.decisions.decision_rule(literal).clone(); + let conflict = self.decisions.decision_rule(literal).clone_box(); if RuleSet::TYPE_PACKAGE == conflict.get_type() { let mut problem = Problem::new(); - problem.add_rule(rule.clone()); + problem.add_rule(rule.clone_box()); problem.add_rule(conflict); self.rules.rule_by_id_mut(rule_index).disable()?; self.problems.push(problem); @@ -144,7 +144,7 @@ impl Solver { .ids() .collect(); for assert_rule_id in request_rules { - let assert_rule = self.rules.rule_by_id(assert_rule_id).clone(); + let assert_rule = self.rules.rule_by_id(assert_rule_id).clone_box(); if assert_rule.is_disabled() || !assert_rule.is_assertion() { continue; } @@ -204,7 +204,7 @@ impl Solver { // TODO(phase-b): store the constraint inside reason_data; PhpMixed needs to // accept a `dyn ConstraintInterface` wrapper. reason_data.insert("constraint".to_string(), PhpMixed::Null); - problem.add_rule(Rule::generic(GenericRule::new( + problem.add_rule(Box::new(GenericRule::new( Vec::new(), PhpMixed::Int(rule::RULE_ROOT_REQUIRE), PhpMixed::Array( @@ -213,7 +213,7 @@ impl Solver { .map(|(k, v)| (k, Box::new(v))) .collect(), ), - ))); + )) as Box<dyn Rule>); self.problems.push(problem); } } @@ -229,11 +229,8 @@ impl Solver { self.setup_fixed_map(request); - self.io.write_error( - PhpMixed::String("Generating rules".to_string()), - true, - crate::io::io_interface::DEBUG, - ); + self.io + .write_error3("Generating rules", true, crate::io::io_interface::DEBUG); let mut rule_set_generator = RuleSetGenerator::new(self.policy.clone_box(), self.pool.clone()); self.rules = @@ -253,23 +250,20 @@ impl Solver { // make decisions based on root require/fix assertions self.make_assertion_rule_decisions()?; - self.io.write_error( - PhpMixed::String("Resolving dependencies through SAT".to_string()), + self.io.write_error3( + "Resolving dependencies through SAT", true, crate::io::io_interface::DEBUG, ); let before = microtime(true); self.run_sat()?; - self.io.write_error( - PhpMixed::String("".to_string()), - true, - crate::io::io_interface::DEBUG, - ); - self.io.write_error( - PhpMixed::String(sprintf( + self.io + .write_error3("", true, crate::io::io_interface::DEBUG); + self.io.write_error3( + &sprintf( "Dependency resolution completed in %.3f seconds", &[PhpMixed::Float(microtime(true) - before)], - )), + ), true, crate::io::io_interface::VERBOSE, ); @@ -767,8 +761,8 @@ impl Solver { let mut rules_count = self.rules.count(); let mut pass = 1_i64; - self.io.write_error( - PhpMixed::String("Looking at all rules.".to_string()), + self.io.write_error3( + "Looking at all rules.", true, crate::io::io_interface::DEBUG, ); @@ -777,20 +771,20 @@ impl Solver { while n < rules_count { if i == rules_count { if 1 == pass { - self.io.write_error( - PhpMixed::String(format!( + self.io.write_error3( + &format!( "Something's changed, looking at all rules again (pass #{})", pass - )), + ), false, crate::io::io_interface::DEBUG, ); } else { - self.io.overwrite_error( - PhpMixed::String(format!( + self.io.overwrite_error4( + &format!( "Something's changed, looking at all rules again (pass #{})", pass - )), + ), false, None, crate::io::io_interface::DEBUG, @@ -801,7 +795,7 @@ impl Solver { pass += 1; } - let rule = self.rules.rule_by_id(i).clone(); + let rule = self.rules.rule_by_id(i).clone_box(); let literals = rule.get_literals().clone(); if rule.is_disabled() { diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs index 4ab5c6b..8c4595f 100644 --- a/crates/shirabe/src/dependency_resolver/transaction.rs +++ b/crates/shirabe/src/dependency_resolver/transaction.rs @@ -37,6 +37,17 @@ pub struct Transaction { pub(crate) result_packages_by_name: IndexMap<String, Vec<Box<dyn PackageInterface>>>, } +impl Default for Transaction { + fn default() -> Self { + Self { + operations: vec![], + present_packages: vec![], + result_package_map: IndexMap::new(), + result_packages_by_name: IndexMap::new(), + } + } +} + impl Transaction { /// @param PackageInterface[] $presentPackages /// @param PackageInterface[] $resultPackages @@ -67,12 +78,8 @@ impl Transaction { let _package_sort = |a: &dyn PackageInterface, b: &dyn PackageInterface| -> i64 { // sort alias packages by the same name behind their non alias version if a.get_name() == b.get_name() { - let a_is_alias = (a.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); - let b_is_alias = (b.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some(); + let a_is_alias = a.as_any().downcast_ref::<AliasPackage>().is_some(); + let b_is_alias = b.as_any().downcast_ref::<AliasPackage>().is_some(); if a_is_alias != b_is_alias { return if a_is_alias { -1 } else { 1 }; } @@ -119,10 +126,7 @@ impl Transaction { let mut present_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new(); let mut remove_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new(); for package in &self.present_packages { - if (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some() - { + if package.as_any().downcast_ref::<AliasPackage>().is_some() { let key = format!("{}::{}", package.get_name(), package.get_version()); present_alias_map.insert(key.clone(), package.clone_package_box()); remove_alias_map.insert(key, package.clone_package_box()); @@ -151,7 +155,7 @@ impl Transaction { visited.insert(spl_object_hash(package.as_ref()), true); stack.push(package.clone_package_box()); - if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() { + if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() { stack.push(alias.get_alias_of().clone_package_box()); } else { for link in package.get_requires().values() { @@ -165,10 +169,7 @@ impl Transaction { } else if !processed.contains_key(&spl_object_hash(package.as_ref())) { processed.insert(spl_object_hash(package.as_ref()), true); - if (package.as_any() as &dyn Any) - .downcast_ref::<AliasPackage>() - .is_some() - { + if package.as_any().downcast_ref::<AliasPackage>().is_some() { let alias_key = format!("{}::{}", package.get_name(), package.get_version()); if present_alias_map.contains_key(&alias_key) { remove_alias_map.shift_remove(&alias_key); @@ -308,12 +309,10 @@ impl Transaction { let op = &operations[idx]; let package: Box<dyn PackageInterface> = if let Some(install_op) = - (op.as_ref() as &dyn Any).downcast_ref::<InstallOperation>() + op.as_ref().as_any().downcast_ref::<InstallOperation>() { install_op.get_package().clone_package_box() - } else if let Some(update_op) = - (op.as_ref() as &dyn Any).downcast_ref::<UpdateOperation>() - { + } else if let Some(update_op) = op.as_ref().as_any().downcast_ref::<UpdateOperation>() { update_op.get_target_package().clone_package_box() } else { continue; @@ -415,10 +414,14 @@ impl Transaction { let mut uninst_ops: Vec<Box<dyn OperationInterface>> = vec![]; let mut to_remove: Vec<usize> = vec![]; for (idx, op) in operations.iter().enumerate() { - let is_uninstall = (op.as_ref() as &dyn Any) + let is_uninstall = op + .as_ref() + .as_any() .downcast_ref::<UninstallOperation>() .is_some() - || (op.as_ref() as &dyn Any) + || op + .as_ref() + .as_any() .downcast_ref::<MarkAliasUninstalledOperation>() .is_some(); if is_uninstall { |
