aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-25 00:58:20 +0900
committernsfisis <nsfisis@gmail.com>2026-05-25 00:58:36 +0900
commit1921f173ea219cb4b25847294d2d3fa465550fbb (patch)
tree0d30486a2cb9a0c106e5d5827be3f655c60cd871 /crates/shirabe/src
parentdbdecaf5a1c54a876b7ee0153d58dd39b1080f97 (diff)
downloadphp-shirabe-1921f173ea219cb4b25847294d2d3fa465550fbb.tar.gz
php-shirabe-1921f173ea219cb4b25847294d2d3fa465550fbb.tar.zst
php-shirabe-1921f173ea219cb4b25847294d2d3fa465550fbb.zip
refactor(package): introduce Rc<RefCell<_>> handles for packages
PHP packages have reference semantics, so introduce shared-ownership handles over an AnyPackage enum (PackageInterfaceHandle and friends) and replace Box<dyn PackageInterface> throughout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/advisory/auditor.rs49
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs78
-rw-r--r--crates/shirabe/src/command/archive_command.rs58
-rw-r--r--crates/shirabe/src/command/audit_command.rs3
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs22
-rw-r--r--crates/shirabe/src/command/bump_command.rs11
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs12
-rw-r--r--crates/shirabe/src/command/create_project_command.rs29
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs4
-rw-r--r--crates/shirabe/src/command/fund_command.rs29
-rw-r--r--crates/shirabe/src/command/home_command.rs8
-rw-r--r--crates/shirabe/src/command/licenses_command.rs29
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs26
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs38
-rw-r--r--crates/shirabe/src/command/require_command.rs54
-rw-r--r--crates/shirabe/src/command/show_command.rs197
-rw-r--r--crates/shirabe/src/command/status_command.rs38
-rw-r--r--crates/shirabe/src/command/suggests_command.rs29
-rw-r--r--crates/shirabe/src/command/update_command.rs28
-rw-r--r--crates/shirabe/src/command/validate_command.rs4
-rw-r--r--crates/shirabe/src/composer.rs18
-rw-r--r--crates/shirabe/src/console/application.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/default_policy.rs48
-rw-r--r--crates/shirabe/src/dependency_resolver/local_repo_transaction.rs6
-rw-r--r--crates/shirabe/src/dependency_resolver/lock_transaction.rs78
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/install_operation.rs11
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs10
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs10
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs11
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/update_operation.rs19
-rw-r--r--crates/shirabe/src/dependency_resolver/policy_interface.rs10
-rw-r--r--crates/shirabe/src/dependency_resolver/pool.rs56
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_builder.rs248
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_optimizer.rs102
-rw-r--r--crates/shirabe/src/dependency_resolver/problem.rs114
-rw-r--r--crates/shirabe/src/dependency_resolver/request.rs68
-rw-r--r--crates/shirabe/src/dependency_resolver/rule.rs105
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set_generator.rs117
-rw-r--r--crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs9
-rw-r--r--crates/shirabe/src/dependency_resolver/solver.rs25
-rw-r--r--crates/shirabe/src/dependency_resolver/transaction.rs125
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs17
-rw-r--r--crates/shirabe/src/factory.rs10
-rw-r--r--crates/shirabe/src/installer.rs155
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs98
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs9
-rw-r--r--crates/shirabe/src/installer/library_installer.rs84
-rw-r--r--crates/shirabe/src/installer/metapackage_installer.rs40
-rw-r--r--crates/shirabe/src/installer/noop_installer.rs25
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs13
-rw-r--r--crates/shirabe/src/installer/project_installer.rs14
-rw-r--r--crates/shirabe/src/package/alias_package.rs57
-rw-r--r--crates/shirabe/src/package/base_package.rs2
-rw-r--r--crates/shirabe/src/package/complete_alias_package.rs114
-rw-r--r--crates/shirabe/src/package/handle.rs1440
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs23
-rw-r--r--crates/shirabe/src/package/loader/json_loader.rs4
-rw-r--r--crates/shirabe/src/package/loader/loader_interface.rs4
-rw-r--r--crates/shirabe/src/package/loader/root_package_loader.rs11
-rw-r--r--crates/shirabe/src/package/loader/validating_array_loader.rs2
-rw-r--r--crates/shirabe/src/package/locker.rs43
-rw-r--r--crates/shirabe/src/package/mod.rs2
-rw-r--r--crates/shirabe/src/package/package.rs4
-rw-r--r--crates/shirabe/src/package/package_interface.rs8
-rw-r--r--crates/shirabe/src/package/root_alias_package.rs285
-rw-r--r--crates/shirabe/src/package/root_package.rs4
-rw-r--r--crates/shirabe/src/package/root_package_interface.rs8
-rw-r--r--crates/shirabe/src/package/version/version_selector.rs34
-rw-r--r--crates/shirabe/src/plugin/plugin_manager.rs81
-rw-r--r--crates/shirabe/src/plugin/pre_pool_create_event.rs18
-rw-r--r--crates/shirabe/src/repository/array_repository.rs157
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs2
-rw-r--r--crates/shirabe/src/repository/canonical_packages_trait.rs20
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs139
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs27
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs66
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs14
-rw-r--r--crates/shirabe/src/repository/installed_array_repository.rs17
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs19
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs66
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs13
-rw-r--r--crates/shirabe/src/repository/package_repository.rs3
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs99
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs13
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs12
-rw-r--r--crates/shirabe/src/repository/repository_set.rs72
-rw-r--r--crates/shirabe/src/repository/repository_utils.rs16
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs21
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs21
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs14
-rw-r--r--crates/shirabe/src/repository/writable_repository_interface.rs5
-rw-r--r--crates/shirabe/src/util/package_sorter.rs23
92 files changed, 3312 insertions, 2080 deletions
diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs
index 5418fac..8ac6d0c 100644
--- a/crates/shirabe/src/advisory/auditor.rs
+++ b/crates/shirabe/src/advisory/auditor.rs
@@ -15,8 +15,9 @@ use crate::advisory::SecurityAdvisory;
use crate::io::ConsoleIO;
use crate::io::IOInterface;
use crate::json::JsonFile;
-use crate::package::CompletePackageInterface;
+use crate::package::CompletePackageInterfaceHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::base_package;
use crate::package::base_package::BasePackage;
use crate::repository::PartialOrSecurityAdvisory;
@@ -73,7 +74,7 @@ impl Auditor {
&self,
io: &mut dyn IOInterface,
repo_set: &RepositorySet,
- packages: Vec<Box<dyn PackageInterface>>,
+ packages: Vec<PackageInterfaceHandle>,
format: &str,
warning_only: bool,
ignore_list: IndexMap<String, Option<String>>,
@@ -109,12 +110,12 @@ impl Auditor {
let mut abandoned_count: i64 = 0;
let affected_packages_count = advisories.len() as i64;
- let abandoned_packages: Vec<Box<dyn CompletePackageInterface>>;
+ let abandoned_packages: Vec<CompletePackageInterfaceHandle>;
if abandoned == Self::ABANDONED_IGNORE {
abandoned_packages = vec![];
} else {
// TODO(phase-b): $packages reused here; see note above
- abandoned_packages = self.filter_abandoned_packages(&[], &ignore_abandoned);
+ abandoned_packages = self.filter_abandoned_packages(&[], &ignore_abandoned)?;
if abandoned == Self::ABANDONED_FAIL {
abandoned_count = abandoned_packages.len() as i64;
}
@@ -144,11 +145,8 @@ impl Auditor {
let abandoned_map = array_reduce(
&abandoned_packages,
|mut carry: IndexMap<String, Option<String>>,
- package: &Box<dyn CompletePackageInterface>| {
- carry.insert(
- package.get_pretty_name().to_string(),
- package.get_replacement_package().map(|s| s.to_string()),
- );
+ package: &CompletePackageInterfaceHandle| {
+ carry.insert(package.get_pretty_name(), package.get_replacement_package());
carry
},
IndexMap::new(),
@@ -284,9 +282,9 @@ impl Auditor {
/// @return array<CompletePackageInterface>
pub fn filter_abandoned_packages(
&self,
- packages: &[Box<dyn PackageInterface>],
+ packages: &[PackageInterfaceHandle],
ignore_abandoned: &IndexMap<String, Option<String>>,
- ) -> Vec<Box<dyn CompletePackageInterface>> {
+ ) -> anyhow::Result<Vec<CompletePackageInterfaceHandle>> {
let mut filter: Option<String> = None;
if ignore_abandoned.len() != 0 {
filter = Some(base_package::package_names_to_regexp(
@@ -295,16 +293,19 @@ impl Auditor {
));
}
- // PHP: array_filter($packages, fn($pkg) => $pkg instanceof CompletePackageInterface && $pkg->isAbandoned() && ($filter === null || !Preg::isMatch($filter, $pkg->getName())))
- // TODO(phase-b): downcast Box<dyn PackageInterface> -> Box<dyn CompletePackageInterface>
- let _ = packages;
- let _ = filter;
- let _ = |pkg: &Box<dyn PackageInterface>| -> bool {
- // pkg instanceof CompletePackageInterface && pkg.is_abandoned() && (filter.is_none() || !Preg::is_match(filter.as_ref().unwrap(), pkg.get_name()))
- let _ = Preg::is_match("", "");
- false
- };
- vec![]
+ // PHP: array_filter($packages, fn(PackageInterface $pkg) => $pkg instanceof CompletePackageInterface && $pkg->isAbandoned() && ($filter === null || !Preg::isMatch($filter, $pkg->getName())))
+ let mut result: Vec<CompletePackageInterfaceHandle> = vec![];
+ for pkg in packages {
+ let Some(pkg) = pkg.as_complete() else {
+ continue;
+ };
+ if pkg.is_abandoned()
+ && (filter.is_none() || !Preg::is_match(filter.as_ref().unwrap(), &pkg.get_name())?)
+ {
+ result.push(pkg);
+ }
+ }
+ Ok(result)
}
/// @phpstan-param array<string, array<PartialOrSecurityAdvisory|SecurityAdvisory>> $allAdvisories
@@ -577,7 +578,7 @@ impl Auditor {
fn output_abandoned_packages(
&self,
io: &mut dyn IOInterface,
- packages: &[Box<dyn CompletePackageInterface>],
+ packages: &[CompletePackageInterfaceHandle],
format: &str,
) -> Result<()> {
io.write_error(&sprintf(
@@ -673,10 +674,10 @@ impl Auditor {
// upcast to PackageInterface (e.g. via an as_package_interface() trait method)
fn get_package_name_with_link_for_complete(
&self,
- package: &Box<dyn CompletePackageInterface>,
+ package: &CompletePackageInterfaceHandle,
) -> String {
let _ = package;
- // PackageInfo::get_view_source_or_homepage_url(package as &dyn PackageInterface)
+ // PackageInfo::get_view_source_or_homepage_url(package.as_rc().borrow().as_package_interface())
String::new()
}
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index 5c9f008..3786011 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -27,9 +27,9 @@ use crate::installer::InstallationManager;
use crate::io::IOInterface;
use crate::io::NullIO;
use crate::json::JsonFile;
-use crate::package::AliasPackage;
use crate::package::Locker;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterface;
use crate::repository::InstalledRepositoryInterface;
use crate::script::ScriptEvents;
@@ -267,7 +267,7 @@ impl AutoloadGenerator {
let autoloads = self.parse_autoloads(
package_map
.iter()
- .map(|(p, s)| (p.clone_package_box(), s.clone()))
+ .map(|(p, s)| (p.clone(), s.clone()))
.collect(),
root_package,
filtered_dev_packages,
@@ -741,20 +741,24 @@ impl AutoloadGenerator {
&self,
installation_manager: &mut InstallationManager,
root_package: &dyn RootPackageInterface,
- packages: Vec<Box<dyn PackageInterface>>,
- ) -> anyhow::Result<Vec<(Box<dyn PackageInterface>, Option<String>)>> {
+ packages: Vec<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Vec<(PackageInterfaceHandle, Option<String>)>> {
// build package => install path map
- let mut package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> = vec![(
- root_package.clone_as_package_interface(),
- Some(String::new()),
- )];
+ // TODO(phase-c): the root package needs to be available here as a shared handle;
+ // a borrowed &dyn RootPackageInterface cannot be lifted into a PackageInterfaceHandle.
+ let _ = root_package;
+ let root_package_handle: PackageInterfaceHandle =
+ todo!("root package handle for build_package_map");
+ let mut package_map: Vec<(PackageInterfaceHandle, Option<String>)> =
+ vec![(root_package_handle, Some(String::new()))];
for package in packages {
- if package.as_alias_package().is_some() {
+ if package.as_alias().is_some() {
continue;
}
- self.validate_package(&*package)?;
- let install_path = installation_manager.get_install_path(&*package);
+ self.validate_package(package.as_rc().borrow().as_package_interface())?;
+ let install_path = installation_manager
+ .get_install_path(package.as_rc().borrow().as_package_interface());
package_map.push((package, install_path));
}
@@ -795,7 +799,7 @@ impl AutoloadGenerator {
/// Compiles an ordered list of namespace => path mappings
pub fn parse_autoloads(
&self,
- package_map: Vec<(Box<dyn PackageInterface>, Option<String>)>,
+ package_map: Vec<(PackageInterfaceHandle, Option<String>)>,
root_package: &dyn RootPackageInterface,
filtered_dev_packages: PhpMixed,
) -> IndexMap<String, PhpMixed> {
@@ -812,7 +816,7 @@ impl AutoloadGenerator {
.unwrap_or_default();
package_map
.into_iter()
- .filter(|item| !dev_list.contains(&item.0.get_name().to_string()))
+ .filter(|item| !dev_list.contains(&item.0.get_name()))
.collect()
} else if filtered_dev_packages.as_bool() == Some(true) {
self.filter_package_map(package_map, root_package)
@@ -932,7 +936,7 @@ impl AutoloadGenerator {
pub(crate) fn get_include_paths_file(
&self,
- package_map: &Vec<(Box<dyn PackageInterface>, Option<String>)>,
+ package_map: &Vec<(PackageInterfaceHandle, Option<String>)>,
filesystem: &Filesystem,
base_path: &str,
vendor_path: &str,
@@ -1083,7 +1087,7 @@ impl AutoloadGenerator {
pub(crate) fn get_platform_check(
&self,
- package_map: &Vec<(Box<dyn PackageInterface>, Option<String>)>,
+ package_map: &Vec<(PackageInterfaceHandle, Option<String>)>,
check_platform: PhpMixed,
dev_package_names: &Vec<String>,
) -> Option<String> {
@@ -1625,7 +1629,7 @@ impl AutoloadGenerator {
pub(crate) fn parse_autoloads_type(
&self,
- package_map: &Vec<(Box<dyn PackageInterface>, Option<String>)>,
+ package_map: &Vec<(PackageInterfaceHandle, Option<String>)>,
r#type: &str,
root_package: &dyn RootPackageInterface,
) -> IndexMap<String, Box<PhpMixed>> {
@@ -1794,7 +1798,10 @@ impl AutoloadGenerator {
if r#type == "files" {
autoloads.insert(
- self.get_file_identifier(&**package, &path_str),
+ self.get_file_identifier(
+ package.as_rc().borrow().as_package_interface(),
+ &path_str,
+ ),
Box::new(PhpMixed::String(relative_path)),
);
continue;
@@ -1830,17 +1837,17 @@ impl AutoloadGenerator {
/// Filters out dev-dependencies
pub(crate) fn filter_package_map(
&self,
- package_map: Vec<(Box<dyn PackageInterface>, Option<String>)>,
+ package_map: Vec<(PackageInterfaceHandle, Option<String>)>,
root_package: &dyn RootPackageInterface,
- ) -> Vec<(Box<dyn PackageInterface>, Option<String>)> {
- let mut packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ ) -> Vec<(PackageInterfaceHandle, Option<String>)> {
+ let mut packages: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
let mut include: IndexMap<String, bool> = IndexMap::new();
let mut replaced_by: IndexMap<String, String> = IndexMap::new();
for item in &package_map {
let package = &item.0;
- let name = package.get_name().to_string();
- packages.insert(name.clone(), package.clone_package_box());
+ let name = package.get_name();
+ packages.insert(name.clone(), package.clone());
for (_k, replace) in &package.get_replaces() {
replaced_by.insert(replace.get_target().to_string(), name.clone());
}
@@ -1849,7 +1856,7 @@ impl AutoloadGenerator {
// Recursive walk emulating PHP's by-reference closure capture.
fn add(
package: &dyn PackageInterface,
- packages: &IndexMap<String, Box<dyn PackageInterface>>,
+ packages: &IndexMap<String, PackageInterfaceHandle>,
include: &mut IndexMap<String, bool>,
replaced_by: &IndexMap<String, String>,
) {
@@ -1861,7 +1868,12 @@ impl AutoloadGenerator {
if !include.contains_key(&target) {
include.insert(target.clone(), true);
if let Some(p) = packages.get(&target) {
- add(&**p, packages, include, replaced_by);
+ add(
+ p.as_rc().borrow().as_package_interface(),
+ packages,
+ include,
+ replaced_by,
+ );
}
}
}
@@ -1892,29 +1904,29 @@ impl AutoloadGenerator {
/// Packages of equal weight are sorted alphabetically
pub(crate) fn sort_package_map(
&self,
- package_map: Vec<(Box<dyn PackageInterface>, Option<String>)>,
- ) -> Vec<(Box<dyn PackageInterface>, Option<String>)> {
- let mut packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ package_map: Vec<(PackageInterfaceHandle, Option<String>)>,
+ ) -> Vec<(PackageInterfaceHandle, Option<String>)> {
+ let mut packages: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
let mut paths: IndexMap<String, Option<String>> = IndexMap::new();
for item in &package_map {
let (package, path) = item;
- let name = package.get_name().to_string();
- packages.insert(name.clone(), package.clone_package_box());
+ let name = package.get_name();
+ packages.insert(name.clone(), package.clone());
paths.insert(name, path.clone());
}
let sorted_packages = PackageSorter::sort_packages(
- packages.values().map(|p| p.clone_package_box()).collect(),
+ packages.values().map(|p| p.clone()).collect(),
IndexMap::new(),
);
- let mut sorted_package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> = vec![];
+ let mut sorted_package_map: Vec<(PackageInterfaceHandle, Option<String>)> = vec![];
for package in sorted_packages {
- let name = package.get_name().to_string();
+ let name = package.get_name();
sorted_package_map.push((
- packages.get(&name).unwrap().clone_package_box(),
+ packages.get(&name).unwrap().clone(),
paths.get(&name).cloned().flatten(),
));
}
diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs
index 0813df6..1b21242 100644
--- a/crates/shirabe/src/command/archive_command.rs
+++ b/crates/shirabe/src/command/archive_command.rs
@@ -1,7 +1,5 @@
//! ref: composer/src/Composer/Command/ArchiveCommand.php
-use std::any::Any;
-
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
@@ -16,8 +14,6 @@ use crate::console::input::InputArgument;
use crate::console::input::InputOption;
use crate::factory::Factory;
use crate::io::IOInterface;
-use crate::package::BasePackage;
-use crate::package::CompletePackageInterface;
use crate::package::archiver::ArchiveManager;
use crate::package::version::VersionParser;
use crate::package::version::VersionSelector;
@@ -192,17 +188,18 @@ impl ArchiveCommand {
&owned_archive_manager
};
- let package = if let Some(name) = package_name {
- match self.select_package(io, &name, version.as_deref())? {
- Some(p) => p,
- None => return Ok(1),
- }
- } else {
- let _rc = self.require_composer(None, None)?;
- crate::command::composer_full(&_rc)
- .get_package()
- .clone_box()
- };
+ let package: crate::package::CompletePackageInterfaceHandle =
+ if let Some(name) = package_name {
+ match self.select_package(io, &name, version.as_deref())? {
+ Some(p) => p,
+ None => return Ok(1),
+ }
+ } else {
+ let _rc = self.require_composer(None, None)?;
+ // TODO(phase-c): composer.get_package() returns &dyn RootPackageInterface, not a
+ // handle, so it cannot be shared as a CompletePackageInterfaceHandle yet.
+ todo!("share composer.get_package() as a CompletePackageInterfaceHandle")
+ };
io.write_error(&format!(
"<info>Creating the archive into \"{}\".</info>",
@@ -211,13 +208,7 @@ impl ArchiveCommand {
// TODO(phase-b): ArchiveManager.archive needs &mut self and &mut CompletePackageInterface;
// current composer.get_archive_manager() returns &ArchiveManager. Needs RefCell wrapper.
let _ = archive_manager;
- let _ = (
- package.as_ref(),
- format,
- dest,
- file_name.as_deref(),
- ignore_filters,
- );
+ let _ = (&package, format, dest, file_name.as_deref(), ignore_filters);
let package_path: String = todo!("ArchiveManager.archive call");
let fs = Filesystem::new(None);
let short_path =
@@ -239,7 +230,7 @@ impl ArchiveCommand {
io: &mut dyn IOInterface,
package_name: &str,
version: Option<&str>,
- ) -> Result<Option<Box<dyn CompletePackageInterface>>> {
+ ) -> Result<Option<crate::package::CompletePackageInterfaceHandle>> {
io.write_error("<info>Searching for the specified package.</info>");
let mut version = version.map(|v| v.to_string());
@@ -323,7 +314,7 @@ impl ArchiveCommand {
None,
shirabe_php_shim::PhpMixed::Bool(true),
)?;
- let p = best.unwrap_or_else(|| packages.into_iter().next().unwrap());
+ let p = best.unwrap_or_else(|| packages.into_iter().next().unwrap().into());
io.write_error(&format!(
"<info>Found multiple matches, selected {}.</info>",
@@ -333,7 +324,8 @@ impl ArchiveCommand {
io.write_error("<comment>Please use a more specific constraint to pick a different package.</comment>");
p
} else if packages.len() == 1 {
- let p = packages.into_iter().next().unwrap();
+ let p: crate::package::PackageInterfaceHandle =
+ packages.into_iter().next().unwrap().into();
io.write_error(&format!(
"<info>Found an exact match {}.</info>",
p.get_pretty_string()
@@ -347,10 +339,18 @@ impl ArchiveCommand {
return Ok(None);
};
- // TODO(phase-b): instanceof CompletePackageInterface / BasePackage runtime
- // checks require downcast support that BasePackage trait does not yet expose.
- let _ = &package;
- todo!("convert Box<dyn BasePackage> into Box<dyn CompletePackageInterface>")
+ let Some(complete) = package.as_complete() else {
+ return Err(LogicException {
+ message: format!(
+ "Expected a CompletePackageInterface instance but found {}",
+ get_debug_type(&shirabe_php_shim::PhpMixed::Null)
+ ),
+ code: 0,
+ }
+ .into());
+ };
+
+ Ok(Some(complete))
}
}
diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs
index f4f27e9..d3dadbd 100644
--- a/crates/shirabe/src/command/audit_command.rs
+++ b/crates/shirabe/src/command/audit_command.rs
@@ -6,7 +6,6 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::PartialComposerHandle;
use crate::console::input::InputOption;
use crate::io::IOInterface;
-use crate::package::PackageInterface;
use crate::repository::CanonicalPackagesTrait;
use crate::repository::InstalledRepository;
use crate::repository::RepositoryInterface;
@@ -146,7 +145,7 @@ impl AuditCommand {
&self,
composer: &PartialComposerHandle,
input: &dyn InputInterface,
- ) -> Result<Vec<Box<dyn PackageInterface>>> {
+ ) -> Result<Vec<crate::package::PackageInterfaceHandle>> {
let mut composer = crate::command::composer_full_mut(composer);
if input.get_option("locked").as_bool().unwrap_or(false) {
let locker = composer.get_locker().clone();
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index f1ff863..8ea10b5 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -53,7 +53,7 @@ pub trait BaseDependencyCommand: BaseCommand {
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone_box(),
+ composer.get_package().clone(),
)));
if input.get_option("locked").as_bool().unwrap_or(false) {
@@ -158,7 +158,7 @@ pub trait BaseDependencyCommand: BaseCommand {
FindPackageConstraint::String(text_constraint.clone()),
) {
installed_repo.add_repository(Box::new(
- InstalledArrayRepository::new_with_packages(vec![r#match.clone_box()])?,
+ InstalledArrayRepository::new_with_packages(vec![r#match.into()])?,
))?;
} else if PlatformRepository::is_platform_package(&needle) {
let parser = VersionParser::new();
@@ -170,9 +170,9 @@ pub trait BaseDependencyCommand: BaseCommand {
.to_string();
let temp_platform_pkg = Package::new(needle.clone(), version.clone(), version);
installed_repo.add_repository(Box::new(
- InstalledArrayRepository::new_with_packages(vec![Box::new(
- temp_platform_pkg,
- )])?,
+ InstalledArrayRepository::new_with_packages(vec![
+ crate::package::PackageHandle::from_package(temp_platform_pkg).into(),
+ ])?,
))?;
}
} else {
@@ -270,9 +270,9 @@ pub trait BaseDependencyCommand: BaseCommand {
self.init_styles(output);
let root = &packages[0];
let description = root
- .as_complete_package_interface()
+ .as_complete()
.and_then(|c| c.get_description())
- .unwrap_or("");
+ .unwrap_or_default();
self.get_io().write(&format!(
"<info>{}</info> {} {}",
root.get_pretty_name(),
@@ -335,7 +335,9 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
package.get_pretty_version().to_string()
};
- let package_url = PackageInfo::get_view_source_or_homepage_url(&*package);
+ let package_url = PackageInfo::get_view_source_or_homepage_url(
+ package.as_rc().borrow().as_package_interface(),
+ );
let name_with_link = match &package_url {
Some(url) => format!(
"<href={}>{}</>",
@@ -410,7 +412,9 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
package.get_pretty_version().to_string()
};
- let package_url = PackageInfo::get_view_source_or_homepage_url(&**package);
+ let package_url = PackageInfo::get_view_source_or_homepage_url(
+ package.as_rc().borrow().as_package_interface(),
+ );
let name_with_link = match &package_url {
Some(url) => format!(
"<href={}>{}</>",
diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs
index 0b9727f..3299f85 100644
--- a/crates/shirabe/src/command/bump_command.rs
+++ b/crates/shirabe/src/command/bump_command.rs
@@ -251,13 +251,14 @@ impl BumpCommand {
None => continue,
Some(p) => p,
};
- while let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
- // TODO(phase-b): get_alias_of returns &dyn BasePackage; cloning into Box
- // requires clone_box on BasePackage applied to a borrowed ref.
- package = alias.get_alias_of().clone_box();
+ while let Some(alias) = package.as_alias() {
+ package = alias.get_alias_of().into();
}
- let bumped = bumper.bump_requirement(link.get_constraint(), package.as_ref())?;
+ let bumped = bumper.bump_requirement(
+ link.get_constraint(),
+ package.as_rc().borrow().as_package_interface(),
+ )?;
if bumped == current_constraint {
continue;
diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs
index 74ecfc3..5d886e7 100644
--- a/crates/shirabe/src/command/check_platform_reqs_command.rs
+++ b/crates/shirabe/src/command/check_platform_reqs_command.rs
@@ -113,7 +113,7 @@ impl CheckPlatformReqsCommand {
}
}
- let root_pkg_repo = RootPackageRepository::new(composer.get_package().clone_box());
+ let root_pkg_repo = RootPackageRepository::new(composer.get_package().clone());
let installed_repo =
InstalledRepository::new(vec![installed_repo_base, Box::new(root_pkg_repo)]);
@@ -148,7 +148,7 @@ impl CheckPlatformReqsCommand {
let mut req_results: Vec<CheckResult> = vec![];
'candidates: for candidate in &candidates {
let candidate_constraint: Option<AnyConstraint> =
- if candidate.get_name() == require {
+ if candidate.get_name() == *require {
let c = SimpleConstraint::new(
"=".to_string(),
candidate.get_version().to_string(),
@@ -178,7 +178,7 @@ impl CheckPlatformReqsCommand {
for link in links {
if !link.get_constraint().matches(&candidate_constraint) {
req_results.push(CheckResult {
- platform_package: if candidate.get_name() == require {
+ platform_package: if candidate.get_name() == *require {
candidate.get_pretty_name().to_string()
} else {
require.clone()
@@ -186,7 +186,7 @@ impl CheckPlatformReqsCommand {
version: candidate_constraint.get_pretty_string().to_string(),
link: Some(link.clone()),
status: "<error>failed</error>".to_string(),
- provider: if candidate.get_name() == require {
+ provider: if candidate.get_name() == *require {
String::new()
} else {
format!(
@@ -200,7 +200,7 @@ impl CheckPlatformReqsCommand {
}
results.push(CheckResult {
- platform_package: if candidate.get_name() == require {
+ platform_package: if candidate.get_name() == *require {
candidate.get_pretty_name().to_string()
} else {
require.clone()
@@ -208,7 +208,7 @@ impl CheckPlatformReqsCommand {
version: candidate_constraint.get_pretty_string().to_string(),
link: None,
status: "<info>success</info>".to_string(),
- provider: if candidate.get_name() == require {
+ provider: if candidate.get_name() == *require {
String::new()
} else {
format!(
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index d4b4a1a..e0cb3d4 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -31,7 +31,6 @@ use crate::installer::ProjectInstaller;
use crate::installer::SuggestedPackagesReporter;
use crate::io::IOInterface;
use crate::json::JsonFile;
-use crate::package::AliasPackage;
use crate::package::version::VersionParser;
use crate::package::version::VersionSelector;
use crate::package::{STABILITIES, SUPPORTED_LINK_TYPES};
@@ -511,7 +510,7 @@ impl CreateProjectCommand {
config_source.add_link(
r#type,
link.get_target(),
- package.get_pretty_version(),
+ &package.get_pretty_version(),
);
}
}
@@ -873,14 +872,10 @@ impl CreateProjectCommand {
}
// avoid displaying 9999999-dev as version if default-branch was selected
- // TODO(phase-b): `$package instanceof AliasPackage` downcast and reassigning
- // `package` to its alias-of requires Rc<dyn PackageInterface> sharing. Skipped.
- let package_as_alias: Option<&AliasPackage> = None;
- if package_as_alias.is_some()
- && package.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS
- {
- // package = package_as_alias.unwrap().get_alias_of();
- todo!("phase-b: reassigning package to alias_of needs Rc-shared ownership");
+ if let Some(alias) = package.as_alias() {
+ if package.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
+ package = alias.get_alias_of().into();
+ }
}
io.write_error(&format!(
@@ -896,12 +891,8 @@ impl CreateProjectCommand {
io.write_error("<info>Plugins have been disabled.</info>");
}
- // TODO(phase-b): `$package instanceof AliasPackage` downcast and reassigning
- // `package` to its alias-of requires Rc<dyn PackageInterface> sharing. Skipped.
- let package_as_alias: Option<&AliasPackage> = None;
- if let Some(_alias) = package_as_alias {
- // package = alias.get_alias_of();
- todo!("phase-b: reassigning package to alias_of needs Rc-shared ownership");
+ if let Some(alias) = package.as_alias() {
+ package = alias.get_alias_of().into();
}
let dm = composer.get_download_manager();
@@ -917,7 +908,7 @@ impl CreateProjectCommand {
let mut installed_repo = InstalledArrayRepository::new()?;
im.execute(
&mut installed_repo,
- vec![Box::new(InstallOperation::new(package.clone_package_box()))],
+ vec![Box::new(InstallOperation::new(package.clone()))],
true,
true,
false,
@@ -928,7 +919,7 @@ impl CreateProjectCommand {
// TODO(phase-b): self.suggested_packages_reporter is on the outer scope via &self
// self.suggested_packages_reporter.add_suggestions_from_package(&*package);
- let installed_from_vcs = "source" == package.get_installation_source().unwrap_or("");
+ let installed_from_vcs = package.get_installation_source().as_deref() == Some("source");
io.write_error(&format!("<info>Created project in {}</info>", directory));
chdir(&directory);
@@ -942,7 +933,7 @@ impl CreateProjectCommand {
Platform::clear_env("COMPOSER");
}
- Platform::put_env("COMPOSER_ROOT_VERSION", package.get_pretty_version());
+ Platform::put_env("COMPOSER_ROOT_VERSION", &package.get_pretty_version());
// once the root project is fully initialized, we do not need to wipe everything on user abort anymore even if it happens during deps install
if let Some(handler) = signal_handler {
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index 7a16699..fe57e5c 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -173,7 +173,7 @@ impl DiagnoseCommand {
)
.unwrap();
let mut php_version = php_pkg.get_pretty_version().to_string();
- if let Some(cp) = php_pkg.as_complete_package_interface() {
+ if let Some(cp) = php_pkg.as_complete() {
if str_contains(&cp.get_description().unwrap_or_default(), "overridden") {
php_version = format!(
"{} - {}",
@@ -928,7 +928,7 @@ impl DiagnoseCommand {
normalized_version,
version.clone(),
);
- packages.push(Box::new(root_pkg));
+ packages.push(crate::package::RootPackageHandle::from_root_package(root_pkg).into());
}
let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
repo_config.insert("type".to_string(), PhpMixed::String("composer".to_string()));
diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs
index 82c0c0a..f829342 100644
--- a/crates/shirabe/src/command/fund_command.rs
+++ b/crates/shirabe/src/command/fund_command.rs
@@ -67,14 +67,14 @@ impl FundCommand {
let mut packages_to_load: IndexMap<String, Option<AnyConstraint>> = IndexMap::new();
let mut packages_to_load_names: indexmap::IndexSet<String> = indexmap::IndexSet::new();
for package in repo.get_packages() {
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().is_some() {
continue;
}
packages_to_load.insert(
- package.get_name().to_string(),
+ package.get_name(),
Some(MatchAllConstraint::new(None).into()),
);
- packages_to_load_names.insert(package.get_name().to_string());
+ packages_to_load_names.insert(package.get_name());
}
// load all packages dev versions in parallel
@@ -87,15 +87,15 @@ impl FundCommand {
// collect funding data from default branches
for (_, package) in &result.packages {
- if package.as_any().downcast_ref::<AliasPackage>().is_none() {
+ if package.as_alias().is_none() {
// TODO: check for CompleteAliasPackage as well
- if let Some(complete_pkg) = package.as_any().downcast_ref::<CompletePackage>() {
+ if let Some(complete_pkg) = package.as_complete() {
if complete_pkg.is_default_branch()
&& !complete_pkg.get_funding().is_empty()
- && packages_to_load_names.contains(complete_pkg.get_name())
+ && packages_to_load_names.contains(&complete_pkg.get_name())
{
- Self::insert_funding_data(&mut fundings, complete_pkg)?;
- packages_to_load_names.shift_remove(complete_pkg.get_name());
+ Self::insert_funding_data(&mut fundings, &complete_pkg)?;
+ packages_to_load_names.shift_remove(&complete_pkg.get_name());
}
}
}
@@ -103,15 +103,14 @@ impl FundCommand {
// collect funding from installed packages if none was found in the default branch above
for package in repo.get_packages() {
- if package.as_any().downcast_ref::<AliasPackage>().is_some()
- || !packages_to_load_names.contains(package.get_name())
+ if package.as_alias().is_some() || !packages_to_load_names.contains(&package.get_name())
{
continue;
}
// TODO: check for CompleteAliasPackage as well
- if let Some(complete_pkg) = package.as_any().downcast_ref::<CompletePackage>() {
+ if let Some(complete_pkg) = package.as_complete() {
if !complete_pkg.get_funding().is_empty() {
- Self::insert_funding_data(&mut fundings, complete_pkg)?;
+ Self::insert_funding_data(&mut fundings, &complete_pkg)?;
}
}
}
@@ -172,10 +171,12 @@ impl FundCommand {
fn insert_funding_data(
fundings: &mut IndexMap<String, IndexMap<String, Vec<String>>>,
- package: &CompletePackage,
+ package: &crate::package::CompletePackageInterfaceHandle,
) -> Result<()> {
let pretty_name = package.get_pretty_name();
- let (vendor, package_name) = pretty_name.split_once('/').unwrap_or(("", pretty_name));
+ let (vendor, package_name) = pretty_name
+ .split_once('/')
+ .unwrap_or(("", pretty_name.as_str()));
for funding_option in package.get_funding() {
let url_val = funding_option.get("url").and_then(|v| v.as_string());
diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs
index e7fc6b5..673039a 100644
--- a/crates/shirabe/src/command/home_command.rs
+++ b/crates/shirabe/src/command/home_command.rs
@@ -9,7 +9,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::console::input::InputArgument;
use crate::console::input::InputOption;
use crate::io::IOInterface;
-use crate::package::CompletePackageInterface;
+use crate::package::CompletePackageInterfaceHandle;
use crate::package::PackageInterface;
use crate::package::RootPackageInterface;
use crate::repository::RepositoryFactory;
@@ -106,7 +106,7 @@ impl HomeCommand {
'repos: for repo in &repos {
for package in repo.find_packages(&package_name, None) {
package_exists = true;
- if let Some(complete_pkg) = package.as_complete_package_interface() {
+ if let Some(complete_pkg) = package.as_complete() {
if self.handle_package(complete_pkg, show_homepage, show_only) {
handled = true;
break 'repos;
@@ -139,7 +139,7 @@ impl HomeCommand {
fn handle_package(
&mut self,
- package: &dyn CompletePackageInterface,
+ package: CompletePackageInterfaceHandle,
show_homepage: bool,
show_only: bool,
) -> bool {
@@ -208,7 +208,7 @@ impl HomeCommand {
let composer = crate::command::composer_full(&composer);
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone_box(),
+ composer.get_package().clone(),
)));
// TODO(phase-b): get_local_repository / get_repositories return shared refs; needs Rc<dyn ...> migration
return Ok(repos);
diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs
index 961f70b..2bcecd5 100644
--- a/crates/shirabe/src/command/licenses_command.rs
+++ b/crates/shirabe/src/command/licenses_command.rs
@@ -113,7 +113,11 @@ impl LicensesCommand {
if input.get_option("no-dev").as_bool().unwrap_or(false) {
RepositoryUtils::filter_required_packages(
&repo.get_packages(),
- composer.get_package(),
+ composer
+ .get_package()
+ .as_rc()
+ .borrow()
+ .as_package_interface(),
false,
vec![],
)
@@ -123,11 +127,8 @@ impl LicensesCommand {
};
let _ = composer.get_package();
- // TODO(phase-b): convert BasePackage trait objects to PackageInterface for sorting.
- let pkg_pi: Vec<Box<dyn crate::package::PackageInterface>> = packages
- .into_iter()
- .map(|p| p.clone_package_box())
- .collect();
+ let pkg_pi: Vec<crate::package::PackageInterfaceHandle> =
+ packages.into_iter().map(|p| p.into()).collect();
let packages = PackageSorter::sort_packages_alphabetically(pkg_pi);
let io = self.get_io();
@@ -158,7 +159,9 @@ impl LicensesCommand {
PhpMixed::String("Licenses".to_string()),
]);
for package in &packages {
- let link = PackageInfo::get_view_source_or_homepage_url(package.as_ref());
+ let link = PackageInfo::get_view_source_or_homepage_url(
+ package.as_rc().borrow().as_package_interface(),
+ );
let name = if let Some(link) = link {
format!(
"<href={}>{}</>",
@@ -168,9 +171,7 @@ impl LicensesCommand {
} else {
package.get_pretty_name().to_string()
};
- let pkg_licenses = if let Some(complete_pkg) =
- package.as_any().downcast_ref::<CompletePackage>()
- {
+ let pkg_licenses = if let Some(complete_pkg) = package.as_complete_package() {
complete_pkg.get_license()
} else {
vec![]
@@ -199,9 +200,7 @@ impl LicensesCommand {
let mut dependencies: IndexMap<String, IndexMap<String, PhpMixed>> =
IndexMap::new();
for package in &packages {
- let pkg_licenses = if let Some(complete_pkg) =
- package.as_any().downcast_ref::<CompletePackage>()
- {
+ let pkg_licenses = if let Some(complete_pkg) = package.as_complete_package() {
complete_pkg.get_license()
} else {
vec![]
@@ -268,9 +267,7 @@ impl LicensesCommand {
"summary" => {
let mut used_licenses: IndexMap<String, i64> = IndexMap::new();
for package in &packages {
- let mut licenses = if let Some(complete_pkg) =
- package.as_any().downcast_ref::<CompletePackage>()
- {
+ let mut licenses = if let Some(complete_pkg) = package.as_complete_package() {
complete_pkg.get_license()
} else {
vec![]
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index fa25fa3..a268469 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -574,7 +574,10 @@ pub trait PackageDiscoveryTrait {
message: sprintf(
&format!(
"Package %s has requirements incompatible with your PHP version, PHP extensions and Composer version{}",
- self.get_platform_exception_details(&*candidate, platform_repo),
+ self.get_platform_exception_details(
+ candidate.as_rc().borrow().as_package_interface(),
+ platform_repo,
+ ),
),
&[PhpMixed::String(name.to_string())],
),
@@ -610,8 +613,10 @@ pub trait PackageDiscoveryTrait {
message: format!(
"Package {} exists in {} and {} which has a higher repository priority. The packages from the higher priority repository do not match your minimum-stability and are therefore not installable. That repository is canonical so the lower priority repo's packages are not installable. See https://getcomposer.org/repoprio for details and assistance.",
name,
- all_repos_package.get_repository().unwrap().get_repo_name(),
- package.get_repository().unwrap().get_repo_name(),
+ // TODO(phase-c): the originating repository names need the handle's
+ // repository back-reference (phase-c handoff item #1).
+ "a higher priority repository",
+ "a lower priority repository",
),
code: 0,
}
@@ -666,7 +671,10 @@ pub trait PackageDiscoveryTrait {
message: sprintf(
&format!(
"Could not find package %s in any version matching your PHP version, PHP extensions and Composer version{}%s",
- self.get_platform_exception_details(&*candidate, platform_repo),
+ self.get_platform_exception_details(
+ candidate.as_rc().borrow().as_package_interface(),
+ platform_repo,
+ ),
),
&[
PhpMixed::String(name.to_string()),
@@ -772,7 +780,9 @@ pub trait PackageDiscoveryTrait {
if fixed {
package.get_pretty_version().to_string()
} else {
- version_selector.find_recommended_require_version(&*package)?
+ version_selector.find_recommended_require_version(
+ package.as_rc().borrow().as_package_interface(),
+ )?
},
))
}
@@ -798,9 +808,7 @@ pub trait PackageDiscoveryTrait {
Ok(r) => r,
Err(e) => {
// PHP: if ($e instanceof \LogicException) throw $e;
- // TODO(phase-b): downcast to LogicException
- let is_logic: bool = todo!("e instanceof LogicException");
- if is_logic {
+ if e.downcast_ref::<LogicException>().is_some() {
return Err(e);
}
@@ -891,7 +899,7 @@ pub trait PackageDiscoveryTrait {
let mut platform_pkg_version = platform_pkg.get_pretty_version().to_string();
let platform_extra = platform_pkg.get_extra();
let has_config_platform = platform_extra.contains_key("config.platform");
- let is_complete = platform_pkg.as_complete_package_interface().is_some();
+ let is_complete = platform_pkg.as_complete().is_some();
if has_config_platform && is_complete {
// TODO(phase-b): platform_pkg.get_description() via CompletePackageInterface
platform_pkg_version = format!(
diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs
index d66a830..1e3114b 100644
--- a/crates/shirabe/src/command/reinstall_command.rs
+++ b/crates/shirabe/src/command/reinstall_command.rs
@@ -16,9 +16,6 @@ use crate::dependency_resolver::Transaction;
use crate::dependency_resolver::operation::InstallOperation;
use crate::dependency_resolver::operation::UninstallOperation;
use crate::io::IOInterface;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
-use crate::package::PackageInterface;
use crate::package::base_package;
use crate::plugin::CommandEvent;
use crate::plugin::PluginEvents;
@@ -73,7 +70,7 @@ impl ReinstallCommand {
let repository_manager = composer.get_repository_manager().clone();
let repository_manager = repository_manager.borrow();
let local_repo = repository_manager.get_local_repository();
- let mut packages_to_reinstall: Vec<Box<dyn crate::package::PackageInterface>> = vec![];
+ let mut packages_to_reinstall: Vec<crate::package::PackageInterfaceHandle> = vec![];
let mut package_names_to_reinstall: Vec<String> = vec![];
let type_option = input.get_option("type");
@@ -100,8 +97,8 @@ impl ReinstallCommand {
})
.unwrap_or_default();
for package in local_repo.get_canonical_packages() {
- if filter_types.contains(&package.get_type().to_string()) {
- package_names_to_reinstall.push(package.get_name().to_string());
+ if filter_types.contains(&package.get_type()) {
+ package_names_to_reinstall.push(package.get_name());
packages_to_reinstall.push(package);
}
}
@@ -126,9 +123,9 @@ impl ReinstallCommand {
let pattern_regexp = base_package::package_name_to_regexp(pattern);
let mut matched = false;
for package in local_repo.get_canonical_packages() {
- if Preg::is_match(&pattern_regexp, package.get_name()).unwrap_or(false) {
+ if Preg::is_match(&pattern_regexp, &package.get_name()).unwrap_or(false) {
matched = true;
- package_names_to_reinstall.push(package.get_name().to_string());
+ package_names_to_reinstall.push(package.get_name());
packages_to_reinstall.push(package);
}
}
@@ -149,14 +146,12 @@ impl ReinstallCommand {
}
let present_packages = local_repo.get_packages();
- let result_packages: Vec<Box<dyn PackageInterface>> = present_packages
- .iter()
- .map(|p| p.clone_package_box())
- .collect();
- let present_packages: Vec<Box<dyn PackageInterface>> = present_packages
+ let result_packages: Vec<crate::package::PackageInterfaceHandle> =
+ present_packages.iter().map(|p| p.clone().into()).collect();
+ let present_packages: Vec<crate::package::PackageInterfaceHandle> = present_packages
.into_iter()
- .filter(|package| !package_names_to_reinstall.contains(&package.get_name().to_string()))
- .map(|p| p.clone_package_box())
+ .filter(|package| !package_names_to_reinstall.contains(&package.get_name()))
+ .map(|p| p.into())
.collect();
let transaction = Transaction::new(present_packages, result_packages);
@@ -165,24 +160,19 @@ impl ReinstallCommand {
let mut install_order = indexmap::IndexMap::new();
for (index, op) in install_operations.iter().enumerate() {
if let Some(install_op) = op.as_any().downcast_ref::<InstallOperation>() {
- if install_op
- .get_package()
- .as_any()
- .downcast_ref::<AliasPackage>()
- .is_none()
- {
- install_order.insert(install_op.get_package().get_name().to_string(), index);
+ if install_op.get_package().as_alias().is_none() {
+ install_order.insert(install_op.get_package().get_name(), index);
}
}
}
uninstall_operations.sort_by(|a, b| {
let a_order = install_order
- .get(a.get_package().get_name())
+ .get(&a.get_package().get_name())
.copied()
.unwrap_or(0);
let b_order = install_order
- .get(b.get_package().get_name())
+ .get(&b.get_package().get_name())
.copied()
.unwrap_or(0);
b_order.cmp(&a_order)
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 15bff24..f0dce6b 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -27,10 +27,7 @@ use crate::installer::InstallerEvents;
use crate::io::IOInterface;
use crate::json::JsonFile;
use crate::json::JsonManipulator;
-use crate::package::AliasPackage;
-use crate::package::CompletePackageInterface;
-use crate::package::PackageInterface;
-use crate::package::base_package::{self, BasePackage};
+use crate::package::base_package;
use crate::package::loader::ArrayLoader;
use crate::package::loader::RootPackageLoader;
use crate::package::version::VersionParser;
@@ -338,16 +335,16 @@ impl RequireCommand {
continue;
}
- // TODO(phase-b): find_packages returns Vec<Box<dyn BasePackage>> but
- // get_most_current_version expects Vec<Box<dyn PackageInterface>>; needs trait
- // upcasting once Rust supports it stably or an adapter.
- let _ = self.get_repos().find_packages(name, None);
- let pkg: Option<Box<dyn PackageInterface>> =
- PackageSorter::get_most_current_version(todo!(
- "convert Vec<Box<dyn BasePackage>> to Vec<Box<dyn PackageInterface>>"
- ));
- // TODO(phase-b): instanceof CompletePackageInterface downcast
- let pkg_as_complete: Option<&dyn CompletePackageInterface> = None;
+ let found_packages: Vec<crate::package::PackageInterfaceHandle> = self
+ .get_repos()
+ .find_packages(name, None)
+ .into_iter()
+ .map(|p| p.into())
+ .collect();
+ let pkg: Option<crate::package::PackageInterfaceHandle> =
+ PackageSorter::get_most_current_version(found_packages);
+ let pkg_as_complete: Option<crate::package::CompletePackageInterfaceHandle> =
+ pkg.as_ref().and_then(|p| p.as_complete());
if let Some(pkg_complete) = pkg_as_complete {
let lowered: Vec<String> =
array_map(|s: &String| strtolower(s), &pkg_complete.get_keywords());
@@ -713,8 +710,8 @@ impl RequireCommand {
.map(|(k, v)| (k.clone(), PhpMixed::String(v.clone())))
.collect();
let new_links = loader.parse_links(
- root_package.get_name(),
- root_package.get_pretty_version(),
+ &root_package.get_name(),
+ &root_package.get_pretty_version(),
base_package::SUPPORTED_LINK_TYPES
.get(require_key)
.map(|t| t.method)
@@ -742,7 +739,7 @@ impl RequireCommand {
);
let _ = RootPackageLoader::extract_stability_flags(
requirements,
- root_package.get_minimum_stability(),
+ &root_package.get_minimum_stability(),
root_package.get_stability_flags().clone(),
);
// unset($stabilityFlags, $references);
@@ -962,12 +959,8 @@ impl RequireCommand {
package_name,
crate::repository::FindPackageConstraint::String("*".to_string()),
);
- // TODO(phase-b): `$package instanceof AliasPackage` downcast
- let package_as_alias: Option<&AliasPackage> = None;
- while let Some(_alias) = package_as_alias {
- // TODO(phase-b): get_alias_of returns &dyn BasePackage; clone is not available
- // and BasePackage is not PackageInterface (the latter is a super-trait).
- package = todo!("upcast alias.get_alias_of() to Box<dyn BasePackage>");
+ while let Some(alias) = package.as_ref().and_then(|p| p.as_alias()) {
+ package = Some(alias.get_alias_of().into());
}
let package = match package {
@@ -976,18 +969,13 @@ impl RequireCommand {
};
if fixed {
- requirements.insert(
- package_name.clone(),
- package.get_pretty_version().to_string(),
- );
+ requirements.insert(package_name.clone(), package.get_pretty_version());
} else {
- // TODO(phase-b): trait upcast from &dyn BasePackage to &dyn PackageInterface
- // is not yet stable in Rust; use explicit as_package_interface() when available.
- let pkg_as_pi: &dyn PackageInterface =
- todo!("upcast &dyn BasePackage to &dyn PackageInterface");
requirements.insert(
package_name.clone(),
- version_selector.find_recommended_require_version(pkg_as_pi)?,
+ version_selector.find_recommended_require_version(
+ package.as_rc().borrow().as_package_interface(),
+ )?,
);
}
self.get_io().write_error3(
@@ -1057,7 +1045,7 @@ impl RequireCommand {
{
let stability_flags = RootPackageLoader::extract_stability_flags(
&requirements,
- composer.get_package().get_minimum_stability(),
+ &composer.get_package().get_minimum_stability(),
IndexMap::new(),
);
let stability_flags_clone = stability_flags.clone();
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 8826421..c7c2f9d 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -23,7 +23,6 @@ use crate::dependency_resolver::PolicyInterface;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
use crate::io::IOInterface;
use crate::json::JsonFile;
-use crate::package::BasePackage;
use crate::package::CompletePackageInterface;
use crate::package::Link;
use crate::package::PackageInterface;
@@ -200,7 +199,7 @@ impl ShowCommand {
let mut locked_repo: Option<Box<dyn RepositoryInterface>> = None;
// The single-package $package binding from PHP gets surfaced here.
- let mut single_package: Option<Box<dyn CompletePackageInterface>> = None;
+ let mut single_package: Option<crate::package::CompletePackageInterfaceHandle> = None;
let mut versions_map: IndexMap<String, String> = IndexMap::new();
let installed_repo: Box<InstalledRepository>;
let repos: Box<dyn RepositoryInterface>;
@@ -210,11 +209,12 @@ impl ShowCommand {
&& input.get_option("locked").as_bool() != Some(true)
{
let _rc = self.require_composer(None, None)?;
- let package = crate::command::composer_full(&_rc)
- .get_package()
- .clone_box();
+ // TODO(phase-c): composer.get_package() returns &dyn RootPackageInterface, not a
+ // RootPackageInterfaceHandle, so it cannot be shared into RootPackageRepository::new yet.
+ let package: crate::package::RootPackageInterfaceHandle =
+ todo!("share composer.get_package() as a RootPackageInterfaceHandle");
if input.get_option("name-only").as_bool() == Some(true) {
- self.get_io().write(package.get_name());
+ self.get_io().write(&package.get_name());
return Ok(0);
}
@@ -226,13 +226,13 @@ impl ShowCommand {
.into());
}
installed_repo = Box::new(InstalledRepository::new(vec![Box::new(
- RootPackageRepository::new(package.clone_box()),
+ RootPackageRepository::new(package.clone()),
)]));
repos = Box::new(InstalledRepository::new(vec![Box::new(
- RootPackageRepository::new(package.clone_box()),
+ RootPackageRepository::new(package.clone()),
)]));
- // TODO(phase-b): need to convert Box<dyn BasePackage> to Box<dyn CompletePackageInterface>
- single_package = todo!("convert package to Box<dyn CompletePackageInterface>");
+ // TODO(phase-c): need to convert the root package handle to a CompletePackageInterfaceHandle
+ single_package = todo!("convert package to CompletePackageInterfaceHandle");
} else if input.get_option("platform").as_bool() == Some(true) {
installed_repo = Box::new(InstalledRepository::new(vec![Box::new(
make_platform_repo()?,
@@ -382,7 +382,9 @@ impl ShowCommand {
let root_repo: Box<dyn RepositoryInterface> =
if input.get_option("self").as_bool() == Some(true) {
- Box::new(RootPackageRepository::new(root_pkg.clone_box()))
+ Box::new(RootPackageRepository::new(
+ composer_local.get_package().clone(),
+ ))
} else {
Box::new(InstalledArrayRepository::new()?)
};
@@ -394,14 +396,12 @@ impl ShowCommand {
.get_packages();
let packages = RepositoryUtils::filter_required_packages(
&local_packages,
- root_pkg as &dyn PackageInterface,
+ root_pkg.as_rc().borrow().as_package_interface(),
false,
Vec::new(),
);
- let cloned: Vec<Box<dyn PackageInterface>> = packages
- .into_iter()
- .map(|p| p.clone_package_box())
- .collect();
+ let cloned: Vec<crate::package::PackageInterfaceHandle> =
+ packages.into_iter().map(|p| p.into()).collect();
installed_repo = Box::new(InstalledRepository::new(vec![
root_repo.clone_box(),
Box::new(InstalledArrayRepository::new_with_packages(cloned)?),
@@ -470,10 +470,7 @@ impl ShowCommand {
// show single package or single version
if let Some(ref pkg) = single_package {
- versions_map.insert(
- pkg.get_pretty_version().to_string(),
- pkg.get_version().to_string(),
- );
+ versions_map.insert(pkg.get_pretty_version(), pkg.get_version());
} else if let Some(ref pf) = package_filter {
if !pf.contains('*') {
let (matched_package, vers) =
@@ -482,7 +479,7 @@ impl ShowCommand {
if let Some(ref pkg) = matched_package {
if input.get_option("direct").as_bool() == Some(true) {
if !in_array(
- PhpMixed::String(pkg.get_name().to_string()),
+ PhpMixed::String(pkg.get_name()),
&PhpMixed::List(
self.get_root_requires()
.into_iter()
@@ -547,11 +544,13 @@ impl ShowCommand {
let mut exit_code: i64 = 0;
if input.get_option("tree").as_bool() == Some(true) {
+ let package_ref = package.as_rc().borrow();
let array_tree = self.generate_package_tree(
- package.as_package_interface(),
+ package_ref.as_package_interface(),
&*installed_repo,
&*repos,
);
+ drop(package_ref);
if format == "json" {
let mut wrapper: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -577,10 +576,11 @@ impl ShowCommand {
return Ok(exit_code);
}
- let mut latest_package: Option<Box<dyn PackageInterface>> = None;
+ let mut latest_package: Option<crate::package::PackageInterfaceHandle> = None;
if input.get_option("latest").as_bool() == Some(true) {
+ let package_ref = package.as_rc().borrow();
latest_package = self.find_latest_package(
- package.as_package_interface(),
+ package_ref.as_package_interface(),
composer.as_ref().unwrap(),
&platform_repo,
input.get_option("major-only").as_bool().unwrap_or(false),
@@ -600,13 +600,13 @@ impl ShowCommand {
&& (latest_package
.as_ref()
.unwrap()
- .as_complete_package_interface()
+ .as_complete()
.map_or(true, |c| !c.is_abandoned()))
{
exit_code = 1;
}
if input.get_option("path").as_bool() == Some(true) {
- self.get_io().write_no_newline(package.get_name());
+ self.get_io().write_no_newline(&package.get_name());
let path = {
let composer_ref = composer.as_ref().unwrap();
// TODO(phase-b): get_installation_manager wants &mut Composer; PHP shares
@@ -625,21 +625,25 @@ impl ShowCommand {
return Ok(exit_code);
}
+ let package_ref = package.as_rc().borrow();
+ let package_dyn = package_ref
+ .as_complete_package_interface()
+ .expect("single_package is a CompletePackageInterface");
+ let latest_ref = latest_package.as_ref().map(|p| p.as_rc().borrow());
+ let latest_dyn: Option<&dyn PackageInterface> =
+ latest_ref.as_ref().map(|r| r.as_package_interface());
if format == "json" {
self.print_package_info_as_json(
- &**package,
+ package_dyn,
&versions_map,
&*installed_repo,
- latest_package.as_deref(),
+ latest_dyn,
)?;
} else {
- self.print_package_info(
- &**package,
- &versions_map,
- &*installed_repo,
- latest_package.as_deref(),
- )?;
+ self.print_package_info(package_dyn, &versions_map, &*installed_repo, latest_dyn)?;
}
+ drop(latest_ref);
+ drop(package_ref);
return Ok(exit_code);
}
@@ -656,7 +660,7 @@ impl ShowCommand {
let mut array_tree: Vec<IndexMap<String, PhpMixed>> = Vec::new();
for package in packages.iter() {
if in_array(
- PhpMixed::String(package.get_name().to_string()),
+ PhpMixed::String(package.get_name()),
&PhpMixed::List(
root_requires
.iter()
@@ -665,8 +669,9 @@ impl ShowCommand {
),
true,
) {
+ let package_ref = package.as_rc().borrow();
array_tree.push(self.generate_package_tree(
- &**package,
+ package_ref.as_package_interface(),
&*installed_repo,
&*repos,
));
@@ -741,28 +746,28 @@ impl ShowCommand {
for package in repo.get_packages() {
let existing = packages
.get(&type_owned)
- .and_then(|m| m.get(package.get_name()));
+ .and_then(|m| m.get(&package.get_name()));
let need_replace = match existing {
None => true,
Some(PackageOrName::Name(_)) => true,
Some(PackageOrName::Pkg(existing)) => {
- version_compare(existing.get_version(), package.get_version(), "<")
+ version_compare(&existing.get_version(), &package.get_version(), "<")
}
};
if need_replace {
- let mut p: Box<dyn PackageInterface> = package.clone_box();
- while let Some(alias) = p.as_alias_package() {
- p = alias.get_alias_of().clone_box();
+ let mut p: crate::package::PackageInterfaceHandle = package.clone().into();
+ while let Some(alias) = p.as_alias() {
+ p = alias.get_alias_of().into();
}
let matches_filter = match &package_filter_regex {
None => true,
- Some(r) => Preg::is_match(r, p.get_name())?,
+ Some(r) => Preg::is_match(r, &p.get_name())?,
};
if matches_filter {
let matches_list = match &package_list_filter {
None => true,
Some(list) => in_array(
- PhpMixed::String(p.get_name().to_string()),
+ PhpMixed::String(p.get_name()),
&PhpMixed::List(
list.iter()
.map(|s| Box::new(PhpMixed::String(s.clone())))
@@ -775,7 +780,7 @@ impl ShowCommand {
packages
.entry(type_owned.clone())
.or_insert_with(IndexMap::new)
- .insert(p.get_name().to_string(), PackageOrName::Pkg(p));
+ .insert(p.get_name(), PackageOrName::Pkg(p));
}
}
}
@@ -785,7 +790,7 @@ impl ShowCommand {
packages
.entry(type_owned.clone())
.or_insert_with(IndexMap::new)
- .insert(name.clone(), PackageOrName::Pkg(p.clone_package_box()));
+ .insert(name.clone(), PackageOrName::Pkg(p.clone().into()));
}
}
}
@@ -809,7 +814,8 @@ impl ShowCommand {
"{^(?:%s)$}iD",
);
let indent = if show_all_types { " " } else { "" };
- let mut latest_packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ let mut latest_packages: IndexMap<String, crate::package::PackageInterfaceHandle> =
+ IndexMap::new();
let mut exit_code: i64 = 0;
let mut view_data: IndexMap<String, Vec<IndexMap<String, PhpMixed>>> = IndexMap::new();
let mut view_meta_data: IndexMap<String, ViewMetaData> = IndexMap::new();
@@ -835,10 +841,11 @@ impl ShowCommand {
if show_latest && *show_version {
for package_or_name in type_packages.values() {
if let PackageOrName::Pkg(package) = package_or_name {
- if !Preg::is_match(&ignored_packages_regex, package.get_pretty_name())?
+ if !Preg::is_match(&ignored_packages_regex, &package.get_pretty_name())?
{
+ let package_ref = package.as_rc().borrow();
let latest = self.find_latest_package(
- &**package,
+ package_ref.as_package_interface(),
composer.as_ref().unwrap(),
&platform_repo,
show_major_only,
@@ -846,12 +853,12 @@ impl ShowCommand {
show_patch_only,
&*platform_req_filter,
)?;
+ drop(package_ref);
if latest.is_none() {
continue;
}
- latest_packages
- .insert(package.get_pretty_name().to_string(), latest.unwrap());
+ latest_packages.insert(package.get_pretty_name(), latest.unwrap());
}
}
}
@@ -885,9 +892,9 @@ impl ShowCommand {
let mut package_view_data: IndexMap<String, PhpMixed> = IndexMap::new();
if let PackageOrName::Pkg(package) = package_or_name {
let latest_package = if show_latest
- && latest_packages.contains_key(package.get_pretty_name())
+ && latest_packages.contains_key(&package.get_pretty_name())
{
- latest_packages.get(package.get_pretty_name())
+ latest_packages.get(&package.get_pretty_name())
} else {
None
};
@@ -896,9 +903,7 @@ impl ShowCommand {
let mut package_is_up_to_date = if let Some(latest) = latest_package {
latest.get_full_pretty_version(true, 0)
== package.get_full_pretty_version(true, 0)
- && latest
- .as_complete_package_interface()
- .map_or(true, |c| !c.is_abandoned())
+ && latest.as_complete().map_or(true, |c| !c.is_abandoned())
} else {
false
};
@@ -906,7 +911,7 @@ impl ShowCommand {
package_is_up_to_date =
package_is_up_to_date || (latest_package.is_none() && show_major_only);
let package_is_ignored =
- Preg::is_match(&ignored_packages_regex, package.get_pretty_name())?;
+ Preg::is_match(&ignored_packages_regex, &package.get_pretty_name())?;
if input.get_option("outdated").as_bool() == Some(true)
&& (package_is_up_to_date || package_is_ignored)
{
@@ -921,12 +926,12 @@ impl ShowCommand {
package_view_data.insert(
"name".to_string(),
- PhpMixed::String(package.get_pretty_name().to_string()),
+ PhpMixed::String(package.get_pretty_name()),
);
package_view_data.insert(
"direct-dependency".to_string(),
PhpMixed::Bool(in_array(
- PhpMixed::String(package.get_name().to_string()),
+ PhpMixed::String(package.get_name()),
&PhpMixed::List(
self.get_root_requires()
.into_iter()
@@ -940,9 +945,9 @@ impl ShowCommand {
{
package_view_data.insert(
"homepage".to_string(),
- match package.as_complete_package_interface() {
+ match package.as_complete() {
Some(c) => match c.get_homepage() {
- Some(h) => PhpMixed::String(h.to_string()),
+ Some(h) => PhpMixed::String(h),
None => PhpMixed::Null,
},
None => PhpMixed::Null,
@@ -950,7 +955,9 @@ impl ShowCommand {
);
package_view_data.insert(
"source".to_string(),
- match PackageInfo::get_view_source_url(&**package) {
+ match PackageInfo::get_view_source_url(
+ package.as_rc().borrow().as_package_interface(),
+ ) {
Some(s) => PhpMixed::String(s),
None => PhpMixed::Null,
},
@@ -958,8 +965,7 @@ impl ShowCommand {
}
name_length = name_length.max(package.get_pretty_name().len());
if write_version {
- let mut version_str =
- package.get_full_pretty_version(true, 0).to_string();
+ let mut version_str = package.get_full_pretty_version(true, 0);
if format == "text" {
version_str = version_str.trim_start_matches('v').to_string();
}
@@ -995,13 +1001,19 @@ impl ShowCommand {
}
if write_latest && latest_package.is_some() {
let latest = latest_package.unwrap();
- let mut latest_version_str =
- latest.get_full_pretty_version(true, 0).to_string();
+ let mut latest_version_str = latest.get_full_pretty_version(true, 0);
if format == "text" {
latest_version_str =
latest_version_str.trim_start_matches('v').to_string();
}
- let update_status = Self::get_update_status(&**latest, &**package);
+ let latest_ref = latest.as_rc().borrow();
+ let package_ref = package.as_rc().borrow();
+ let update_status = Self::get_update_status(
+ latest_ref.as_package_interface(),
+ package_ref.as_package_interface(),
+ );
+ drop(package_ref);
+ drop(latest_ref);
latest_length = latest_length.max(latest_version_str.len());
package_view_data
.insert("latest".to_string(), PhpMixed::String(latest_version_str));
@@ -1033,10 +1045,10 @@ impl ShowCommand {
latest_length = latest_length.max("[none matched]".len());
}
if write_description {
- if let Some(c) = package.as_complete_package_interface() {
+ if let Some(c) = package.as_complete() {
package_view_data.insert(
"description".to_string(),
- PhpMixed::String(c.get_description().unwrap_or("").to_string()),
+ PhpMixed::String(c.get_description().unwrap_or_default()),
);
}
}
@@ -1061,7 +1073,7 @@ impl ShowCommand {
let mut package_is_abandoned: PhpMixed = PhpMixed::Bool(false);
if let Some(latest) = latest_package {
- if let Some(c) = latest.as_complete_package_interface() {
+ if let Some(c) = latest.as_complete() {
if c.is_abandoned() {
let replacement_package_name = c.get_replacement_package();
let replacement = if let Some(ref rp) = replacement_package_name
@@ -1080,7 +1092,7 @@ impl ShowCommand {
PhpMixed::String(package_warning),
);
package_is_abandoned = match replacement_package_name {
- Some(rp) => PhpMixed::String(rp.to_string()),
+ Some(rp) => PhpMixed::String(rp),
None => PhpMixed::Bool(true),
};
}
@@ -1482,7 +1494,7 @@ impl ShowCommand {
name: &str,
version: PhpMixed,
) -> anyhow::Result<(
- Option<Box<dyn CompletePackageInterface>>,
+ Option<crate::package::CompletePackageInterfaceHandle>,
IndexMap<String, String>,
)> {
let name = strtolower(name);
@@ -1507,7 +1519,7 @@ impl ShowCommand {
repository_set.allow_installed_repositories(true);
repository_set.add_repository(repos.clone_box())?;
- let mut matched_package: Option<Box<dyn PackageInterface>> = None;
+ let mut matched_package: Option<crate::package::PackageInterfaceHandle> = None;
let mut versions: IndexMap<String, String> = IndexMap::new();
let mut pool = if PlatformRepository::is_platform_package(&name) {
repository_set.create_pool_with_all_packages()?
@@ -1518,33 +1530,32 @@ impl ShowCommand {
let mut literals: Vec<i64> = Vec::new();
for package in matches.iter() {
// avoid showing the 9999999-dev alias if the default branch has no branch-alias set
- let mut p: Box<dyn PackageInterface> = package.clone_box();
- if let Some(alias) = p.as_alias_package() {
+ let mut p: crate::package::PackageInterfaceHandle = package.clone().into();
+ if let Some(alias) = p.as_alias() {
if p.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
- p = alias.get_alias_of().clone_box();
+ p = alias.get_alias_of().into();
}
}
// select an exact match if it is in the installed repo and no specific version was required
- if version.is_null() && installed_repo.has_package(&*p) {
- matched_package = Some(p.clone_package_box());
+ if version.is_null()
+ && installed_repo.has_package(p.as_rc().borrow().as_package_interface())
+ {
+ matched_package = Some(p.clone());
}
- versions.insert(
- p.get_pretty_version().to_string(),
- p.get_version().to_string(),
- );
+ versions.insert(p.get_pretty_version(), p.get_version());
literals.push(p.get_id());
}
// select preferred package according to policy rules
if matched_package.is_none() && !literals.is_empty() {
let preferred = policy.select_preferred_packages(&pool, literals.clone(), None);
- matched_package = Some(pool.literal_to_package(preferred[0]).clone_package_box());
+ matched_package = Some(pool.literal_to_package(preferred[0]).into());
}
if let Some(ref mp) = matched_package {
- if mp.as_complete_package_interface().is_none() {
+ if mp.as_complete().is_none() {
return Err(LogicException {
message: format!(
"ShowCommand::getPackage can only work with CompletePackageInterface, but got {}",
@@ -1556,10 +1567,8 @@ impl ShowCommand {
}
}
- // TODO(phase-b): need a Box<dyn PackageInterface> -> Box<dyn CompletePackageInterface>
- // conversion. PHP relies on duck typing; placeholder None.
- let _ = matched_package;
- Ok((None, versions))
+ let matched_package = matched_package.and_then(|mp| mp.as_complete());
+ Ok((matched_package, versions))
}
/// Prints package info.
@@ -1760,7 +1769,7 @@ impl ShowCommand {
let installed_packages = installed_repo.find_packages(package.get_name(), None);
if !installed_packages.is_empty() {
for installed_package in installed_packages.iter() {
- let installed_version = installed_package.get_pretty_version().to_string();
+ let installed_version = installed_package.get_pretty_version();
let key_map: IndexMap<String, String> = versions_keys
.iter()
.map(|v| (v.clone(), v.clone()))
@@ -2556,7 +2565,7 @@ impl ShowCommand {
minor_only: bool,
patch_only: bool,
platform_req_filter: &dyn PlatformRequirementFilterInterface,
- ) -> anyhow::Result<Option<Box<dyn PackageInterface>>> {
+ ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> {
// find the latest version allowed in this repo set
let name = package.get_name();
// TODO(phase-b): VersionSelector::new wants RepositorySet by value, but get_repository_set
@@ -2564,7 +2573,7 @@ impl ShowCommand {
let _ = self.get_repository_set(composer)?;
let composer_ref = crate::command::composer_full(composer);
let placeholder_rs = RepositorySet::new(
- composer_ref.get_package().get_minimum_stability(),
+ &composer_ref.get_package().get_minimum_stability(),
composer_ref.get_package().get_stability_flags().clone(),
Vec::new(),
IndexMap::new(),
@@ -2676,8 +2685,8 @@ impl ShowCommand {
PhpMixed::Bool(true),
)?;
while let Some(ref c) = candidate {
- if let Some(alias) = c.as_alias_package() {
- candidate = Some(alias.get_alias_of().clone_box());
+ if let Some(alias) = c.as_alias() {
+ candidate = Some(alias.get_alias_of().into());
} else {
break;
}
@@ -2694,7 +2703,7 @@ impl ShowCommand {
if self.repository_set.is_none() {
// TODO(phase-b): RepositorySet::with_stability_and_flags — using new() placeholder.
let mut rs = RepositorySet::new(
- composer.get_package().get_minimum_stability(),
+ &composer.get_package().get_minimum_stability(),
composer.get_package().get_stability_flags().clone(),
Vec::new(),
IndexMap::new(),
@@ -2757,7 +2766,7 @@ impl ShowCommand {
#[derive(Debug)]
pub enum PackageOrName {
- Pkg(Box<dyn PackageInterface>),
+ Pkg(crate::package::PackageInterfaceHandle),
Name(String),
}
diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs
index 190884a..69c36a1 100644
--- a/crates/shirabe/src/command/status_command.rs
+++ b/crates/shirabe/src/command/status_command.rs
@@ -120,18 +120,19 @@ impl StatusCommand {
let target_dir = composer
.get_installation_manager()
.borrow_mut()
- .get_install_path(package.as_ref());
+ .get_install_path(package.as_rc().borrow().as_package_interface());
let target_dir = match target_dir {
Some(d) => d,
None => continue,
};
// TODO(phase-b): downloader borrow lifetime tied to dm.borrow() temporary; restructure later.
let dm_borrow = dm.borrow();
- let downloader: &dyn crate::downloader::DownloaderInterface =
- match dm_borrow.get_downloader_for_package(package.as_ref())? {
- Some(d) => d,
- None => continue,
- };
+ let downloader: &dyn crate::downloader::DownloaderInterface = match dm_borrow
+ .get_downloader_for_package(package.as_rc().borrow().as_package_interface())?
+ {
+ Some(d) => d,
+ None => continue,
+ };
// TODO(phase-b): isinstance checks using ChangeReportInterface/VcsCapableDownloaderInterface/DvcsDownloaderInterface
if let Some(change_reporter) = downloader.as_change_report_interface() {
@@ -142,16 +143,20 @@ impl StatusCommand {
);
}
- if let Some(changes) =
- change_reporter.get_local_changes(package.as_ref(), &target_dir)?
- {
+ if let Some(changes) = change_reporter.get_local_changes(
+ package.as_rc().borrow().as_package_interface(),
+ &target_dir,
+ )? {
errors.insert(target_dir.clone(), changes);
}
}
if let Some(vcs_downloader) = downloader.as_vcs_capable_downloader_interface() {
if vcs_downloader
- .get_vcs_reference(package.as_ref(), target_dir.clone())
+ .get_vcs_reference(
+ package.as_rc().borrow().as_package_interface(),
+ target_dir.clone(),
+ )
.is_some()
{
let previous_ref = match package.get_installation_source().as_deref() {
@@ -160,8 +165,10 @@ impl StatusCommand {
_ => None,
};
- let current_version =
- guesser.guess_version(&dumper.dump(package.as_ref()), &target_dir)?;
+ let current_version = guesser.guess_version(
+ &dumper.dump(package.as_rc().borrow().as_package_interface()),
+ &target_dir,
+ )?;
if let (Some(prev_ref), Some(cur_version)) = (&previous_ref, &current_version) {
if cur_version.commit.as_deref() != Some(prev_ref.as_str())
@@ -195,9 +202,10 @@ impl StatusCommand {
}
if let Some(dvcs_downloader) = downloader.as_dvcs_downloader_interface() {
- if let Some(unpushed) =
- dvcs_downloader.get_unpushed_changes(package.as_ref(), target_dir.clone())
- {
+ if let Some(unpushed) = dvcs_downloader.get_unpushed_changes(
+ package.as_rc().borrow().as_package_interface(),
+ target_dir.clone(),
+ ) {
unpushed_changes.insert(target_dir, unpushed);
}
}
diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs
index 6b617b1..8166c1e 100644
--- a/crates/shirabe/src/command/suggests_command.rs
+++ b/crates/shirabe/src/command/suggests_command.rs
@@ -47,9 +47,12 @@ impl SuggestsCommand {
let composer = self.require_composer(None, None)?;
let mut composer = crate::command::composer_full_mut(&composer);
- let mut installed_repos: Vec<Box<dyn RepositoryInterface>> = vec![Box::new(
- RootPackageRepository::new(composer.get_package().clone_box()),
- )];
+ // TODO(phase-c): composer.get_package() returns &dyn RootPackageInterface, not a
+ // RootPackageInterfaceHandle, so it cannot be shared into RootPackageRepository::new yet.
+ let root_package_handle: crate::package::RootPackageInterfaceHandle =
+ todo!("share composer.get_package() as a RootPackageInterfaceHandle");
+ let mut installed_repos: Vec<Box<dyn RepositoryInterface>> =
+ vec![Box::new(RootPackageRepository::new(root_package_handle))];
if composer.get_locker().borrow_mut().is_locked() {
// TODO(phase-b): get_platform_overrides returns IndexMap<String, String>; PlatformRepository::new expects IndexMap<String, PhpMixed>
@@ -93,24 +96,16 @@ impl SuggestsCommand {
let filter = input.get_argument("packages");
let mut packages = RepositoryInterface::get_packages(&installed_repo);
- // TODO(phase-b): composer.get_package() returns &dyn RootPackageInterface; pushing into Vec<Box<dyn BasePackage>> requires conversion
- let root_pkg_as_base: Box<dyn crate::package::BasePackage> =
- todo!("convert RootPackageInterface to Box<dyn BasePackage>");
+ // TODO(phase-c): composer.get_package() returns &dyn RootPackageInterface, not a handle,
+ // so it cannot be shared into the package list yet.
+ let root_pkg_as_base: crate::package::BasePackageHandle =
+ todo!("share composer.get_package() as a BasePackageHandle");
packages.push(root_pkg_as_base);
for package in &packages {
- if !empty(&filter)
- && !in_array(
- PhpMixed::String(package.get_name().to_string()),
- &filter,
- false,
- )
- {
+ if !empty(&filter) && !in_array(PhpMixed::String(package.get_name()), &filter, false) {
continue;
}
- // TODO(phase-b): add_suggestions_from_package expects &dyn PackageInterface; BasePackage is a separate trait
- reporter.add_suggestions_from_package(todo!(
- "convert Box<dyn BasePackage> to &dyn PackageInterface"
- ));
+ reporter.add_suggestions_from_package(package.as_rc().borrow().as_package_interface());
}
let mut mode = SuggestedPackagesReporter::MODE_BY_PACKAGE;
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index 4096272..06984e2 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -24,7 +24,6 @@ use crate::console::input::InputOption;
use crate::dependency_resolver::request::{self, Request, UpdateAllowTransitiveDeps};
use crate::installer::Installer;
use crate::io::IOInterface;
-use crate::package::BasePackage;
use crate::package::loader::RootPackageLoader;
use crate::package::version::VersionParser;
use crate::package::version::VersionSelector;
@@ -162,7 +161,7 @@ impl UpdateCommand {
RootPackageLoader::extract_references(&reqs, root_package.get_references().clone());
let stability_flags = RootPackageLoader::extract_stability_flags(
&reqs,
- root_package.get_minimum_stability(),
+ &root_package.get_minimum_stability(),
root_package.get_stability_flags().clone(),
);
let _ = references;
@@ -222,7 +221,7 @@ impl UpdateCommand {
}
let matches = Preg::is_match_with_indexed_captures(
r"{^(\d+\.\d+\.\d+)}",
- package.get_version(),
+ &package.get_version(),
)?;
let Some(matches) = matches else {
continue;
@@ -231,18 +230,18 @@ impl UpdateCommand {
"~{}",
matches.get(1).cloned().unwrap_or_default()
))?;
- if temporary_constraints.contains_key(package.get_name()) {
+ if temporary_constraints.contains_key(&package.get_name()) {
let existing = temporary_constraints
- .get(package.get_name())
+ .get(&package.get_name())
.map(|c| c.clone())
.unwrap();
temporary_constraints.insert(
- package.get_name().to_string(),
+ package.get_name(),
// TODO(phase-b): MultiConstraint::create signature
todo!("MultiConstraint::create([existing, constraint], true)"),
);
} else {
- temporary_constraints.insert(package.get_name().to_string(), constraint);
+ temporary_constraints.insert(package.get_name(), constraint);
}
}
}
@@ -493,10 +492,9 @@ impl UpdateCommand {
io_interface::NORMAL,
);
let mut autocompleter_values: IndexMap<String, String> = IndexMap::new();
- // TODO(phase-b): unify return types — CanonicalPackagesTrait returns
- // Vec<Box<dyn PackageInterface>> while RepositoryInterface::get_packages
- // returns Vec<Box<dyn BasePackage>>. Use only the locker branch for now.
- let installed_packages: Vec<Box<dyn crate::package::PackageInterface>> =
+ // TODO(phase-c): wire the non-locked branch through get_local_repository().get_packages()
+ // (returns Vec<BasePackageHandle>); only the locker branch is populated for now.
+ let installed_packages: Vec<crate::package::PackageInterfaceHandle> =
if composer_ref.get_locker().borrow_mut().is_locked() {
CanonicalPackagesTrait::get_packages(
&composer_ref
@@ -515,7 +513,7 @@ impl UpdateCommand {
let mut version_selector = self.create_version_selector(composer)?;
for package in &installed_packages {
if let Some(filter) = &filter {
- if !Preg::is_match(filter, package.get_name()).unwrap_or(false) {
+ if !Preg::is_match(filter, &package.get_name()).unwrap_or(false) {
continue;
}
}
@@ -525,7 +523,7 @@ impl UpdateCommand {
// TODO(phase-b): derive from stabilityFlags / minimum_stability
let stability: &str = "stable";
let latest_version = version_selector.find_best_candidate(
- package.get_name(),
+ &package.get_name(),
constraint,
stability,
None,
@@ -538,7 +536,7 @@ impl UpdateCommand {
if let Some(latest) = latest_version {
if package.get_version() != latest.get_version() || latest.is_dev() {
autocompleter_values.insert(
- package.get_name().to_string(),
+ package.get_name(),
format!(
"<comment>{}</comment> => <comment>{}</comment>",
current_version,
@@ -622,7 +620,7 @@ impl UpdateCommand {
fn create_version_selector(&self, composer: &PartialComposerHandle) -> Result<VersionSelector> {
let composer = crate::command::composer_full(composer);
let mut repository_set = RepositorySet::new(
- composer.get_package().get_minimum_stability(),
+ &composer.get_package().get_minimum_stability(),
composer.get_package().get_stability_flags().clone(),
// TODO(phase-b): collect root aliases from composer.get_package().get_aliases()
Vec::new(),
diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs
index 60514a1..814f09f 100644
--- a/crates/shirabe/src/command/validate_command.rs
+++ b/crates/shirabe/src/command/validate_command.rs
@@ -216,7 +216,7 @@ impl ValidateCommand {
let path = composer
.get_installation_manager()
.borrow_mut()
- .get_install_path(package.as_ref());
+ .get_install_path(package.as_rc().borrow().as_package_interface());
let path = match path {
Some(p) => p,
None => continue,
@@ -229,7 +229,7 @@ impl ValidateCommand {
self.output_result(
io,
- package.get_pretty_name(),
+ &package.get_pretty_name(),
&mut dep_errors,
&mut dep_warnings,
check_publish,
diff --git a/crates/shirabe/src/composer.rs b/crates/shirabe/src/composer.rs
index 2da4639..fe700cf 100644
--- a/crates/shirabe/src/composer.rs
+++ b/crates/shirabe/src/composer.rs
@@ -9,7 +9,7 @@ use crate::downloader::DownloadManager;
use crate::event_dispatcher::EventDispatcher;
use crate::installer::InstallationManager;
use crate::package::archiver::ArchiveManager;
-use crate::package::{Locker, RootPackageInterface};
+use crate::package::{Locker, RootPackageInterfaceHandle};
use crate::plugin::PluginManager;
use crate::repository::RepositoryManager;
use crate::util::r#loop::Loop;
@@ -35,7 +35,7 @@ pub fn get_version() -> String {
#[derive(Debug, Default)]
pub struct PartialComposer {
global: bool,
- package: Option<Box<dyn RootPackageInterface>>,
+ package: Option<RootPackageInterfaceHandle>,
r#loop: Option<std::rc::Rc<std::cell::RefCell<Loop>>>,
repository_manager: Option<std::rc::Rc<std::cell::RefCell<RepositoryManager>>>,
installation_manager: Option<std::rc::Rc<std::cell::RefCell<InstallationManager>>>,
@@ -44,12 +44,12 @@ pub struct PartialComposer {
}
impl PartialComposer {
- pub fn set_package(&mut self, package: Box<dyn RootPackageInterface>) {
+ pub fn set_package(&mut self, package: RootPackageInterfaceHandle) {
self.package = Some(package);
}
- pub fn get_package(&self) -> &dyn RootPackageInterface {
- self.package.as_deref().unwrap()
+ pub fn get_package(&self) -> &RootPackageInterfaceHandle {
+ self.package.as_ref().unwrap()
}
pub fn set_config(&mut self, config: std::rc::Rc<std::cell::RefCell<Config>>) {
@@ -193,11 +193,11 @@ impl Composer {
&mut self.partial
}
- pub fn set_package(&mut self, package: Box<dyn crate::package::RootPackageInterface>) {
+ pub fn set_package(&mut self, package: RootPackageInterfaceHandle) {
self.partial.set_package(package);
}
- pub fn get_package(&self) -> &dyn crate::package::RootPackageInterface {
+ pub fn get_package(&self) -> &RootPackageInterfaceHandle {
self.partial.get_package()
}
@@ -316,14 +316,14 @@ impl PartialOrFullComposer {
}
}
- pub fn set_package(&mut self, package: Box<dyn crate::package::RootPackageInterface>) {
+ pub fn set_package(&mut self, package: RootPackageInterfaceHandle) {
match self {
Self::Full(full) => full.set_package(package),
Self::Partial(partial) => partial.set_package(package),
}
}
- pub fn get_package(&self) -> &dyn crate::package::RootPackageInterface {
+ pub fn get_package(&self) -> &RootPackageInterfaceHandle {
match self {
Self::Full(full) => full.get_package(),
Self::Partial(partial) => partial.get_package(),
diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs
index 4bf9197..0b04e26 100644
--- a/crates/shirabe/src/console/application.rs
+++ b/crates/shirabe/src/console/application.rs
@@ -639,14 +639,18 @@ impl Application {
// TODO(phase-b): build_package_map needs &mut InstallationManager
// but get_composer returns &Composer; skip until shared ownership is settled.
let package_map: Vec<(
- Box<dyn crate::package::PackageInterface>,
+ crate::package::PackageInterfaceHandle,
Option<String>,
)> = todo!(
"build_package_map requires &mut InstallationManager"
);
let map = generator.parse_autoloads(
package_map,
- &*root_package,
+ root_package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
PhpMixed::Bool(false),
);
diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs
index cc33b81..0c6b2fc 100644
--- a/crates/shirabe/src/dependency_resolver/default_policy.rs
+++ b/crates/shirabe/src/dependency_resolver/default_policy.rs
@@ -1,6 +1,5 @@
//! ref: composer/src/Composer/DependencyResolver/DefaultPolicy.php
-use std::any::Any;
use std::cell::RefCell;
use indexmap::IndexMap;
@@ -10,9 +9,8 @@ use shirabe_semver::constraint::SimpleConstraint;
use crate::dependency_resolver::PolicyInterface;
use crate::dependency_resolver::Pool;
-use crate::package::AliasPackage;
-use crate::package::PackageInterface;
-use crate::package::{BasePackage, STABILITIES};
+use crate::package::BasePackageHandle;
+use crate::package::STABILITIES;
use crate::util::Platform;
#[derive(Debug)]
@@ -46,14 +44,14 @@ impl DefaultPolicy {
pub fn compare_by_priority(
&self,
pool: &Pool,
- a: &dyn BasePackage,
- b: &dyn BasePackage,
+ a: &BasePackageHandle,
+ b: &BasePackageHandle,
required_package: Option<String>,
ignore_replace: bool,
) -> i64 {
- if PackageInterface::get_name(a) == PackageInterface::get_name(b) {
- let a_aliased = a.as_any().downcast_ref::<AliasPackage>().is_some();
- let b_aliased = b.as_any().downcast_ref::<AliasPackage>().is_some();
+ if a.get_name() == b.get_name() {
+ let a_aliased = a.as_alias().is_some();
+ let b_aliased = b.as_alias().is_some();
if a_aliased && !b_aliased {
return -1;
}
@@ -73,10 +71,8 @@ impl DefaultPolicy {
if let Some(ref required_package) = required_package {
if let Some(pos) = required_package.find('/') {
let required_vendor = &required_package[..pos];
- let a_is_same_vendor =
- PackageInterface::get_name(a).starts_with(required_vendor);
- let b_is_same_vendor =
- PackageInterface::get_name(b).starts_with(required_vendor);
+ let a_is_same_vendor = a.get_name().starts_with(required_vendor);
+ let b_is_same_vendor = b.get_name().starts_with(required_vendor);
if b_is_same_vendor != a_is_same_vendor {
return if a_is_same_vendor { -1 } else { 1 };
}
@@ -112,7 +108,7 @@ impl DefaultPolicy {
.iter()
.copied()
.filter(|&literal| {
- pool.literal_to_package(literal).get_version() == preferred_version
+ pool.literal_to_package(literal).get_version() == *preferred_version
})
.collect();
if !best_literals.is_empty() {
@@ -129,10 +125,10 @@ impl DefaultPolicy {
continue;
}
let package = pool.literal_to_package(literal);
- if self.version_compare(package, best_package, operator) {
+ if self.version_compare(&package, &best_package, operator) {
best_package = package;
best_literals = vec![literal];
- } else if self.version_compare(package, best_package, "==") {
+ } else if self.version_compare(&package, &best_package, "==") {
best_literals.push(literal);
}
}
@@ -144,7 +140,7 @@ impl DefaultPolicy {
for &literal in &literals {
let package = pool.literal_to_package(literal);
- if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_alias() {
if alias_pkg.is_root_package_alias() {
has_local_alias = true;
break;
@@ -159,7 +155,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().downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_alias() {
if alias_pkg.is_root_package_alias() {
selected.push(literal);
}
@@ -168,9 +164,9 @@ impl DefaultPolicy {
selected
}
- pub(crate) fn replaces(&self, source: &dyn BasePackage, target: &dyn BasePackage) -> bool {
+ pub(crate) fn replaces(&self, source: &BasePackageHandle, target: &BasePackageHandle) -> bool {
for link in source.get_replaces().values() {
- if link.get_target() == target.get_name() {
+ if link.get_target() == target.get_name().as_str() {
return true;
}
}
@@ -181,8 +177,8 @@ impl DefaultPolicy {
impl PolicyInterface for DefaultPolicy {
fn version_compare(
&self,
- a: &dyn PackageInterface,
- b: &dyn PackageInterface,
+ a: &BasePackageHandle,
+ b: &BasePackageHandle,
operator: &str,
) -> bool {
if self.prefer_stable {
@@ -267,8 +263,8 @@ impl PolicyInterface for DefaultPolicy {
}
let result = self.compare_by_priority(
pool,
- pool.literal_to_package(a),
- pool.literal_to_package(b),
+ &pool.literal_to_package(a),
+ &pool.literal_to_package(b),
required_package.clone(),
true,
);
@@ -300,8 +296,8 @@ impl PolicyInterface for DefaultPolicy {
}
let result = self.compare_by_priority(
pool,
- pool.literal_to_package(a),
- pool.literal_to_package(b),
+ &pool.literal_to_package(a),
+ &pool.literal_to_package(b),
required_package.clone(),
false,
);
diff --git a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
index b4aa4a4..7b60522 100644
--- a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
@@ -14,9 +14,9 @@ impl LocalRepoTransaction {
locked_repository: &dyn RepositoryInterface,
local_repository: &dyn InstalledRepositoryInterface,
) -> Self {
- // TODO(phase-b): RepositoryInterface::get_packages returns Box<dyn BasePackage>
- // but Transaction::new wants Box<dyn PackageInterface>. Upcast each via PackageInterface
- // trait once a `into_package_interface` helper is added.
+ // TODO(phase-c): RepositoryInterface::get_packages yields BasePackageHandle; widen each to
+ // PackageInterfaceHandle (via .into()) and feed them to Transaction::new once the repository
+ // getters expose handles here.
let _ = (locked_repository, local_repository);
Self {
inner: Transaction::new(Vec::new(), Vec::new()),
diff --git a/crates/shirabe/src/dependency_resolver/lock_transaction.rs b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
index 14900d1..09d4571 100644
--- a/crates/shirabe/src/dependency_resolver/lock_transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
@@ -1,34 +1,30 @@
//! ref: composer/src/Composer/DependencyResolver/LockTransaction.php
-use std::any::Any;
-
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::Preg;
use crate::dependency_resolver::Decisions;
use crate::dependency_resolver::Pool;
use crate::dependency_resolver::Transaction;
-use crate::package::AliasPackage;
-use crate::package::Package;
-use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
#[derive(Debug)]
pub struct LockTransaction {
inner: Transaction,
/// packages in current lock file, platform repo or otherwise present
/// Indexed by spl_object_hash
- pub(crate) present_map: IndexMap<String, Box<dyn PackageInterface>>,
+ pub(crate) present_map: IndexMap<String, PackageInterfaceHandle>,
/// Packages which cannot be mapped, platform repo, root package, other fixed repos
/// Indexed by package id
- pub(crate) unlockable_map: IndexMap<i64, Box<dyn PackageInterface>>,
- pub(crate) result_packages: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
+ pub(crate) unlockable_map: IndexMap<i64, PackageInterfaceHandle>,
+ pub(crate) result_packages: IndexMap<String, Vec<PackageInterfaceHandle>>,
}
impl LockTransaction {
pub fn new(
pool: &Pool,
- present_map: IndexMap<String, Box<dyn PackageInterface>>,
- unlockable_map: IndexMap<i64, Box<dyn PackageInterface>>,
+ present_map: IndexMap<String, PackageInterfaceHandle>,
+ unlockable_map: IndexMap<i64, PackageInterfaceHandle>,
decisions: &Decisions,
) -> Self {
let mut this = Self {
@@ -38,22 +34,18 @@ impl LockTransaction {
result_packages: IndexMap::new(),
};
this.set_result_packages(pool, decisions);
- let all: Vec<Box<dyn PackageInterface>> = this
+ let all: Vec<PackageInterfaceHandle> = this
.result_packages
.get("all")
- .map(|v| v.iter().map(|p| p.clone_package_box()).collect())
+ .map(|v| v.iter().cloned().collect())
.unwrap_or_default();
- let present: Vec<Box<dyn PackageInterface>> = this
- .present_map
- .values()
- .map(|p| p.clone_package_box())
- .collect();
+ let present: Vec<PackageInterfaceHandle> = this.present_map.values().cloned().collect();
this.inner = Transaction::new(present, all);
this
}
pub fn set_result_packages(&mut self, pool: &Pool, decisions: &Decisions) {
- let mut result_packages: IndexMap<String, Vec<Box<dyn PackageInterface>>> = IndexMap::new();
+ let mut result_packages: IndexMap<String, Vec<PackageInterfaceHandle>> = IndexMap::new();
result_packages.insert("all".to_string(), vec![]);
result_packages.insert("non-dev".to_string(), vec![]);
result_packages.insert("dev".to_string(), vec![]);
@@ -67,12 +59,12 @@ impl LockTransaction {
result_packages
.get_mut("all")
.unwrap()
- .push(package.clone_box());
+ .push(package.clone().into());
if !self.unlockable_map.contains_key(&package.get_id()) {
result_packages
.get_mut("non-dev")
.unwrap()
- .push(package.clone_box());
+ .push(package.clone().into());
}
}
}
@@ -110,7 +102,7 @@ impl LockTransaction {
&self,
dev_mode: bool,
update_mirrors: bool,
- ) -> Vec<Box<dyn PackageInterface>> {
+ ) -> Vec<PackageInterfaceHandle> {
let key = if dev_mode { "dev" } else { "non-dev" };
let mut packages = vec![];
@@ -120,26 +112,22 @@ impl LockTransaction {
.map(|v| v.as_slice())
.unwrap_or_default();
for package in source {
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().is_some() {
continue;
}
- if update_mirrors
- && !self
- .present_map
- .contains_key(&shirabe_php_shim::spl_object_hash(package.as_ref()))
- {
- let updated = self.update_mirror_and_urls(package.as_ref());
+ if update_mirrors && !self.present_map.contains_key(&package.ptr_id().to_string()) {
+ let updated = self.update_mirror_and_urls(package);
packages.push(updated);
} else {
- packages.push(package.clone_package_box());
+ packages.push(package.clone());
}
}
packages
}
- fn update_mirror_and_urls(&self, package: &dyn PackageInterface) -> Box<dyn PackageInterface> {
+ fn update_mirror_and_urls(&self, package: &PackageInterfaceHandle) -> PackageInterfaceHandle {
for present_package in self.present_map.values() {
if package.get_name() != present_package.get_name() {
continue;
@@ -157,38 +145,38 @@ impl LockTransaction {
continue;
}
- if let Some(concrete_pkg) = present_package.as_any().downcast_ref::<Package>() {
- // TODO(phase-b): set_source_url/set_source_mirrors expect &mut and owned types;
- // present_package is &Box<dyn PackageInterface> (immutable). Revisit ownership.
+ if let Some(concrete_pkg) = present_package.as_package() {
+ // TODO(phase-c): mirror the source url/mirrors of the present package onto it via
+ // its handle setters once the per-field copy semantics are reviewed.
let _ = concrete_pkg;
- let _ = package.get_source_url().map(|s| s.to_string());
+ let _ = package.get_source_url();
let _ = package.get_source_mirrors();
}
if present_package.get_dist_type() != package.get_dist_type() {
- return present_package.clone_package_box();
+ return present_package.clone();
}
if package.get_dist_url().is_some()
&& present_package.get_dist_reference().is_some()
- && Preg::is_match(r"{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i", package.get_dist_url().unwrap()).unwrap_or(false)
+ && Preg::is_match(r"{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i", &package.get_dist_url().unwrap()).unwrap_or(false)
{
let new_dist_url = Preg::replace(
r"{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i",
- present_package.get_dist_reference().unwrap(),
- package.get_dist_url().unwrap(),
+ &present_package.get_dist_reference().unwrap(),
+ &package.get_dist_url().unwrap(),
)
- .unwrap_or_else(|_| package.get_dist_url().unwrap().to_string());
- // TODO(phase-b): set_dist_url requires &mut PackageInterface; revisit ownership.
+ .unwrap_or_else(|_| package.get_dist_url().unwrap());
+ // TODO(phase-c): apply new_dist_url onto present_package via its handle setter.
let _ = new_dist_url;
}
- // TODO(phase-b): set_dist_mirrors requires &mut PackageInterface; revisit ownership.
+ // TODO(phase-c): apply dist mirrors onto present_package via its handle setter.
let _ = package.get_dist_mirrors();
- return present_package.clone_package_box();
+ return present_package.clone();
}
- package.clone_package_box()
+ package.clone()
}
pub fn get_aliases(
@@ -200,11 +188,11 @@ impl LockTransaction {
if let Some(all_packages) = self.result_packages.get("all") {
for package in all_packages {
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().is_some() {
let mut i = 0;
while i < remaining_aliases.len() {
if remaining_aliases[i].get("package").map(|s| s.as_str())
- == Some(package.get_name())
+ == Some(package.get_name().as_str())
{
used_aliases.push(remaining_aliases.remove(i));
} else {
diff --git a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
index ee5e84d..466c877 100644
--- a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
@@ -3,19 +3,20 @@
use crate::dependency_resolver::operation::OperationInterface;
use crate::dependency_resolver::operation::SolverOperation;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
#[derive(Debug)]
pub struct InstallOperation {
- pub(crate) package: Box<dyn PackageInterface>,
+ pub(crate) package: PackageInterfaceHandle,
}
impl InstallOperation {
- pub fn new(package: Box<dyn PackageInterface>) -> Self {
+ pub fn new(package: PackageInterfaceHandle) -> Self {
Self { package }
}
- pub fn get_package(&self) -> &dyn PackageInterface {
- self.package.as_ref()
+ pub fn get_package(&self) -> &PackageInterfaceHandle {
+ &self.package
}
pub fn format(package: &dyn PackageInterface, lock: bool) -> String {
@@ -43,7 +44,7 @@ impl OperationInterface for InstallOperation {
}
fn show(&self, lock: bool) -> String {
- Self::format(self.package.as_ref(), lock)
+ Self::format(self.package.as_rc().borrow().as_package_interface(), lock)
}
fn to_string(&self) -> String {
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 e176b7f..339f86c 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
@@ -42,12 +42,10 @@ impl OperationInterface for MarkAliasInstalledOperation {
true,
<dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,
),
- PackageInterface::get_pretty_name(self.package.get_alias_of()),
- PackageInterface::get_full_pretty_version(
- self.package.get_alias_of(),
- true,
- <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,
- ),
+ self.package.get_alias_of().get_pretty_name(),
+ self.package
+ .get_alias_of()
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,),
)
}
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 141bf4a..a5b7b7d 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
@@ -42,12 +42,10 @@ impl OperationInterface for MarkAliasUninstalledOperation {
true,
<dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,
),
- PackageInterface::get_pretty_name(self.package.get_alias_of()),
- PackageInterface::get_full_pretty_version(
- self.package.get_alias_of(),
- true,
- <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,
- ),
+ self.package.get_alias_of().get_pretty_name(),
+ self.package
+ .get_alias_of()
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV,),
)
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
index 5e1f6bc..2757146 100644
--- a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
@@ -3,19 +3,20 @@
use crate::dependency_resolver::operation::OperationInterface;
use crate::dependency_resolver::operation::SolverOperation;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
#[derive(Debug)]
pub struct UninstallOperation {
- pub(crate) package: Box<dyn PackageInterface>,
+ pub(crate) package: PackageInterfaceHandle,
}
impl UninstallOperation {
- pub fn new(package: Box<dyn PackageInterface>) -> Self {
+ pub fn new(package: PackageInterfaceHandle) -> Self {
Self { package }
}
- pub fn get_package(&self) -> &dyn PackageInterface {
- self.package.as_ref()
+ pub fn get_package(&self) -> &PackageInterfaceHandle {
+ &self.package
}
pub fn format(package: &dyn PackageInterface, _lock: bool) -> String {
@@ -42,7 +43,7 @@ impl OperationInterface for UninstallOperation {
}
fn show(&self, lock: bool) -> String {
- Self::format(self.package.as_ref(), lock)
+ Self::format(self.package.as_rc().borrow().as_package_interface(), lock)
}
fn to_string(&self) -> String {
diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
index 2ce103b..6881782 100644
--- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
@@ -3,28 +3,29 @@
use crate::dependency_resolver::operation::OperationInterface;
use crate::dependency_resolver::operation::SolverOperation;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::version::VersionParser;
#[derive(Debug)]
pub struct UpdateOperation {
- pub(crate) initial_package: Box<dyn PackageInterface>,
- pub(crate) target_package: Box<dyn PackageInterface>,
+ pub(crate) initial_package: PackageInterfaceHandle,
+ pub(crate) target_package: PackageInterfaceHandle,
}
impl UpdateOperation {
- pub fn new(initial: Box<dyn PackageInterface>, target: Box<dyn PackageInterface>) -> Self {
+ pub fn new(initial: PackageInterfaceHandle, target: PackageInterfaceHandle) -> Self {
Self {
initial_package: initial,
target_package: target,
}
}
- pub fn get_initial_package(&self) -> &dyn PackageInterface {
- self.initial_package.as_ref()
+ pub fn get_initial_package(&self) -> &PackageInterfaceHandle {
+ &self.initial_package
}
- pub fn get_target_package(&self) -> &dyn PackageInterface {
- self.target_package.as_ref()
+ pub fn get_target_package(&self) -> &PackageInterfaceHandle {
+ &self.target_package
}
pub fn format(
@@ -89,8 +90,8 @@ impl OperationInterface for UpdateOperation {
fn show(&self, lock: bool) -> String {
Self::format(
- self.initial_package.as_ref(),
- self.target_package.as_ref(),
+ self.initial_package.as_rc().borrow().as_package_interface(),
+ self.target_package.as_rc().borrow().as_package_interface(),
lock,
)
}
diff --git a/crates/shirabe/src/dependency_resolver/policy_interface.rs b/crates/shirabe/src/dependency_resolver/policy_interface.rs
index 148e21f..002bfe9 100644
--- a/crates/shirabe/src/dependency_resolver/policy_interface.rs
+++ b/crates/shirabe/src/dependency_resolver/policy_interface.rs
@@ -1,15 +1,11 @@
//! ref: composer/src/Composer/DependencyResolver/PolicyInterface.php
use crate::dependency_resolver::Pool;
-use crate::package::PackageInterface;
+use crate::package::BasePackageHandle;
pub trait PolicyInterface: std::fmt::Debug {
- fn version_compare(
- &self,
- a: &dyn PackageInterface,
- b: &dyn PackageInterface,
- operator: &str,
- ) -> bool;
+ fn version_compare(&self, a: &BasePackageHandle, b: &BasePackageHandle, operator: &str)
+ -> bool;
fn select_preferred_packages(
&self,
diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs
index 771f363..388f23f 100644
--- a/crates/shirabe/src/dependency_resolver/pool.rs
+++ b/crates/shirabe/src/dependency_resolver/pool.rs
@@ -3,28 +3,29 @@
use std::fmt;
use indexmap::IndexMap;
-use shirabe_php_shim::{Countable, STR_PAD_LEFT, abs, spl_object_hash, str_pad};
+use shirabe_php_shim::{Countable, STR_PAD_LEFT, abs, str_pad};
use shirabe_semver::compiling_matcher::CompilingMatcher;
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::SimpleConstraint;
use crate::advisory::PartialSecurityAdvisory;
use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::version::VersionParser;
/// A package pool contains all packages for dependency resolution
#[derive(Debug)]
pub struct Pool {
/// @var BasePackage[]
- pub(crate) packages: Vec<Box<dyn BasePackage>>,
+ pub(crate) packages: Vec<BasePackageHandle>,
/// @var array<string, BasePackage[]>
- pub(crate) package_by_name: IndexMap<String, Vec<Box<dyn BasePackage>>>,
+ pub(crate) package_by_name: IndexMap<String, Vec<BasePackageHandle>>,
/// @var VersionParser
pub(crate) version_parser: VersionParser,
/// @var array<string, array<string, BasePackage[]>>
- pub(crate) provider_cache: IndexMap<String, IndexMap<String, Vec<Box<dyn BasePackage>>>>,
+ pub(crate) provider_cache: IndexMap<String, IndexMap<String, Vec<BasePackageHandle>>>,
/// @var BasePackage[]
- pub(crate) unacceptable_fixed_or_locked_packages: Vec<Box<dyn BasePackage>>,
+ pub(crate) unacceptable_fixed_or_locked_packages: Vec<BasePackageHandle>,
/// @var array<string, array<string, string>> Map of package name => normalized version => pretty version
pub(crate) removed_versions: IndexMap<String, IndexMap<String, String>>,
/// @var array<string, array<string, string>> Map of package object hash => removed normalized versions => removed pretty version
@@ -44,8 +45,8 @@ impl Pool {
/// @param array<string, array<string, array<SecurityAdvisory|PartialSecurityAdvisory>>> $securityRemovedVersions
/// @param array<string, array<string, string>> $abandonedRemovedVersions
pub fn new(
- packages: Vec<Box<dyn BasePackage>>,
- unacceptable_fixed_or_locked_packages: Vec<Box<dyn BasePackage>>,
+ packages: Vec<BasePackageHandle>,
+ unacceptable_fixed_or_locked_packages: Vec<BasePackageHandle>,
removed_versions: IndexMap<String, IndexMap<String, String>>,
removed_versions_by_package: IndexMap<String, IndexMap<String, String>>,
security_removed_versions: IndexMap<String, IndexMap<String, Vec<PartialSecurityAdvisory>>>,
@@ -197,18 +198,18 @@ impl Pool {
}
/// @param BasePackage[] $packages
- fn set_packages(&mut self, packages: Vec<Box<dyn BasePackage>>) {
+ fn set_packages(&mut self, packages: Vec<BasePackageHandle>) {
let mut id: i64 = 1;
- for mut package in packages {
- *package.id_mut() = id;
+ for package in packages {
+ package.set_id(id);
id += 1;
for provided in package.get_names(true) {
self.package_by_name
.entry(provided)
.or_insert_with(Vec::new)
- .push(package.clone_box());
+ .push(package.clone());
}
self.packages.push(package);
@@ -216,13 +217,13 @@ impl Pool {
}
/// @return BasePackage[]
- pub fn get_packages(&self) -> &Vec<Box<dyn BasePackage>> {
+ pub fn get_packages(&self) -> &Vec<BasePackageHandle> {
&self.packages
}
/// Retrieves the package object for a given package id.
- pub fn package_by_id(&self, id: i64) -> &dyn BasePackage {
- self.packages[(id - 1) as usize].as_ref()
+ pub fn package_by_id(&self, id: i64) -> BasePackageHandle {
+ self.packages[(id - 1) as usize].clone()
}
/// Searches all packages providing the given package name and match the constraint
@@ -235,7 +236,7 @@ impl Pool {
&mut self,
name: &str,
constraint: Option<&AnyConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
// PHP: $key = (string) $constraint;
let key = match constraint {
Some(c) => c.to_string(),
@@ -243,7 +244,7 @@ impl Pool {
};
if let Some(by_key) = self.provider_cache.get(name) {
if let Some(cached) = by_key.get(&key) {
- return cached.iter().map(|p| p.clone_box()).collect();
+ return cached.clone();
}
}
@@ -251,7 +252,7 @@ impl Pool {
self.provider_cache
.entry(name.to_string())
.or_insert_with(IndexMap::new)
- .insert(key, computed.iter().map(|p| p.clone_box()).collect());
+ .insert(key, computed.clone());
computed
}
@@ -263,23 +264,23 @@ impl Pool {
&self,
name: &str,
constraint: Option<&AnyConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
let Some(candidates) = self.package_by_name.get(name) else {
return vec![];
};
- let mut matches: Vec<Box<dyn BasePackage>> = vec![];
+ let mut matches: Vec<BasePackageHandle> = vec![];
for candidate in candidates {
- if self.r#match(candidate.as_ref(), name, constraint) {
- matches.push(candidate.clone_box());
+ if self.r#match(candidate, name, constraint) {
+ matches.push(candidate.clone());
}
}
matches
}
- pub fn literal_to_package(&self, literal: i64) -> &dyn BasePackage {
+ pub fn literal_to_package(&self, literal: i64) -> BasePackageHandle {
let package_id = abs(literal);
self.package_by_id(package_id)
@@ -289,7 +290,7 @@ impl Pool {
pub fn literal_to_pretty_string(
&self,
literal: i64,
- installed_map: &IndexMap<String, Box<dyn BasePackage>>,
+ installed_map: &IndexMap<String, BasePackageHandle>,
) -> String {
let package = self.literal_to_package(literal);
@@ -312,7 +313,7 @@ impl Pool {
/// @param string $name Name of the package to be matched
pub fn r#match(
&self,
- candidate: &dyn BasePackage,
+ candidate: &BasePackageHandle,
name: &str,
constraint: Option<&AnyConstraint>,
) -> bool {
@@ -370,17 +371,16 @@ impl Pool {
false
}
- pub fn is_unacceptable_fixed_or_locked_package(&self, package: &dyn BasePackage) -> bool {
+ pub fn is_unacceptable_fixed_or_locked_package(&self, package: &BasePackageHandle) -> bool {
// PHP: \in_array($package, $this->unacceptableFixedOrLockedPackages, true)
// strict comparison checks reference identity for objects
- let target_hash = spl_object_hash(package);
self.unacceptable_fixed_or_locked_packages
.iter()
- .any(|p| spl_object_hash(p.as_ref()) == target_hash)
+ .any(|p| p.ptr_eq(package))
}
/// @return BasePackage[]
- pub fn get_unacceptable_fixed_or_locked_packages(&self) -> &Vec<Box<dyn BasePackage>> {
+ pub fn get_unacceptable_fixed_or_locked_packages(&self) -> &Vec<BasePackageHandle> {
&self.unacceptable_fixed_or_locked_packages
}
}
diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs
index f80ef38..38a083e 100644
--- a/crates/shirabe/src/dependency_resolver/pool_builder.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs
@@ -8,8 +8,8 @@ use shirabe_external_packages::composer::semver::CompilingMatcher;
use shirabe_external_packages::composer::semver::Intervals;
use shirabe_php_shim::{
LogicException, PhpMixed, array_chunk, array_flip, array_flip_strings, array_map, array_merge,
- array_search, array_search_mixed, count, in_array, microtime, number_format, round,
- spl_object_hash, sprintf, strpos,
+ array_search, array_search_mixed, count, in_array, microtime, number_format, round, sprintf,
+ strpos,
};
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::MatchAllConstraint;
@@ -23,10 +23,12 @@ use crate::dependency_resolver::SecurityAdvisoryPoolFilter;
use crate::event_dispatcher::EventDispatcher;
use crate::io::IOInterface;
use crate::package::AliasPackage;
+use crate::package::BasePackageHandle;
use crate::package::CompleteAliasPackage;
use crate::package::CompletePackage;
use crate::package::PackageInterface;
-use crate::package::base_package::{self, BasePackage};
+use crate::package::PackageInterfaceHandle;
+use crate::package::base_package;
use crate::package::version::StabilityFilter;
use crate::plugin::PluginEvents;
use crate::plugin::PrePoolCreateEvent;
@@ -48,11 +50,11 @@ pub struct PoolBuilder {
alias_map: IndexMap<String, IndexMap<i64, AliasPackage>>,
packages_to_load: IndexMap<String, AnyConstraint>,
loaded_packages: IndexMap<String, AnyConstraint>,
- loaded_per_repo: IndexMap<i64, IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>>,
- packages: IndexMap<i64, Box<dyn BasePackage>>,
- unacceptable_fixed_or_locked_packages: Vec<Box<dyn BasePackage>>,
+ loaded_per_repo: IndexMap<i64, IndexMap<String, IndexMap<String, PackageInterfaceHandle>>>,
+ packages: IndexMap<i64, BasePackageHandle>,
+ unacceptable_fixed_or_locked_packages: Vec<BasePackageHandle>,
update_allow_list: Vec<String>,
- skipped_load: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
+ skipped_load: IndexMap<String, Vec<PackageInterfaceHandle>>,
ignored_types: Vec<String>,
allowed_types: Option<Vec<String>>,
/// If provided, only these package names are loaded
@@ -161,10 +163,7 @@ impl PoolBuilder {
for locked_package in
CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap())
{
- if !self.is_update_allowed(&*locked_package) {
- // TODO(phase-b): PackageInterface lacks clone_box; PHP shares references.
- // skipped_load population needs shared-ownership Rc<dyn PackageInterface>.
-
+ if !self.is_update_allowed(locked_package.as_rc().borrow().as_package_interface()) {
// 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
// regular packages. We mark them specially so they can be reloaded fully including update propagation
@@ -182,10 +181,7 @@ impl PoolBuilder {
}
}
- // TODO(phase-b): lock_package wants Box<dyn BasePackage>; locked_package is a
- // PackageInterface trait object from CanonicalPackagesTrait::get_packages. The
- // PHP code passes the same object; needs Rc<dyn BasePackage> migration.
- request.lock_package(todo!("convert PackageInterface → Box<dyn BasePackage>"));
+ request.lock_package(locked_package.into());
}
}
}
@@ -209,9 +205,9 @@ impl PoolBuilder {
// TODO in how far can we do the above for conflicts? It's more tricky cause conflicts can be limited to
// specific versions while replace is a conflict with all versions of the name
- let in_root_or_platform = package
- .get_repository()
- .map(|r| {
+ // TODO(phase-c): package->repository back-reference not yet on handles
+ let in_root_or_platform = None
+ .map(|r: &dyn RepositoryInterface| {
r.as_any().is::<RootPackageRepository>()
|| r.as_any().is::<PlatformRepository>()
})
@@ -221,13 +217,12 @@ impl PoolBuilder {
&self.acceptable_stabilities,
&self.stability_flags,
&package.get_names(true),
- package.get_stability(),
+ &package.get_stability(),
)
{
- self.load_package(request, &repositories, &*package, false)?;
+ self.load_package(request, &repositories, &package, false)?;
} else {
- self.unacceptable_fixed_or_locked_packages
- .push(package.clone_box());
+ self.unacceptable_fixed_or_locked_packages.push(package);
}
}
@@ -261,11 +256,11 @@ impl PoolBuilder {
let indices: Vec<i64> = self.packages.keys().cloned().collect();
for i in indices {
let package = match self.packages.get(&i) {
- Some(p) => p.clone_box(),
+ Some(p) => p.clone(),
None => continue,
};
// we check all alias related packages at once, so no need to check individual aliases
- if package.as_alias_package().is_some() {
+ if package.as_alias().is_some() {
continue;
}
@@ -275,12 +270,11 @@ impl PoolBuilder {
None => continue,
};
- // TODO(phase-b): package_and_aliases originally held Box<dyn BasePackage>;
- // AliasPackage is a PHP class so we collect (index, version) tuples instead of
- // cloning the alias objects.
+ // TODO(phase-c): alias_map still stores AliasPackage by value, so we collect
+ // (index, version) tuples instead of the alias handles.
let mut package_and_aliases: Vec<(i64, String)> = Vec::new();
package_and_aliases.push((i, package.get_version().to_string()));
- if let Some(aliases) = self.alias_map.get(&spl_object_hash(&*package)) {
+ if let Some(aliases) = self.alias_map.get(&package.ptr_id().to_string()) {
for (idx, alias) in aliases {
package_and_aliases.push((*idx, alias.get_version().to_string()));
}
@@ -314,10 +308,10 @@ impl PoolBuilder {
self.stability_flags.clone(),
self.root_aliases.clone(),
self.root_references.clone(),
- self.packages.values().map(|p| p.clone_box()).collect(),
+ self.packages.values().cloned().collect(),
self.unacceptable_fixed_or_locked_packages
.iter()
- .map(|p| p.clone_box())
+ .cloned()
.collect(),
);
// TODO(phase-b): EventDispatcher::dispatch expects an owned Event, not &mut PrePoolCreateEvent
@@ -327,24 +321,16 @@ impl PoolBuilder {
.borrow_mut()
.dispatch(Some(pre_pool_create_event.get_name()), None)?;
// PHP rebinds $this->packages to a list-style array; preserve indices via reindexing.
- self.packages = pre_pool_create_event
- .get_packages()
- .iter()
- .enumerate()
- .map(|(i, p)| (i as i64, p.clone_box()))
- .collect();
- self.unacceptable_fixed_or_locked_packages = pre_pool_create_event
- .get_unacceptable_fixed_packages()
- .iter()
- .map(|p| p.clone_box())
- .collect();
+ // TODO(plugin)/TODO(phase-c): rebind self.packages from the (handle-based) event packages
+ // once EventDispatcher::dispatch returns the mutated event.
+ let _ = &pre_pool_create_event;
}
let mut pool = Pool::new(
- self.packages.values().map(|p| p.clone_box()).collect(),
+ self.packages.values().cloned().collect(),
self.unacceptable_fixed_or_locked_packages
.iter()
- .map(|p| p.clone_box())
+ .cloned()
.collect(),
IndexMap::new(),
IndexMap::new(),
@@ -543,7 +529,7 @@ impl PoolBuilder {
k.clone(),
inner
.iter()
- .map(|(kk, vv)| (kk.clone(), vv.clone_package_box()))
+ .map(|(kk, vv)| (kk.clone(), vv.clone()))
.collect(),
)
})
@@ -561,8 +547,6 @@ impl PoolBuilder {
}
let packages_in_result = result.packages;
for (_, package) in &packages_in_result {
- // TODO(phase-b): proper upcast Box<dyn BasePackage> → Box<dyn PackageInterface>;
- // clone_box on BasePackage produces a BasePackage, while loaded_per_repo stores PackageInterface.
let pkg_name = package.get_name().to_string();
let pkg_version = package.get_version().to_string();
let pkg_type = package.get_type().to_string();
@@ -592,8 +576,8 @@ impl PoolBuilder {
continue;
}
let _ = (pkg_name, pkg_version);
- let propagate = !self.path_repo_unlocked.contains_key(package.get_name());
- self.load_package(request, repositories, package.as_ref(), propagate)?;
+ let propagate = !self.path_repo_unlocked.contains_key(&package.get_name());
+ self.load_package(request, repositories, package, propagate)?;
}
}
@@ -627,36 +611,33 @@ impl PoolBuilder {
&mut self,
request: &mut Request,
repositories: &Vec<Box<dyn RepositoryInterface>>,
- package: &dyn BasePackage,
+ package: &BasePackageHandle,
propagate_update: bool,
) -> anyhow::Result<()> {
let index = self.index_counter;
self.index_counter += 1;
- self.packages.insert(index, package.clone_box());
+ self.packages.insert(index, package.clone());
- if let Some(alias) = package.as_alias_package() {
+ if let Some(alias) = package.as_alias() {
// TODO(phase-b): alias_map should hold shared references (Rc<AliasPackage>); AliasPackage
// is a PHP class and must not be cloned.
- let _ = alias;
+ let _ = &alias;
self.alias_map
- .entry(spl_object_hash(alias.get_alias_of()))
+ .entry(alias.get_alias_of().ptr_id().to_string())
.or_insert_with(IndexMap::new)
.insert(index, todo!("share AliasPackage via Rc"));
}
- let name = PackageInterface::get_name(package).to_string();
+ let name = package.get_name();
// we're simply setting the root references on all versions for a name here and rely on the solver to pick the
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
// apply to
if let Some(reference) = self.root_references.get(&name) {
- // do not modify the references on already locked or fixed packages
- if !request.is_locked_package(package) && !request.is_fixed_package(package) {
- // TODO(phase-b): set_source_dist_references mutates the package; load_package takes
- // `&dyn BasePackage`. PHP passes by reference (shared) and mutates in place. Needs
- // either &mut dyn BasePackage propagation or Rc<RefCell<...>>.
- let _ = reference;
- }
+ // TODO(phase-c): apply root references to the package; PHP mutates the shared package in
+ // place and skips already locked/fixed packages, which needs &mut access through the
+ // handle plus a handle-based Request.
+ let _ = reference;
}
// if propagateUpdate is false we are loading a fixed or locked package, root aliases do not apply as they are
@@ -664,51 +645,39 @@ impl PoolBuilder {
//
// packages in pathRepoUnlocked however need to also load root aliases, they have propagateUpdate set to
// false because their deps should not be unlocked, but that is irrelevant for root aliases
- let path_repo_match = self
- .path_repo_unlocked
- .contains_key(PackageInterface::get_name(package));
+ let path_repo_match = self.path_repo_unlocked.contains_key(&package.get_name());
let alias_for_version = self
.root_aliases
.get(&name)
- .and_then(|m| m.get(package.get_version()))
+ .and_then(|m| m.get(&package.get_version()))
.cloned();
if (propagate_update || path_repo_match) && alias_for_version.is_some() {
let alias = alias_for_version.unwrap();
- let base_package: Box<dyn BasePackage> = if let Some(ap) = package.as_alias_package() {
- ap.get_alias_of().clone_box()
+ let base_package: BasePackageHandle = if let Some(ap) = package.as_alias() {
+ ap.get_alias_of().into()
} else {
- package.clone_box()
+ package.clone()
+ };
+ let _ = (&base_package, &alias);
+ let alias_package: BasePackageHandle = if base_package.as_complete_package().is_some() {
+ // TODO(phase-c): construct CompleteAliasPackage from the aliasOf handle.
+ todo!("new CompleteAliasPackage(base_package, alias_normalized, alias)")
+ } else {
+ // TODO(phase-c): construct AliasPackage from the aliasOf handle.
+ todo!("new AliasPackage(base_package, alias_normalized, alias)")
};
- let alias_package: Box<dyn BasePackage> =
- if base_package.as_any().is::<CompletePackage>() {
- // TODO(phase-b): CompleteAliasPackage does not yet impl BasePackage; also its
- // constructor wants CompletePackage by value but BasePackage is a PHP class
- // (shared). Needs Rc<CompletePackage> migration + BasePackage impl.
- let _ = CompleteAliasPackage::new(
- todo!("downcast Box<dyn BasePackage> to CompletePackage by value"),
- alias.get("alias_normalized").cloned().unwrap_or_default(),
- alias.get("alias").cloned().unwrap_or_default(),
- );
- todo!("CompleteAliasPackage must implement BasePackage")
- } else {
- Box::new(AliasPackage::new(
- base_package.clone_box(),
- alias.get("alias_normalized").cloned().unwrap_or_default(),
- alias.get("alias").cloned().unwrap_or_default(),
- ))
- };
// PHP: $aliasPackage->setRootPackageAlias(true);
// BasePackage doesn't expose this directly; the AliasPackage trait method handles it.
let new_index = self.index_counter;
self.index_counter += 1;
- self.packages.insert(new_index, alias_package.clone_box());
- if let Some(ap) = alias_package.as_alias_package() {
+ self.packages.insert(new_index, alias_package.clone());
+ if let Some(ap) = alias_package.as_alias() {
// TODO(phase-b): alias_map should hold shared references (Rc<AliasPackage>); AliasPackage
// is a PHP class and must not be cloned.
- let _ = ap;
+ let _ = &ap;
self.alias_map
- .entry(spl_object_hash(ap.get_alias_of()))
+ .entry(ap.get_alias_of().ptr_id().to_string())
.or_insert_with(IndexMap::new)
.insert(new_index, todo!("share AliasPackage via Rc"));
}
@@ -800,7 +769,7 @@ impl PoolBuilder {
if root_requires.contains_key(name) {
let name_owned = name.to_string();
return array_map(
- |package: &Box<dyn PackageInterface>| -> String {
+ |package: &PackageInterfaceHandle| -> String {
if name_owned != package.get_name() {
format!("{} (via replace of {})", package.get_name(), name_owned)
} else {
@@ -812,8 +781,8 @@ impl PoolBuilder {
}
for package_or_replacer in &self.skipped_load[name] {
- if root_requires.contains_key(package_or_replacer.get_name()) {
- matches.push(package_or_replacer.get_name().to_string());
+ if root_requires.contains_key(&package_or_replacer.get_name()) {
+ matches.push(package_or_replacer.get_name());
}
for (_k, link) in &package_or_replacer.get_replaces() {
if root_requires.contains_key(link.get_target()) {
@@ -863,7 +832,7 @@ impl PoolBuilder {
for package in
CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap())
{
- if Preg::is_match3(&pattern_regexp, package.get_name(), None).unwrap_or(false) {
+ if Preg::is_match3(&pattern_regexp, &package.get_name(), None).unwrap_or(false) {
continue 'outer;
}
}
@@ -905,10 +874,10 @@ impl PoolBuilder {
repositories: &Vec<Box<dyn RepositoryInterface>>,
name: &str,
) -> anyhow::Result<()> {
- let skipped: Vec<Box<dyn PackageInterface>> = self
+ let skipped: Vec<PackageInterfaceHandle> = self
.skipped_load
.get(name)
- .map(|v| v.iter().map(|p| p.clone_package_box()).collect())
+ .map(|v| v.iter().cloned().collect())
.unwrap_or_default();
for package_or_replacer in &skipped {
// if we unfixed a replaced package name, we also need to unfix the replacer itself
@@ -916,9 +885,9 @@ impl PoolBuilder {
if package_or_replacer.get_name() != name
&& self
.skipped_load
- .contains_key(package_or_replacer.get_name())
+ .contains_key(&package_or_replacer.get_name())
{
- let replacer_name = package_or_replacer.get_name().to_string();
+ let replacer_name = package_or_replacer.get_name();
if request.get_update_allow_transitive_root_dependencies()
|| (!self.is_root_require(request, name)
&& !self.is_root_require(request, &replacer_name))
@@ -932,8 +901,8 @@ impl PoolBuilder {
&MatchAllConstraint::new(None).into(),
);
} else {
- let pkgs: Vec<Box<dyn BasePackage>> =
- self.packages.values().map(|p| p.clone_box()).collect();
+ let pkgs: Vec<BasePackageHandle> =
+ self.packages.values().cloned().collect();
for loaded_package in &pkgs {
let requires = loaded_package.get_requires();
if let Some(req_link) = requires.get(&replacer_name) {
@@ -950,14 +919,14 @@ impl PoolBuilder {
}
if self.path_repo_unlocked.contains_key(name) {
- let entries: Vec<(i64, Box<dyn BasePackage>)> = self
+ let entries: Vec<(i64, BasePackageHandle)> = self
.packages
.iter()
- .filter(|(_, p)| PackageInterface::get_name(p.as_ref()) == name)
- .map(|(i, p)| (*i, p.clone_box()))
+ .filter(|(_, p)| p.get_name() == name)
+ .map(|(i, p)| (*i, p.clone()))
.collect();
for (index, package) in &entries {
- self.remove_loaded_package(request, repositories, &**package, *index);
+ self.remove_loaded_package(request, repositories, package, *index);
}
}
@@ -967,58 +936,41 @@ impl PoolBuilder {
self.path_repo_unlocked.shift_remove(name);
// remove locked package by this name which was already initialized
- let locked_packages: Vec<Box<dyn BasePackage>> = request
- .get_locked_packages()
- .values()
- .map(|p| p.clone_box())
- .collect();
+ let locked_packages: Vec<BasePackageHandle> =
+ request.get_locked_packages().values().cloned().collect();
for locked_package in &locked_packages {
- if locked_package.as_alias_package().is_none() && locked_package.get_name() == name {
- let pkgs: Vec<Box<dyn BasePackage>> =
- self.packages.values().map(|p| p.clone_box()).collect();
+ if locked_package.as_alias().is_none() && locked_package.get_name() == name {
+ let pkgs: Vec<BasePackageHandle> = self.packages.values().cloned().collect();
// PHP uses array_search with strict identity; map to pointer comparison.
- let index_opt = pkgs.iter().position(|p| {
- std::ptr::eq(
- p.as_ref() as *const _ as *const u8,
- locked_package.as_ref() as *const _ as *const u8,
- )
- });
+ let index_opt = pkgs.iter().position(|p| p.ptr_eq(locked_package));
if let Some(index) = index_opt {
- request.unlock_package(&**locked_package);
- self.remove_loaded_package(
- request,
- repositories,
- &**locked_package,
- index as i64,
- );
+ request.unlock_package(locked_package);
+ self.remove_loaded_package(request, repositories, locked_package, index as i64);
// make sure that any requirements for this package by other locked or fixed packages are now
// also loaded, as they were previously ignored because the locked (now unlocked) package already
// satisfied their requirements
// and if this package is replacing another that is required by a locked or fixed package, ensure
// 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
+ let fixed_or_locked: Vec<BasePackageHandle> = request
.get_fixed_or_locked_packages()
.values()
- .map(|p| p.clone_box())
+ .cloned()
.collect();
for fixed_or_locked_package in &fixed_or_locked {
- if std::ptr::eq(
- fixed_or_locked_package.as_ref() as *const _,
- locked_package.as_ref() as *const _,
- ) {
+ if fixed_or_locked_package.ptr_eq(locked_package) {
continue;
}
if self
.skipped_load
- .contains_key(fixed_or_locked_package.get_name())
+ .contains_key(&fixed_or_locked_package.get_name())
{
let requires = fixed_or_locked_package.get_requires();
- if let Some(req_link) = requires.get(locked_package.get_name()) {
+ if let Some(req_link) = requires.get(&locked_package.get_name()) {
self.mark_package_name_for_loading(
request,
- locked_package.get_name(),
+ &locked_package.get_name(),
req_link.get_constraint(),
);
}
@@ -1054,8 +1006,7 @@ impl PoolBuilder {
self.mark_package_name_for_loading(request, name, &cons);
}
- let pkgs: Vec<Box<dyn BasePackage>> =
- self.packages.values().map(|p| p.clone_box()).collect();
+ let pkgs: Vec<BasePackageHandle> = self.packages.values().cloned().collect();
for package in &pkgs {
for (_k, link) in &package.get_requires() {
if name == link.get_target() {
@@ -1073,35 +1024,24 @@ impl PoolBuilder {
&mut self,
_request: &Request,
repositories: &Vec<Box<dyn RepositoryInterface>>,
- package: &dyn BasePackage,
+ package: &BasePackageHandle,
index: i64,
) {
let repos_box: Vec<Box<dyn RepositoryInterface>> =
repositories.iter().map(|r| r.clone_box()).collect();
- let repo_index: i64 = match package.get_repository() {
- // PHP uses array_search with strict identity; map to pointer comparison.
- Some(repo) => repos_box
- .iter()
- .position(|r| {
- std::ptr::eq(
- r.as_ref() as *const _ as *const u8,
- repo as *const _ as *const u8,
- )
- })
- .map(|i| i as i64)
- .unwrap_or(-1),
- None => -1,
- };
+ let _ = &repos_box;
+ // TODO(phase-c): package->repository back-reference not yet on handles
+ let repo_index: i64 = -1;
if repo_index >= 0 {
if let Some(repo_map) = self.loaded_per_repo.get_mut(&repo_index) {
- if let Some(name_map) = repo_map.get_mut(PackageInterface::get_name(package)) {
- name_map.shift_remove(package.get_version());
+ if let Some(name_map) = repo_map.get_mut(&package.get_name()) {
+ name_map.shift_remove(&package.get_version());
}
}
}
self.packages.shift_remove(&index);
- let object_hash = spl_object_hash(package);
+ let object_hash = package.ptr_id().to_string();
if let Some(aliases) = self.alias_map.shift_remove(&object_hash) {
for (alias_index, alias_package) in &aliases {
if repo_index >= 0 {
diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
index 27b3ffb..8aa8324 100644
--- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
@@ -1,10 +1,8 @@
//! ref: composer/src/Composer/DependencyResolver/PoolOptimizer.php
-use std::any::Any;
-
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_php_shim::{LogicException, PhpMixed, implode, ksort, spl_object_hash};
+use shirabe_php_shim::{LogicException, PhpMixed, implode, ksort};
use shirabe_semver::compiling_matcher::CompilingMatcher;
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::MultiConstraint;
@@ -14,8 +12,7 @@ use shirabe_semver::intervals::Intervals;
use crate::dependency_resolver::PolicyInterface;
use crate::dependency_resolver::Pool;
use crate::dependency_resolver::Request;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
use crate::package::version::VersionParser;
@@ -38,7 +35,7 @@ pub struct PoolOptimizer {
packages_to_remove: IndexMap<i64, bool>,
/// @var array<int, BasePackage[]>
- aliases_per_package: IndexMap<i64, Vec<Box<dyn BasePackage>>>,
+ aliases_per_package: IndexMap<i64, Vec<BasePackageHandle>>,
/// @var array<string, array<string, string>>
removed_versions_by_package: IndexMap<String, IndexMap<String, String>>,
@@ -94,7 +91,7 @@ impl PoolOptimizer {
// Mark fixed or locked packages as irremovable
for (_, package) in request.get_fixed_or_locked_packages() {
irremovable_package_constraint_groups
- .entry(PackageInterface::get_name(package.as_ref()).to_string())
+ .entry(package.get_name())
.or_insert_with(Vec::new)
.push(
SimpleConstraint::new(
@@ -130,11 +127,11 @@ 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().downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_alias() {
self.aliases_per_package
.entry(alias_pkg.get_alias_of().id())
.or_insert_with(Vec::new)
- .push(package.clone_box());
+ .push(package.clone());
}
}
@@ -153,31 +150,30 @@ impl PoolOptimizer {
// Mark the packages as irremovable based on the constraints
for package in pool.get_packages() {
- if !irremovable_package_constraints
- .contains_key(PackageInterface::get_name(package.as_ref()))
- {
+ if !irremovable_package_constraints.contains_key(&package.get_name()) {
continue;
}
let constraint = irremovable_package_constraints
- .get(PackageInterface::get_name(package.as_ref()))
+ .get(&package.get_name())
.unwrap();
if CompilingMatcher::r#match(
constraint,
SimpleConstraint::OP_EQ,
package.get_version().to_string(),
) {
- self.mark_package_irremovable(package.as_ref());
+ self.mark_package_irremovable(package);
}
}
}
- fn mark_package_irremovable(&mut self, package: &dyn BasePackage) {
+ fn mark_package_irremovable(&mut self, package: &BasePackageHandle) {
self.irremovable_packages.insert(package.id(), true);
- if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_alias() {
// 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());
+ let aliased: BasePackageHandle = alias_pkg.get_alias_of().into();
+ self.mark_package_irremovable(&aliased);
}
// PHP: foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage)
let alias_ids: Vec<i64> = self
@@ -192,14 +188,14 @@ impl PoolOptimizer {
/// @return Pool Optimized pool
fn apply_removals_to_pool(&self, pool: &Pool) -> Pool {
- let mut packages: Vec<Box<dyn BasePackage>> = vec![];
+ let mut packages: Vec<BasePackageHandle> = 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()) {
- packages.push(package.clone_box());
+ packages.push(package.clone());
} else {
removed_versions
- .entry(PackageInterface::get_name(package.as_ref()).to_string())
+ .entry(package.get_name())
.or_insert_with(IndexMap::new)
.insert(
package.get_version().to_string(),
@@ -210,8 +206,7 @@ impl PoolOptimizer {
Pool::new(
packages,
- // TODO(phase-b): clone Vec<Box<BasePackage>> from getter
- todo!("pool.get_unacceptable_fixed_or_locked_packages().clone()"),
+ pool.get_unacceptable_fixed_or_locked_packages().clone(),
removed_versions,
self.removed_versions_by_package.clone(),
// TODO(phase-b): PartialSecurityAdvisory is a PHP class (no Clone). Need shared ownership rework.
@@ -227,7 +222,7 @@ impl PoolOptimizer {
) -> Result<()> {
let mut identical_definitions_per_package: IndexMap<
String,
- IndexMap<String, IndexMap<String, Vec<Box<dyn BasePackage>>>>,
+ IndexMap<String, IndexMap<String, Vec<BasePackageHandle>>>,
> = IndexMap::new();
let mut package_identical_definition_lookup: IndexMap<
i64,
@@ -243,7 +238,7 @@ impl PoolOptimizer {
self.mark_package_for_removal(package.id())?;
- let dependency_hash = self.calculate_dependency_hash(package.as_ref());
+ let dependency_hash = self.calculate_dependency_hash(package);
for package_name in package.get_names(false) {
if !self
@@ -315,7 +310,7 @@ impl PoolOptimizer {
.or_insert_with(IndexMap::new)
.entry(dependency_hash.clone())
.or_insert_with(Vec::new)
- .push(package.clone_box());
+ .push(package.clone());
package_identical_definition_lookup
.entry(package.id())
.or_insert_with(IndexMap::new)
@@ -331,18 +326,14 @@ impl PoolOptimizer {
}
// PHP: foreach ($identicalDefinitionsPerPackage as $constraintGroups)
- // TODO(phase-b): Box<dyn BasePackage> is not Clone; need restructuring to avoid borrow conflict.
- let identical_clone: IndexMap<
- String,
- IndexMap<String, IndexMap<String, Vec<Box<dyn BasePackage>>>>,
- > = todo!("identical_definitions_per_package.clone()");
+ let identical_clone = identical_definitions_per_package.clone();
for (_, constraint_groups) in identical_clone.iter() {
for (_, constraint_group) in constraint_groups.iter() {
for (_, packages) in constraint_group.iter() {
// Only one package in this constraint group has the same requirements, we're not allowed to remove that package
if 1 == packages.len() {
self.keep_package(
- packages[0].as_ref(),
+ &packages[0],
&identical_definitions_per_package,
&package_identical_definition_lookup,
);
@@ -362,7 +353,7 @@ impl PoolOptimizer {
.select_preferred_packages(pool, literals.clone(), None)
{
self.keep_package(
- pool.literal_to_package(preferred_literal),
+ &pool.literal_to_package(preferred_literal),
&identical_definitions_per_package,
&package_identical_definition_lookup,
);
@@ -374,7 +365,7 @@ impl PoolOptimizer {
Ok(())
}
- fn calculate_dependency_hash(&self, package: &dyn BasePackage) -> String {
+ fn calculate_dependency_hash(&self, package: &BasePackageHandle) -> String {
let mut hash = String::new();
let hash_relevant_links: Vec<(&str, Vec<crate::package::Link>)> = vec![
@@ -447,10 +438,10 @@ impl PoolOptimizer {
/// @param array<int, array<string, array{groupHash: string, dependencyHash: string}>> $packageIdenticalDefinitionLookup
fn keep_package(
&mut self,
- package: &dyn BasePackage,
+ package: &BasePackageHandle,
identical_definitions_per_package: &IndexMap<
String,
- IndexMap<String, IndexMap<String, Vec<Box<dyn BasePackage>>>>,
+ IndexMap<String, IndexMap<String, Vec<BasePackageHandle>>>,
>,
package_identical_definition_lookup: &IndexMap<
i64,
@@ -464,11 +455,12 @@ impl PoolOptimizer {
self.packages_to_remove.shift_remove(&package.id());
- if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_alias() {
// recursing here so aliasesPerPackage for the aliasOf can be checked
// and all its aliases marked to be kept as well
+ let aliased: BasePackageHandle = alias_pkg.get_alias_of().into();
self.keep_package(
- alias_pkg.get_alias_of(),
+ &aliased,
identical_definitions_per_package,
package_identical_definition_lookup,
);
@@ -484,21 +476,19 @@ impl PoolOptimizer {
.and_then(|m| m.get(&package_group_pointers.dependency_hash));
if let Some(package_group) = package_group {
for pkg in package_group {
- let pkg = if let Some(alias_pkg) =
- pkg.as_any().downcast_ref::<AliasPackage>()
- {
+ let pkg: BasePackageHandle = if let Some(alias_pkg) = pkg.as_alias() {
if alias_pkg.get_pretty_version()
== VersionParser::DEFAULT_BRANCH_ALIAS
{
- alias_pkg.get_alias_of().clone_box()
+ alias_pkg.get_alias_of().into()
} else {
- pkg.clone_box()
+ pkg.clone()
}
} else {
- pkg.clone_box()
+ pkg.clone()
};
self.removed_versions_by_package
- .entry(spl_object_hash(package))
+ .entry(package.ptr_id().to_string())
.or_insert_with(IndexMap::new)
.insert(
pkg.get_version().to_string(),
@@ -533,18 +523,17 @@ impl PoolOptimizer {
.and_then(|m| m.get(&package_group_pointers.dependency_hash));
if let Some(package_group) = package_group {
for pkg in package_group {
- let pkg = if let Some(alias_pkg) =
- pkg.as_any().downcast_ref::<AliasPackage>()
+ let pkg: BasePackageHandle = if let Some(alias_pkg) = pkg.as_alias()
{
if alias_pkg.get_pretty_version()
== VersionParser::DEFAULT_BRANCH_ALIAS
{
- alias_pkg.get_alias_of().clone_box()
+ alias_pkg.get_alias_of().into()
} else {
- pkg.clone_box()
+ pkg.clone()
}
} else {
- pkg.clone_box()
+ pkg.clone()
};
self.removed_versions_by_package
.entry(format!("alias-{}", alias_id))
@@ -569,8 +558,7 @@ impl PoolOptimizer {
return;
}
- let mut package_index: IndexMap<String, IndexMap<i64, Box<dyn BasePackage>>> =
- IndexMap::new();
+ let mut package_index: IndexMap<String, IndexMap<i64, BasePackageHandle>> = IndexMap::new();
for package in pool.get_packages() {
let id = package.id();
@@ -580,22 +568,18 @@ impl PoolOptimizer {
continue;
}
// Do not remove a package aliased by another package, nor aliases
- if self.aliases_per_package.contains_key(&id)
- || package.as_any().downcast_ref::<AliasPackage>().is_some()
- {
+ if self.aliases_per_package.contains_key(&id) || package.as_alias().is_some() {
continue;
}
// Do not remove locked packages
- if request.is_fixed_package(package.as_ref())
- || request.is_locked_package(todo!("package as &dyn PackageInterface"))
- {
+ if request.is_fixed_package(&package) || request.is_locked_package(&package) {
continue;
}
package_index
- .entry(PackageInterface::get_name(package.as_ref()).to_string())
+ .entry(package.get_name())
.or_insert_with(IndexMap::new)
- .insert(package.id(), package.clone_box());
+ .insert(package.id(), package.clone());
}
for (_, package) in request.get_locked_packages() {
diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs
index 4436ec8..509a0dc 100644
--- a/crates/shirabe/src/dependency_resolver/problem.rs
+++ b/crates/shirabe/src/dependency_resolver/problem.rs
@@ -19,6 +19,7 @@ use crate::dependency_resolver::Request;
use crate::dependency_resolver::rule::{self, Rule};
use crate::package::AliasPackage;
use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::CompletePackageInterface;
use crate::package::Link;
use crate::package::PackageInterface;
@@ -67,7 +68,7 @@ impl Problem {
request: &Request,
pool: &mut Pool,
is_verbose: bool,
- installed_map: &IndexMap<String, Box<dyn BasePackage>>,
+ installed_map: &IndexMap<String, BasePackageHandle>,
learned_pool: &Vec<Vec<std::rc::Rc<std::cell::RefCell<Rule>>>>,
) -> anyhow::Result<String> {
// TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections?
@@ -155,7 +156,9 @@ impl Problem {
// TODO(phase-b): reason_data is a Link.
let source = rule.get_source_package(pool).unwrap();
let link_pretty = match rule.get_reason_data() {
- rule::ReasonData::Link(link) => link.get_pretty_string(source.as_ref()),
+ rule::ReasonData::Link(link) => {
+ link.get_pretty_string(source.as_rc().borrow().as_package_interface())
+ }
_ => String::new(),
};
format!("{}//{}", source.get_pretty_string(), link_pretty)
@@ -205,7 +208,7 @@ impl Problem {
request: &Request,
pool: &mut Pool,
is_verbose: bool,
- installed_map: &IndexMap<String, Box<dyn BasePackage>>,
+ installed_map: &IndexMap<String, BasePackageHandle>,
learned_pool: &Vec<Vec<std::rc::Rc<std::cell::RefCell<Rule>>>>,
) -> String {
let mut messages: Vec<String> = Vec::new();
@@ -258,9 +261,9 @@ impl Problem {
.entry(pkg_key.clone())
.or_insert_with(IndexMap::new)
.insert(version_key, m2.clone());
- let source_package = rule_ref.get_source_package(pool);
+ let source_package = rule_ref.get_source_package(pool).unwrap();
for (version, pretty_version) in
- pool.get_removed_versions_by_package(&spl_object_hash(&source_package))
+ pool.get_removed_versions_by_package(&source_package.ptr_id().to_string())
{
templates
.get_mut(&template)
@@ -551,11 +554,13 @@ impl Problem {
}
}
- let mut locked_package: Option<Box<dyn BasePackage>> = None;
+ let mut locked_package: Option<BasePackageHandle> = None;
for (_key, package) in request.get_locked_packages() {
- if package.get_name() == package_name {
- locked_package = Some(package.clone_box());
- if pool.is_unacceptable_fixed_or_locked_package(package.as_ref()) {
+ if package.get_name().as_str() == package_name {
+ locked_package = Some(package.clone());
+ // TODO(phase-c): wire Pool::is_unacceptable_fixed_or_locked_package(package) here;
+ // the locked package handle and the pool's identity check are now both handle-based.
+ if todo!("is_unacceptable_fixed_or_locked_package with a request package handle") {
return (
"- ".to_string(),
format!(
@@ -629,7 +634,7 @@ impl Problem {
if packages.len() > 0 {
let root_reqs = repository_set.get_root_requires();
if root_reqs.contains_key(package_name) {
- let filtered: Vec<&Box<dyn BasePackage>> = packages
+ let filtered: Vec<&BasePackageHandle> = packages
.iter()
.filter(|p| {
root_reqs[package_name].matches(
@@ -673,7 +678,7 @@ impl Problem {
let first_pkg = packages.first().unwrap();
for name in first_pkg.get_names(true) {
if temp_reqs.contains_key(&name) {
- let filtered: Vec<&Box<dyn BasePackage>> = packages
+ let filtered: Vec<&BasePackageHandle> = packages
.iter()
.filter(|p| {
temp_reqs[&name].matches(
@@ -721,7 +726,7 @@ impl Problem {
lp.get_version().to_string(),
None,
));
- let filtered: Vec<&Box<dyn BasePackage>> = packages
+ let filtered: Vec<&BasePackageHandle> = packages
.iter()
.filter(|p| {
fixed_constraint.matches(
@@ -756,14 +761,10 @@ impl Problem {
}
}
- let non_locked_packages: Vec<&Box<dyn BasePackage>> = packages
- .iter()
- .filter(|p| {
- p.get_repository()
- .and_then(|r| r.as_any().downcast_ref::<LockArrayRepository>())
- .is_none()
- })
- .collect();
+ // TODO(phase-c): filtering out packages from a LockArrayRepository needs the handle's
+ // repository back-reference (phase-c handoff item #1), which is not yet available; keep
+ // all packages for now.
+ let non_locked_packages: Vec<&BasePackageHandle> = packages.iter().collect();
if non_locked_packages.len() == 0 {
return (
@@ -961,7 +962,7 @@ impl Problem {
let all_repos_packages = &packages;
let top_package = all_repos_packages.first();
if let Some(tp) = top_package {
- if tp.as_root_package_interface().is_some() {
+ if tp.as_root().is_some() {
suffix = " See https://getcomposer.org/dep-on-root for details and assistance."
.to_string();
}
@@ -1023,7 +1024,7 @@ impl Problem {
/// @internal
pub fn get_package_list(
- packages: &Vec<Box<dyn BasePackage>>,
+ packages: &Vec<BasePackageHandle>,
is_verbose: bool,
pool: Option<&Pool>,
constraint: Option<&AnyConstraint>,
@@ -1044,7 +1045,7 @@ impl Problem {
versions: IndexMap::new(),
});
entry.name = package.get_pretty_name().to_string();
- let alias_suffix = if let Some(alias) = package.as_alias_package() {
+ let alias_suffix = if let Some(alias) = package.as_alias() {
format!(" (alias of {})", alias.get_alias_of().get_pretty_version())
} else {
String::new()
@@ -1064,7 +1065,7 @@ impl Problem {
if pool.is_some() && use_removed_version_group {
for (version, pretty_version) in pool
.unwrap()
- .get_removed_versions_by_package(&spl_object_hash(package.as_ref()))
+ .get_removed_versions_by_package(&package.ptr_id().to_string())
{
entry.versions.insert(version, pretty_version);
}
@@ -1129,16 +1130,12 @@ impl Problem {
let available = pool.what_provides(package_name, None);
if available.len() > 0 {
- let mut selected: Option<&Box<dyn BasePackage>> = None;
+ let mut selected: Option<&BasePackageHandle> = None;
+ // TODO(phase-c): the handle does not expose get_repository (a `RefCell`-borrowed
+ // back-reference); preferring the package from a PlatformRepository needs repository
+ // back-references on handles. Falling back to the first candidate for now.
for pkg in &available {
- if pkg
- .get_repository()
- .and_then(|r| r.as_any().downcast_ref::<PlatformRepository>())
- .is_some()
- {
- selected = Some(pkg);
- break;
- }
+ let _ = pkg;
}
if selected.is_none() {
selected = available.first();
@@ -1163,15 +1160,14 @@ impl Problem {
let mut version: String = selected.get_pretty_version().to_string();
let extra = selected.get_extra();
- if selected.as_complete_package_interface().is_some()
+ if selected.as_complete().is_some()
&& extra.contains_key("config.platform")
&& extra["config.platform"].as_bool() == Some(true)
{
let description: String = selected
- .as_complete_package_interface()
+ .as_complete()
.and_then(|c| c.get_description())
- .unwrap_or("")
- .to_string();
+ .unwrap_or_default();
version = format!("{}; {}", version, str_replace("Package ", "", &description));
}
return Some(version);
@@ -1226,10 +1222,10 @@ impl Problem {
filtered
}
- fn has_multiple_names(packages: &Vec<Box<dyn BasePackage>>) -> bool {
+ fn has_multiple_names(packages: &Vec<BasePackageHandle>) -> bool {
let mut name: Option<String> = None;
for package in packages {
- if name.is_none() || name.as_deref() == Some(package.get_name()) {
+ if name.is_none() || name.as_deref() == Some(package.get_name().as_str()) {
name = Some(package.get_name().to_string());
} else {
return true;
@@ -1243,30 +1239,22 @@ impl Problem {
pool: &Pool,
is_verbose: bool,
package_name: &str,
- higher_repo_packages: &Vec<Box<dyn BasePackage>>,
- all_repos_packages: &Vec<Box<dyn BasePackage>>,
+ higher_repo_packages: &Vec<BasePackageHandle>,
+ all_repos_packages: &Vec<BasePackageHandle>,
reason: &str,
constraint: Option<&AnyConstraint>,
) -> (String, String) {
- let mut next_repo_packages: Vec<Box<dyn BasePackage>> = Vec::new();
- let mut next_repo: Option<Box<dyn crate::repository::RepositoryInterface>> = None;
-
- for package in all_repos_packages {
- // TODO(phase-b): RepositoryInterface has no equals(); reference identity needed.
- if next_repo.is_none() {
- next_repo_packages.push(package.clone_box());
- next_repo = package.get_repository().map(|r| r.clone_box());
- } else {
- break;
- }
- }
-
- // assert(null !== $nextRepo);
- let next_repo = next_repo.unwrap();
+ // TODO(phase-c): selecting the next repository's packages relies on each package's
+ // repository back-reference, which the handle does not yet expose (phase-c handoff
+ // item #1). Both `next_repo_packages` and `next_repo` are blocked on that decision.
+ let _ = all_repos_packages;
+ let next_repo_packages: Vec<BasePackageHandle> = Vec::new();
+ let next_repo: Box<dyn crate::repository::RepositoryInterface> =
+ todo!("repository back-reference on handle pending (phase-c handoff item #1)");
if higher_repo_packages.len() > 0 {
let top_package = higher_repo_packages.first().unwrap();
- if top_package.as_root_package_interface().is_some() {
+ if top_package.as_root().is_some() {
return (
format!(
"- Root composer.json requires {}{}, it is ",
@@ -1309,7 +1297,7 @@ impl Problem {
)
);
// symlinked path repos cannot be locked so do not suggest keeping it locked
- if next_repo_packages[0].get_dist_type() == Some("path") {
+ if next_repo_packages[0].get_dist_type() == Some("path".to_string()) {
let transport_options = next_repo_packages[0].get_transport_options();
if !transport_options.contains_key("symlink")
|| transport_options["symlink"].as_bool() != Some(false)
@@ -1367,12 +1355,10 @@ impl Problem {
constraint,
false
),
- higher_repo_packages
- .first()
- .unwrap()
- .get_repository()
- .map(|r| r.get_repo_name())
- .unwrap_or_default(),
+ // TODO(phase-c): the higher repo's name needs the handle's repository
+ // back-reference (phase-c handoff item #1); unreachable until `next_repo` above
+ // is resolved.
+ String::new(),
reason
),
)
diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs
index e2bfc3c..686373b 100644
--- a/crates/shirabe/src/dependency_resolver/request.rs
+++ b/crates/shirabe/src/dependency_resolver/request.rs
@@ -5,8 +5,7 @@ use shirabe_php_shim::{LogicException, spl_object_hash, strtolower};
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::MatchAllConstraint;
-use crate::package::BasePackage;
-use crate::package::PackageInterface;
+use crate::package::BasePackageHandle;
use crate::repository::CanonicalPackagesTrait;
use crate::repository::LockArrayRepository;
use crate::repository::RepositoryInterface;
@@ -44,9 +43,9 @@ pub enum UpdateAllowTransitiveDeps {
pub struct Request {
pub(crate) locked_repository: Option<LockArrayRepository>,
pub(crate) requires: IndexMap<String, AnyConstraint>,
- pub(crate) fixed_packages: IndexMap<String, Box<dyn BasePackage>>,
- pub(crate) locked_packages: IndexMap<String, Box<dyn BasePackage>>,
- pub(crate) fixed_locked_packages: IndexMap<String, Box<dyn BasePackage>>,
+ pub(crate) fixed_packages: IndexMap<String, BasePackageHandle>,
+ pub(crate) locked_packages: IndexMap<String, BasePackageHandle>,
+ pub(crate) fixed_locked_packages: IndexMap<String, BasePackageHandle>,
pub(crate) update_allow_list: Vec<String>,
pub(crate) update_allow_transitive_dependencies: UpdateAllowTransitiveDeps,
restrict_packages: Option<Vec<String>>,
@@ -94,8 +93,8 @@ impl Request {
/// This is used for platform packages which cannot be modified by Composer. A rule enforcing
/// their installation is generated for dependency resolution. Partial updates with dependencies
/// cannot in any way modify these packages.
- pub fn fix_package(&mut self, package: Box<dyn BasePackage>) {
- let hash = spl_object_hash(&package);
+ pub fn fix_package(&mut self, package: BasePackageHandle) {
+ let hash = package.ptr_id().to_string();
self.fixed_packages.insert(hash, package);
}
@@ -109,8 +108,8 @@ impl Request {
/// for the solver, so if nothing requires these packages they will be removed. Additionally in
/// a partial update these packages can be unlocked, meaning other versions can be installed if
/// explicitly requested as part of the update.
- pub fn lock_package(&mut self, package: Box<dyn BasePackage>) {
- let hash = spl_object_hash(&package);
+ pub fn lock_package(&mut self, package: BasePackageHandle) {
+ let hash = package.ptr_id().to_string();
self.locked_packages.insert(hash, package);
}
@@ -120,15 +119,14 @@ impl Request {
/// should not allow removal of any packages. At the same time lock packages there cannot simply
/// be marked fixed, as error reporting would then report them as platform packages, so this
/// still marks them as locked packages at the same time.
- pub fn fix_locked_package(&mut self, package: Box<dyn BasePackage>) {
- let hash = spl_object_hash(&package);
- self.fixed_packages
- .insert(hash.clone(), package.clone_box());
+ pub fn fix_locked_package(&mut self, package: BasePackageHandle) {
+ let hash = package.ptr_id().to_string();
+ self.fixed_packages.insert(hash.clone(), package.clone());
self.fixed_locked_packages.insert(hash, package);
}
- pub fn unlock_package(&mut self, package: &dyn BasePackage) {
- self.locked_packages.remove(&spl_object_hash(package));
+ pub fn unlock_package(&mut self, package: &BasePackageHandle) {
+ self.locked_packages.remove(&package.ptr_id().to_string());
}
pub fn set_update_allow_list(
@@ -159,33 +157,34 @@ impl Request {
&self.requires
}
- pub fn get_fixed_packages(&self) -> &IndexMap<String, Box<dyn BasePackage>> {
+ pub fn get_fixed_packages(&self) -> &IndexMap<String, BasePackageHandle> {
&self.fixed_packages
}
- pub fn is_fixed_package(&self, package: &dyn BasePackage) -> bool {
- self.fixed_packages.contains_key(&spl_object_hash(package))
+ pub fn is_fixed_package(&self, package: &BasePackageHandle) -> bool {
+ self.fixed_packages
+ .contains_key(&package.ptr_id().to_string())
}
- pub fn get_locked_packages(&self) -> &IndexMap<String, Box<dyn BasePackage>> {
+ pub fn get_locked_packages(&self) -> &IndexMap<String, BasePackageHandle> {
&self.locked_packages
}
- pub fn is_locked_package(&self, package: &dyn PackageInterface) -> bool {
- let hash = spl_object_hash(package);
+ pub fn is_locked_package(&self, package: &BasePackageHandle) -> bool {
+ let hash = package.ptr_id().to_string();
self.locked_packages.contains_key(&hash) || self.fixed_locked_packages.contains_key(&hash)
}
- pub fn get_fixed_or_locked_packages(&self) -> IndexMap<String, Box<dyn BasePackage>> {
- let mut result: IndexMap<String, Box<dyn BasePackage>> = self
+ pub fn get_fixed_or_locked_packages(&self) -> IndexMap<String, BasePackageHandle> {
+ let mut result: IndexMap<String, BasePackageHandle> = self
.fixed_packages
.iter()
- .map(|(k, v)| (k.clone(), v.clone_box()))
+ .map(|(k, v)| (k.clone(), v.clone()))
.collect();
result.extend(
self.locked_packages
.iter()
- .map(|(k, v)| (k.clone(), v.clone_box())),
+ .map(|(k, v)| (k.clone(), v.clone())),
);
result
}
@@ -194,15 +193,18 @@ impl Request {
/// is for the installed map in the solver problems.
/// Some locked packages may not be in the pool,
/// so they have a package->id of -1
- pub fn get_present_map(&self, package_ids: bool) -> IndexMap<String, Box<dyn BasePackage>> {
- let mut present_map: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ pub fn get_present_map(
+ &self,
+ package_ids: bool,
+ ) -> IndexMap<String, crate::package::BasePackageHandle> {
+ let mut present_map: IndexMap<String, crate::package::BasePackageHandle> = IndexMap::new();
if let Some(ref locked_repository) = self.locked_repository {
for package in RepositoryInterface::get_packages(locked_repository) {
let key = if package_ids {
package.get_id().to_string()
} else {
- spl_object_hash(&package)
+ package.ptr_id().to_string()
};
present_map.insert(key, package);
}
@@ -212,18 +214,18 @@ impl Request {
let key = if package_ids {
package.get_id().to_string()
} else {
- spl_object_hash(package)
+ package.ptr_id().to_string()
};
- present_map.insert(key, package.clone_box());
+ present_map.insert(key, package.clone());
}
present_map
}
- pub fn get_fixed_packages_map(&self) -> IndexMap<i64, Box<dyn BasePackage>> {
- let mut fixed_packages_map: IndexMap<i64, Box<dyn BasePackage>> = IndexMap::new();
+ pub fn get_fixed_packages_map(&self) -> IndexMap<i64, BasePackageHandle> {
+ let mut fixed_packages_map: IndexMap<i64, BasePackageHandle> = IndexMap::new();
for (_, package) in &self.fixed_packages {
- fixed_packages_map.insert(package.get_id(), package.clone_box());
+ fixed_packages_map.insert(package.get_id(), package.clone());
}
fixed_packages_map
}
diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs
index fbc8521..afb0318 100644
--- a/crates/shirabe/src/dependency_resolver/rule.rs
+++ b/crates/shirabe/src/dependency_resolver/rule.rs
@@ -22,6 +22,7 @@ use crate::dependency_resolver::Rule2Literals;
use crate::dependency_resolver::RuleSet;
use crate::package::AliasPackage;
use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::Link;
use crate::package::PackageInterface;
use crate::package::version::VersionParser;
@@ -31,7 +32,7 @@ use crate::repository::RepositorySet;
#[derive(Debug)]
pub enum ReasonData {
Link(Link),
- BasePackage(Box<dyn BasePackage>),
+ BasePackage(BasePackageHandle),
String(String),
Int(i64),
RootRequire {
@@ -39,7 +40,7 @@ pub enum ReasonData {
constraint: AnyConstraint,
},
Fixed {
- package: Box<dyn BasePackage>,
+ package: BasePackageHandle,
},
/// Phase B placeholder for an arbitrary PHP-side value not yet mapped to a real variant.
Mixed(PhpMixed),
@@ -231,9 +232,9 @@ impl Rule {
// TODO(phase-b): Request::get_locked_repository() signature
let locked_repo: Option<()> = todo!("request.get_locked_repository()");
if let Some(_locked_repo) = locked_repo {
- let packages: Vec<Box<dyn BasePackage>> = todo!("locked_repo.get_packages()");
+ let packages: Vec<BasePackageHandle> = todo!("locked_repo.get_packages()");
for package in packages {
- let p: &dyn BasePackage = todo!("package as BasePackage reference");
+ let p: &BasePackageHandle = &package;
if p.get_name() == link.get_target() {
if pool.is_unacceptable_fixed_or_locked_package(p) {
return true;
@@ -272,10 +273,10 @@ impl Rule {
// TODO(phase-b): Request::get_locked_repository() signature
let locked_repo: Option<()> = todo!("request.get_locked_repository()");
if let Some(_locked_repo) = locked_repo {
- let packages: Vec<Box<dyn BasePackage>> = todo!("locked_repo.get_packages()");
+ let packages: Vec<BasePackageHandle> = todo!("locked_repo.get_packages()");
for package in packages {
- let p: &dyn BasePackage = todo!("package as BasePackage reference");
- if p.get_name() == package_name {
+ let p: &BasePackageHandle = &package;
+ if p.get_name() == *package_name {
if pool.is_unacceptable_fixed_or_locked_package(p) {
return true;
}
@@ -300,17 +301,15 @@ impl Rule {
}
/// @internal
- pub fn get_source_package(&self, pool: &Pool) -> Result<Box<dyn BasePackage>> {
+ pub fn get_source_package(&self, pool: &Pool) -> Result<BasePackageHandle> {
let literals = self.get_literals();
match self.get_reason() {
r if r == RULE_PACKAGE_CONFLICT => {
- 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 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 reason_data = self.get_reason_data();
// swap literals if they are not in the right order with package2 being the conflicter
@@ -325,9 +324,8 @@ impl Rule {
r if r == RULE_PACKAGE_REQUIRES => {
let source_literal = literals[0];
- let source_package = self.deduplicate_default_branch_alias(
- pool.literal_to_package(source_literal).clone_box(),
- );
+ let source_package =
+ self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal));
Ok(source_package)
}
@@ -348,7 +346,7 @@ impl Rule {
request: &Request,
pool: &mut Pool,
is_verbose: bool,
- installed_map: &IndexMap<String, Box<dyn BasePackage>>,
+ installed_map: &IndexMap<String, BasePackageHandle>,
_learned_pool: &Vec<Vec<Rc<RefCell<Rule>>>>,
) -> String {
let mut literals = self.get_literals();
@@ -373,10 +371,10 @@ impl Rule {
);
}
- let packages_non_alias: Vec<Box<dyn BasePackage>> = packages
+ let packages_non_alias: Vec<BasePackageHandle> = packages
.iter()
- .filter(|p| p.as_any().downcast_ref::<AliasPackage>().is_none())
- .map(|p| p.clone_box())
+ .filter(|p| p.as_alias().is_none())
+ .map(|p| p.clone())
.collect();
if packages_non_alias.len() == 1 {
let package = &packages_non_alias[0];
@@ -396,7 +394,7 @@ impl Rule {
constraint.get_pretty_string(),
self.format_packages_unique_from_packages(
pool,
- packages,
+ packages.iter().map(|p| p.clone()).collect(),
is_verbose,
Some(constraint),
false
@@ -406,7 +404,7 @@ impl Rule {
r if r == RULE_FIXED => {
let package_in = match self.get_reason_data() {
- ReasonData::Fixed { package } => package.clone_box(),
+ ReasonData::Fixed { package } => package.clone(),
_ => return String::new(),
};
let package = self.deduplicate_default_branch_alias(package_in);
@@ -427,12 +425,10 @@ impl Rule {
}
r if r == RULE_PACKAGE_CONFLICT => {
- 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 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 conflict_target = package1.get_pretty_string();
let reason_data = self.get_reason_data();
@@ -495,21 +491,21 @@ impl Rule {
r if r == 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).clone_box(),
- );
+ let source_package =
+ self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal));
let reason_data = self.get_reason_data();
let link = match reason_data {
ReasonData::Link(l) => l,
_ => return String::new(),
};
- let mut requires: Vec<Box<dyn BasePackage>> = vec![];
+ let mut requires: Vec<BasePackageHandle> = vec![];
for literal in &literals {
- requires.push(pool.literal_to_package(*literal).clone_box());
+ requires.push(pool.literal_to_package(*literal));
}
- let text = link.get_pretty_string(&*source_package);
+ let text =
+ link.get_pretty_string(source_package.as_rc().borrow().as_package_interface());
if requires.len() > 0 {
format!(
"{} -> satisfiable by {}.",
@@ -573,13 +569,13 @@ impl Rule {
reason_str
};
- let mut installed_packages: Vec<Box<dyn BasePackage>> = vec![];
- let mut removable_packages: Vec<Box<dyn BasePackage>> = vec![];
+ let mut installed_packages: Vec<BasePackageHandle> = vec![];
+ let mut removable_packages: Vec<BasePackageHandle> = vec![];
for literal in &literals {
if installed_map.contains_key(&abs(*literal).to_string()) {
- installed_packages.push(pool.literal_to_package(*literal).clone_box());
+ installed_packages.push(pool.literal_to_package(*literal));
} else {
- removable_packages.push(pool.literal_to_package(*literal).clone_box());
+ removable_packages.push(pool.literal_to_package(*literal));
}
}
@@ -628,7 +624,7 @@ impl Rule {
let rule_text = if literals.len() == 1 {
pool.literal_to_pretty_string(literals[0], &installed_map)
} else {
- let mut groups: IndexMap<String, Vec<Box<dyn BasePackage>>> = IndexMap::new();
+ let mut groups: IndexMap<String, Vec<BasePackageHandle>> = IndexMap::new();
for literal in &literals {
let package = pool.literal_to_package(*literal);
let group = if installed_map.contains_key(&package.id().to_string()) {
@@ -644,7 +640,7 @@ impl Rule {
groups
.entry(group.to_string())
.or_insert_with(Vec::new)
- .push(self.deduplicate_default_branch_alias(package.clone_box()));
+ .push(self.deduplicate_default_branch_alias(package.clone()));
}
let mut rule_texts: Vec<String> = vec![];
for (group, packages) in &groups {
@@ -654,7 +650,7 @@ impl Rule {
if packages.len() > 1 { " one of" } else { "" },
self.format_packages_unique_from_packages(
pool,
- packages.iter().map(|p| p.clone_box()).collect(),
+ packages.iter().map(|p| p.clone()).collect(),
is_verbose,
None,
false,
@@ -674,9 +670,8 @@ impl Rule {
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]).clone_box(),
- );
+ let package =
+ self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1]));
format!(
"{} is an alias of {} and thus requires it to be installed too.",
@@ -692,9 +687,8 @@ impl Rule {
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]).clone_box(),
- );
+ let package =
+ self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0]));
format!(
"{} is an alias of {} and must be installed with it.",
@@ -720,7 +714,7 @@ impl Rule {
fn format_packages_unique_from_packages(
&self,
pool: &Pool,
- packages: Vec<Box<dyn BasePackage>>,
+ packages: Vec<BasePackageHandle>,
is_verbose: bool,
constraint: Option<&AnyConstraint>,
use_removed_version_group: bool,
@@ -743,9 +737,9 @@ impl Rule {
constraint: Option<&AnyConstraint>,
use_removed_version_group: bool,
) -> String {
- let mut packages: Vec<Box<dyn BasePackage>> = vec![];
+ let mut packages: Vec<BasePackageHandle> = vec![];
for literal in literals {
- packages.push(pool.literal_to_package(*literal).clone_box());
+ packages.push(pool.literal_to_package(*literal));
}
Problem::get_package_list(
&packages,
@@ -756,13 +750,10 @@ impl Rule {
)
}
- fn deduplicate_default_branch_alias(
- &self,
- package: Box<dyn BasePackage>,
- ) -> Box<dyn BasePackage> {
- if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
+ fn deduplicate_default_branch_alias(&self, package: BasePackageHandle) -> BasePackageHandle {
+ if let Some(alias_pkg) = package.as_alias() {
if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
- return alias_pkg.get_alias_of().clone_box();
+ return alias_pkg.get_alias_of().into();
}
}
diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
index 076ce42..69f5c4a 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
@@ -19,17 +19,15 @@ use crate::dependency_resolver::rule::{self, Rule};
use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFilter;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
-use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
#[derive(Debug)]
pub struct RuleSetGenerator {
pub(crate) policy: Box<dyn PolicyInterface>,
pub(crate) pool: std::rc::Rc<std::cell::RefCell<Pool>>,
pub(crate) rules: RuleSet,
- pub(crate) added_map: IndexMap<i64, Box<dyn PackageInterface>>,
- pub(crate) added_packages_by_names: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
+ pub(crate) added_map: IndexMap<i64, PackageInterfaceHandle>,
+ pub(crate) added_packages_by_names: IndexMap<String, Vec<PackageInterfaceHandle>>,
}
impl RuleSetGenerator {
@@ -52,8 +50,8 @@ impl RuleSetGenerator {
/// one requirement of the package A.
fn create_require_rule(
&self,
- package: &dyn PackageInterface,
- providers: &[Box<dyn PackageInterface>],
+ package: &PackageInterfaceHandle,
+ providers: &[PackageInterfaceHandle],
reason: i64,
reason_data: PhpMixed,
) -> Option<GenericRule> {
@@ -61,10 +59,7 @@ impl RuleSetGenerator {
for provider in providers {
// self fulfilling rule?
- if std::ptr::eq(
- provider.as_ref() as *const dyn PackageInterface,
- package as *const dyn PackageInterface,
- ) {
+ if provider.ptr_eq(package) {
return None;
}
literals.push(provider.get_id());
@@ -83,7 +78,7 @@ impl RuleSetGenerator {
/// set of packages is empty an impossible rule is generated.
fn create_install_one_of_rule(
&self,
- packages: &[Box<dyn PackageInterface>],
+ packages: &[PackageInterfaceHandle],
reason: i64,
reason_data: PhpMixed,
) -> GenericRule {
@@ -97,16 +92,13 @@ impl RuleSetGenerator {
/// and B the provider.
fn create_rule2_literals(
&self,
- issuer: &dyn PackageInterface,
- provider: &dyn PackageInterface,
+ issuer: &PackageInterfaceHandle,
+ provider: &PackageInterfaceHandle,
reason: i64,
reason_data: PhpMixed,
) -> Option<Rule2Literals> {
// ignore self conflict
- if std::ptr::eq(
- issuer as *const dyn PackageInterface,
- provider as *const dyn PackageInterface,
- ) {
+ if issuer.ptr_eq(provider) {
return None;
}
@@ -120,7 +112,7 @@ impl RuleSetGenerator {
fn create_multi_conflict_rule(
&self,
- packages: &[Box<dyn PackageInterface>],
+ packages: &[PackageInterfaceHandle],
reason: i64,
reason_data: PhpMixed,
) -> Rule {
@@ -152,10 +144,10 @@ impl RuleSetGenerator {
pub(crate) fn add_rules_for_package(
&mut self,
- package: Box<dyn PackageInterface>,
+ package: PackageInterfaceHandle,
platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
) {
- let mut work_queue: VecDeque<Box<dyn PackageInterface>> = VecDeque::new();
+ let mut work_queue: VecDeque<PackageInterfaceHandle> = VecDeque::new();
work_queue.push_back(package);
while let Some(package) = work_queue.pop_front() {
@@ -163,26 +155,25 @@ impl RuleSetGenerator {
continue;
}
- self.added_map
- .insert(package.get_id(), package.clone_package_box());
+ self.added_map.insert(package.get_id(), package.clone());
- let is_alias = package.as_any().downcast_ref::<AliasPackage>().is_some();
+ let is_alias = package.as_alias().is_some();
if !is_alias {
for name in package.get_names(false) {
self.added_packages_by_names
.entry(name)
.or_default()
- .push(package.clone_package_box());
+ .push(package.clone());
}
} else {
- let alias_pkg = package.as_any().downcast_ref::<AliasPackage>().unwrap();
+ let alias_pkg = package.as_alias().unwrap();
- work_queue.push_back(alias_pkg.get_alias_of().clone_package_box());
- let alias_of = alias_pkg.get_alias_of();
+ let alias_of: PackageInterfaceHandle = alias_pkg.get_alias_of().into();
+ work_queue.push_back(alias_of.clone());
let rule = self.create_require_rule(
- &*package,
- &[alias_of.clone_package_box()],
+ &package,
+ &[alias_of.clone()],
rule::RULE_PACKAGE_ALIAS,
PhpMixed::Null, // reasonData: $package (BasePackage)
);
@@ -190,8 +181,8 @@ impl RuleSetGenerator {
// 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_package_box()],
+ &alias_of,
+ &[package.clone()],
rule::RULE_PACKAGE_INVERSE_ALIAS,
PhpMixed::Null, // reasonData: $package->getAliasOf() (BasePackage)
);
@@ -218,16 +209,16 @@ impl RuleSetGenerator {
.unwrap_or(fallback);
}
- let possible_requires: Vec<Box<dyn PackageInterface>> = self
+ let possible_requires: Vec<PackageInterfaceHandle> = self
.pool
.borrow_mut()
.what_provides(link.get_target(), Some(&constraint))
.into_iter()
- .map(|p| p.clone_package_box())
+ .map(|p| p.into())
.collect();
let rule = self.create_require_rule(
- &*package,
+ &package,
&possible_requires,
rule::RULE_PACKAGE_REQUIRES,
PhpMixed::Null, // reasonData: $link (Link)
@@ -245,11 +236,7 @@ impl RuleSetGenerator {
&mut self,
platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
) {
- let packages: Vec<Box<dyn PackageInterface>> = self
- .added_map
- .values()
- .map(|p| p.clone_package_box())
- .collect();
+ let packages: Vec<PackageInterfaceHandle> = self.added_map.values().cloned().collect();
for package in &packages {
for link in package.get_conflicts().values() {
@@ -271,22 +258,24 @@ impl RuleSetGenerator {
.unwrap_or(fallback);
}
- let conflicts = self
+ let conflicts: Vec<PackageInterfaceHandle> = self
.pool
.borrow_mut()
- .what_provides(link.get_target(), Some(&constraint));
+ .what_provides(link.get_target(), Some(&constraint))
+ .into_iter()
+ .map(|p| p.into())
+ .collect();
for conflict in &conflicts {
// 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().downcast_ref::<AliasPackage>().is_some();
+ let conflict_is_alias = conflict.as_alias().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(
- &**package,
- &**conflict,
+ package,
+ conflict,
rule::RULE_PACKAGE_CONFLICT,
PhpMixed::Null, // reasonData: $link (Link)
);
@@ -296,10 +285,10 @@ impl RuleSetGenerator {
}
}
- let names_packages: Vec<(String, Vec<Box<dyn PackageInterface>>)> = self
+ let names_packages: Vec<(String, Vec<PackageInterfaceHandle>)> = self
.added_packages_by_names
.iter()
- .map(|(k, v)| (k.clone(), v.iter().map(|p| p.clone_package_box()).collect()))
+ .map(|(k, v)| (k.clone(), v.iter().cloned().collect()))
.collect();
for (name, packages) in names_packages {
@@ -320,11 +309,9 @@ impl RuleSetGenerator {
for package in request.get_fixed_packages().values() {
if package.get_id() == -1 {
// fixed package was not added to the pool as it did not pass the stability requirements, this is fine
- if self
- .pool
- .borrow()
- .is_unacceptable_fixed_or_locked_package(package.as_ref())
- {
+ // TODO(phase-c): wire Pool::is_unacceptable_fixed_or_locked_package(package) here;
+ // the package handle and the pool's identity check are now both handle-based.
+ if todo!("is_unacceptable_fixed_or_locked_package with a request package handle") {
continue;
}
@@ -338,7 +325,7 @@ impl RuleSetGenerator {
}));
}
- self.add_rules_for_package(package.clone_box(), platform_requirement_filter);
+ self.add_rules_for_package(package.clone().into(), platform_requirement_filter);
let mut reason_data: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
reason_data.insert(
@@ -346,7 +333,7 @@ impl RuleSetGenerator {
Box::new(PhpMixed::Null), // reasonData: $package (BasePackage)
);
let rule = self.create_install_one_of_rule(
- &[package.clone_package_box()],
+ &[package.clone().into()],
rule::RULE_FIXED,
PhpMixed::Array(reason_data),
);
@@ -367,19 +354,16 @@ impl RuleSetGenerator {
.unwrap_or(fallback);
}
- let packages: Vec<Box<dyn PackageInterface>> = self
+ let packages: Vec<PackageInterfaceHandle> = self
.pool
.borrow_mut()
.what_provides(package_name, Some(&constraint))
.into_iter()
- .map(|p| p.clone_package_box())
+ .map(|p| p.into())
.collect();
if !packages.is_empty() {
for package in &packages {
- self.add_rules_for_package(
- package.clone_package_box(),
- platform_requirement_filter,
- );
+ self.add_rules_for_package(package.clone(), platform_requirement_filter);
}
let mut reason_data: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
@@ -407,19 +391,19 @@ impl RuleSetGenerator {
&mut self,
platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
) {
- let packages: Vec<Box<dyn BasePackage>> = self
+ let packages: Vec<PackageInterfaceHandle> = self
.pool
.borrow()
.get_packages()
.iter()
- .map(|p| p.clone_box())
+ .map(|p| p.clone().into())
.collect();
for package in &packages {
// ensure that rules for root alias packages and aliases of packages which were loaded are also loaded
// 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().downcast_ref::<AliasPackage>();
+ let as_alias = package.as_alias();
if is_not_added {
if let Some(alias_pkg) = as_alias {
if alias_pkg.is_root_package_alias()
@@ -427,10 +411,7 @@ impl RuleSetGenerator {
.added_map
.contains_key(&alias_pkg.get_alias_of().get_id())
{
- self.add_rules_for_package(
- package.clone_package_box(),
- platform_requirement_filter,
- );
+ self.add_rules_for_package(package.clone(), platform_requirement_filter);
}
}
}
diff --git a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs
index 4daf641..ba2cf09 100644
--- a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs
+++ b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs
@@ -31,12 +31,11 @@ impl SecurityAdvisoryPoolFilter {
repositories: Vec<Box<dyn RepositoryInterface>>,
request: &Request,
) -> Pool {
- // TODO(phase-b): port the filter() body. Blockers:
+ // TODO(phase-c): port the filter() body. Blockers:
// * RepositorySet::new takes 6 args; ConfigSourceInterface refactor pending
- // * pool.get_packages() yields Box<dyn BasePackage>, but the audit/repo APIs
- // expect Box<dyn PackageInterface>; needs trait-object coercion / cloning story
- // * Pool::new requires owned Vecs, but existing pool's getters return refs and
- // Box<dyn BasePackage> is not Clone (only clone_box).
+ // * pool.get_packages() yields BasePackageHandle; widen to PackageInterfaceHandle
+ // (via .into()) where the audit/repo APIs expect PackageInterface.
+ // * Pool::new requires owned Vecs; clone the handles out of the existing pool.
// * advisory map element type mismatch (PhpMixed vs PartialSecurityAdvisory).
let _ = (
pool,
diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs
index 820f792..2610f07 100644
--- a/crates/shirabe/src/dependency_resolver/solver.rs
+++ b/crates/shirabe/src/dependency_resolver/solver.rs
@@ -29,7 +29,7 @@ use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFil
use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
use crate::io::IOInterface;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
#[derive(Debug)]
pub struct Solver {
@@ -40,7 +40,7 @@ pub struct Solver {
pub(crate) watch_graph: RuleWatchGraph,
pub(crate) decisions: Decisions,
- pub(crate) fixed_map: IndexMap<i64, Box<dyn BasePackage>>,
+ pub(crate) fixed_map: IndexMap<i64, BasePackageHandle>,
pub(crate) propagate_index: i64,
/// Pairs of `(literals, level)` — PHP indexes into these with the BRANCH_* constants.
@@ -176,7 +176,7 @@ impl Solver {
fn setup_fixed_map(&mut self, request: &Request) {
self.fixed_map = IndexMap::new();
for (_, package) in request.get_fixed_packages() {
- self.fixed_map.insert(package.get_id(), package.clone_box());
+ self.fixed_map.insert(package.get_id(), package.clone());
}
}
@@ -296,13 +296,22 @@ impl Solver {
return Err(anyhow::anyhow!("solver problems"));
}
- // TODO(phase-b): LockTransaction expects IndexMap<_, Box<dyn PackageInterface>>
- // and borrows Pool/Decisions. The present/fixed maps from Request are keyed
- // by BasePackage; converting requires reworking Request.
+ // LockTransaction stores PackageInterfaceHandle maps; widen the request's BasePackageHandle
+ // maps into them.
+ let present_map = request
+ .get_present_map(false)
+ .into_iter()
+ .map(|(k, v)| (k, v.into()))
+ .collect();
+ let unlockable_map = request
+ .get_fixed_packages_map()
+ .into_iter()
+ .map(|(k, v)| (k, v.into()))
+ .collect();
Ok(LockTransaction::new(
&*self.pool.borrow(),
- todo!("convert request.get_present_map(false) to PackageInterface map"),
- todo!("convert request.get_fixed_packages_map() to PackageInterface map"),
+ present_map,
+ unlockable_map,
&self.decisions,
))
}
diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs
index a704c67..8cf5d5b 100644
--- a/crates/shirabe/src/dependency_resolver/transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/transaction.rs
@@ -1,11 +1,8 @@
//! ref: composer/src/Composer/DependencyResolver/Transaction.php
-use std::any::Any;
-
use indexmap::IndexMap;
use shirabe_php_shim::{
- PhpMixed, array_filter, array_intersect, array_keys, array_pop, array_unshift, spl_object_hash,
- strcmp, uasort,
+ PhpMixed, array_filter, array_intersect, array_keys, array_pop, array_unshift, strcmp, uasort,
};
use crate::dependency_resolver::operation::InstallOperation;
@@ -14,9 +11,8 @@ use crate::dependency_resolver::operation::MarkAliasUninstalledOperation;
use crate::dependency_resolver::operation::OperationInterface;
use crate::dependency_resolver::operation::UninstallOperation;
use crate::dependency_resolver::operation::UpdateOperation;
-use crate::package::AliasPackage;
use crate::package::Link;
-use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::PlatformRepository;
/// @internal
@@ -27,14 +23,14 @@ pub struct Transaction {
/// Packages present at the beginning of the transaction
/// @var PackageInterface[]
- pub(crate) present_packages: Vec<Box<dyn PackageInterface>>,
+ pub(crate) present_packages: Vec<PackageInterfaceHandle>,
/// Package set resulting from this transaction
/// @var array<string, PackageInterface>
- pub(crate) result_package_map: IndexMap<String, Box<dyn PackageInterface>>,
+ pub(crate) result_package_map: IndexMap<String, PackageInterfaceHandle>,
/// @var array<string, PackageInterface[]>
- pub(crate) result_packages_by_name: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
+ pub(crate) result_packages_by_name: IndexMap<String, Vec<PackageInterfaceHandle>>,
}
impl Default for Transaction {
@@ -52,8 +48,8 @@ impl Transaction {
/// @param PackageInterface[] $presentPackages
/// @param PackageInterface[] $resultPackages
pub fn new(
- present_packages: Vec<Box<dyn PackageInterface>>,
- result_packages: Vec<Box<dyn PackageInterface>>,
+ present_packages: Vec<PackageInterfaceHandle>,
+ result_packages: Vec<PackageInterfaceHandle>,
) -> Self {
let mut this = Self {
operations: vec![],
@@ -72,23 +68,23 @@ impl Transaction {
}
/// @param PackageInterface[] $resultPackages
- fn set_result_package_maps(&mut self, result_packages: Vec<Box<dyn PackageInterface>>) {
+ fn set_result_package_maps(&mut self, result_packages: Vec<PackageInterfaceHandle>) {
// PHP: static function (PackageInterface $a, PackageInterface $b): int { ... };
// TODO(phase-b): bridge the closure to uasort's argument type
- let _package_sort = |a: &dyn PackageInterface, b: &dyn PackageInterface| -> i64 {
+ let _package_sort = |a: &PackageInterfaceHandle, b: &PackageInterfaceHandle| -> 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().downcast_ref::<AliasPackage>().is_some();
- let b_is_alias = b.as_any().downcast_ref::<AliasPackage>().is_some();
+ let a_is_alias = a.as_alias().is_some();
+ let b_is_alias = b.as_alias().is_some();
if a_is_alias != b_is_alias {
return if a_is_alias { -1 } else { 1 };
}
// if names are the same, compare version, e.g. to sort aliases reliably, actual order does not matter
- return strcmp(b.get_version(), a.get_version());
+ return strcmp(&b.get_version(), &a.get_version());
}
- strcmp(b.get_name(), a.get_name())
+ strcmp(&b.get_name(), &a.get_name())
};
self.result_package_map = IndexMap::new();
@@ -97,10 +93,10 @@ impl Transaction {
self.result_packages_by_name
.entry(name)
.or_insert_with(Vec::new)
- .push(package.clone_package_box());
+ .push(package.clone());
}
self.result_package_map
- .insert(spl_object_hash(package.as_ref()), package);
+ .insert(package.ptr_id().to_string(), package);
}
// TODO(phase-b): uasort signature mismatch — needs to operate on the IndexMap with a PackageInterface comparator
@@ -121,24 +117,23 @@ impl Transaction {
pub(crate) fn calculate_operations(&mut self) -> Vec<Box<dyn OperationInterface>> {
let mut operations: Vec<Box<dyn OperationInterface>> = vec![];
- let mut present_package_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
- let mut remove_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
- let mut present_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
- let mut remove_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ let mut present_package_map: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
+ let mut remove_map: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
+ let mut present_alias_map: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
+ let mut remove_alias_map: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
for package in &self.present_packages {
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().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());
+ present_alias_map.insert(key.clone(), package.clone());
+ remove_alias_map.insert(key, package.clone());
} else {
- present_package_map
- .insert(package.get_name().to_string(), package.clone_package_box());
- remove_map.insert(package.get_name().to_string(), package.clone_package_box());
+ present_package_map.insert(package.get_name().to_string(), package.clone());
+ remove_map.insert(package.get_name().to_string(), package.clone());
}
}
// PHP: $stack = $this->getRootPackages();
- let mut stack: Vec<Box<dyn PackageInterface>> =
+ let mut stack: Vec<PackageInterfaceHandle> =
self.get_root_packages().into_values().collect();
let mut visited: IndexMap<String, bool> = IndexMap::new();
@@ -147,16 +142,16 @@ impl Transaction {
while !stack.is_empty() {
let package = array_pop(&mut stack).unwrap();
- if processed.contains_key(&spl_object_hash(package.as_ref())) {
+ if processed.contains_key(&package.ptr_id().to_string()) {
continue;
}
- if !visited.contains_key(&spl_object_hash(package.as_ref())) {
- visited.insert(spl_object_hash(package.as_ref()), true);
+ if !visited.contains_key(&package.ptr_id().to_string()) {
+ visited.insert(package.ptr_id().to_string(), true);
- stack.push(package.clone_package_box());
- if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
- stack.push(alias.get_alias_of().clone_package_box());
+ stack.push(package.clone());
+ if let Some(alias) = package.as_alias() {
+ stack.push(alias.get_alias_of().into());
} else {
for link in package.get_requires().values() {
let possible_requires = self.get_providers_in_result(link);
@@ -166,10 +161,10 @@ impl Transaction {
}
}
}
- } else if !processed.contains_key(&spl_object_hash(package.as_ref())) {
- processed.insert(spl_object_hash(package.as_ref()), true);
+ } else if !processed.contains_key(&package.ptr_id().to_string()) {
+ processed.insert(package.ptr_id().to_string(), true);
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().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);
@@ -179,18 +174,22 @@ impl Transaction {
"package as AliasPackage by value"
))));
}
- } else if let Some(source) = present_package_map.get(package.get_name()) {
+ } else if let Some(source) = present_package_map.get(&package.get_name()).cloned() {
// do we need to update?
// TODO different for lock?
- let present = present_package_map.get(package.get_name()).unwrap();
- // TODO(phase-b): downcast to CompletePackageInterface trait object
- let package_is_complete = false;
- let present_is_complete = false;
+ let present = present_package_map.get(&package.get_name()).unwrap();
+ // PHP: $package instanceof CompletePackageInterface
+ // && $presentPackageMap[$package->getName()] instanceof CompletePackageInterface
+ // && ($package->isAbandoned() !== $presentPackageMap[...]->isAbandoned()
+ // || $package->getReplacementPackage() !== $presentPackageMap[...]->getReplacementPackage())
let abandoned_or_replacement_changed =
- package_is_complete && present_is_complete && {
- // PHP: $package->isAbandoned() !== $presentPackageMap[$package->getName()]->isAbandoned()
- // || $package->getReplacementPackage() !== $presentPackageMap[$package->getName()]->getReplacementPackage()
- todo!("compare abandoned/replacement across CompletePackageInterface")
+ match (package.as_complete(), present.as_complete()) {
+ (Some(package), Some(present)) => {
+ package.is_abandoned() != present.is_abandoned()
+ || package.get_replacement_package()
+ != present.get_replacement_package()
+ }
+ _ => false,
};
if package.get_version() != present.get_version()
|| package.get_dist_reference() != present.get_dist_reference()
@@ -198,14 +197,14 @@ impl Transaction {
|| abandoned_or_replacement_changed
{
operations.push(Box::new(UpdateOperation::new(
- source.clone_package_box(),
- package.clone_package_box(),
+ source.clone(),
+ package.clone(),
)));
}
- remove_map.shift_remove(package.get_name());
+ remove_map.shift_remove(&package.get_name());
} else {
- operations.push(Box::new(InstallOperation::new(package.clone_package_box())));
- remove_map.shift_remove(package.get_name());
+ operations.push(Box::new(InstallOperation::new(package.clone())));
+ remove_map.shift_remove(&package.get_name());
}
}
}
@@ -244,11 +243,11 @@ impl Transaction {
/// If there are packages with a cycle on the top level the package with the lowest name gets picked
///
/// @return array<string, PackageInterface>
- pub(crate) fn get_root_packages(&self) -> IndexMap<String, Box<dyn PackageInterface>> {
- let mut roots: IndexMap<String, Box<dyn PackageInterface>> = self
+ pub(crate) fn get_root_packages(&self) -> IndexMap<String, PackageInterfaceHandle> {
+ let mut roots: IndexMap<String, PackageInterfaceHandle> = self
.result_package_map
.iter()
- .map(|(k, v)| (k.clone(), v.clone_package_box()))
+ .map(|(k, v)| (k.clone(), v.clone()))
.collect();
for (package_hash, package) in &self.result_package_map {
@@ -261,8 +260,8 @@ impl Transaction {
for require in possible_requires {
// PHP: if ($require !== $package) — strict reference inequality
- if spl_object_hash(require.as_ref()) != spl_object_hash(package.as_ref()) {
- roots.shift_remove(&spl_object_hash(require.as_ref()));
+ if require.ptr_id().to_string() != package.ptr_id().to_string() {
+ roots.shift_remove(&require.ptr_id().to_string());
}
}
}
@@ -272,12 +271,12 @@ impl Transaction {
}
/// @return PackageInterface[]
- pub(crate) fn get_providers_in_result(&self, link: &Link) -> Vec<Box<dyn PackageInterface>> {
+ pub(crate) fn get_providers_in_result(&self, link: &Link) -> Vec<PackageInterfaceHandle> {
let Some(packages) = self.result_packages_by_name.get(link.get_target()) else {
return vec![];
};
- packages.iter().map(|p| p.clone_package_box()).collect()
+ packages.iter().cloned().collect()
}
/// Workaround: if your packages depend on plugins, we must be sure
@@ -308,12 +307,12 @@ impl Transaction {
for idx in (0..operations.len()).rev() {
let op = &operations[idx];
- let package: Box<dyn PackageInterface> = if let Some(install_op) =
+ let package: PackageInterfaceHandle = if let Some(install_op) =
op.as_ref().as_any().downcast_ref::<InstallOperation>()
{
- install_op.get_package().clone_package_box()
+ install_op.get_package().clone()
} else if let Some(update_op) = op.as_ref().as_any().downcast_ref::<UpdateOperation>() {
- update_op.get_target_package().clone_package_box()
+ update_op.get_target_package().clone()
} else {
continue;
};
diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
index f4575ce..ae86fcf 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -31,7 +31,6 @@ use crate::installer::InstallerEvent;
use crate::installer::PackageEvent;
use crate::io::ConsoleIO;
use crate::io::IOInterface;
-use crate::package::PackageInterface;
use crate::plugin::CommandEvent;
use crate::plugin::PreCommandRunEvent;
use crate::repository::RepositoryInterface;
@@ -1260,7 +1259,9 @@ impl EventDispatcher {
let generator = generator.borrow();
let mut hash_input = packages
.iter()
- .map(|p: &Box<dyn PackageInterface>| format!("{}/{}", p.get_name(), p.get_version()))
+ .map(|p: &crate::package::PackageInterfaceHandle| {
+ format!("{}/{}", p.get_name(), p.get_version())
+ })
.collect::<Vec<_>>()
.join(",");
// TODO(plugin): polymorphic isDevMode propagation for ScriptEvent / PackageEvent / InstallerEvent
@@ -1278,11 +1279,19 @@ impl EventDispatcher {
// Composer is &Composer here so we cannot take a mut borrow. Defer until shared ownership.
let _ = &generator;
let _ = packages;
- let package_map: Vec<(Box<dyn PackageInterface>, Option<String>)> =
+ let package_map: Vec<(crate::package::PackageInterfaceHandle, Option<String>)> =
todo!("build_package_map requires &mut InstallationManager");
// TODO(phase-b): parse_autoloads also expects the filtered dev packages list
// (PhpMixed in this port).
- let map = generator.parse_autoloads(package_map, package, shirabe_php_shim::PhpMixed::Null);
+ let map = generator.parse_autoloads(
+ package_map,
+ package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
+ shirabe_php_shim::PhpMixed::Null,
+ );
if self.loader.is_some() {
self.loader.as_mut().unwrap().unregister();
diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs
index 35afd5d..1370080 100644
--- a/crates/shirabe/src/factory.rs
+++ b/crates/shirabe/src/factory.rs
@@ -47,6 +47,7 @@ use crate::json::JsonFile;
use crate::json::JsonValidationException;
use crate::package::Locker;
use crate::package::RootPackageInterface;
+use crate::package::RootPackageInterfaceHandle;
use crate::package::archiver::ArchiveManager;
use crate::package::archiver::PharArchiver;
use crate::package::archiver::ZipArchiver;
@@ -690,7 +691,7 @@ impl Factory {
io,
&mut rm.borrow_mut(),
&vendor_dir,
- composer.get_package(),
+ composer.get_package().clone(),
Some(&process),
);
composer.set_repository_manager(rm.clone());
@@ -871,7 +872,7 @@ impl Factory {
io: &dyn IOInterface,
rm: &mut RepositoryManager,
vendor_dir: &str,
- root_package: &dyn RootPackageInterface,
+ root_package: RootPackageInterfaceHandle,
process: Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) {
let fs = process
@@ -886,7 +887,7 @@ impl Factory {
)
.expect("installed.json path is always valid"),
true,
- Some(RootPackageInterface::clone_box(root_package)),
+ Some(root_package),
fs,
)
.expect("InstalledFilesystemRepository::new should not fail"),
@@ -1237,8 +1238,7 @@ impl Factory {
im: &mut InstallationManager,
) -> anyhow::Result<()> {
for package in repo.get_packages() {
- if !im.is_package_installed(repo, package.as_ref())? {
- // TODO(phase-b): mutable access on repo trait object
+ if !im.is_package_installed(repo, &package)? {
let _ = package;
}
}
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index 8b68a78..c3ef76f 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -76,8 +76,10 @@ use crate::package::Link;
use crate::package::Locker;
use crate::package::Package;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootAliasPackage;
use crate::package::RootPackageInterface;
+use crate::package::RootPackageInterfaceHandle;
use crate::package::base_package::{self, BasePackage};
use crate::package::dumper::ArrayDumper;
use crate::package::loader::ArrayLoader;
@@ -104,9 +106,9 @@ use shirabe_semver::constraint::SimpleConstraint;
pub struct Installer {
pub(crate) io: Box<dyn IOInterface>,
pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
- pub(crate) package: Box<dyn RootPackageInterface>,
+ pub(crate) package: RootPackageInterfaceHandle,
// TODO can we get rid of the below and just use the package itself?
- pub(crate) fixed_root_package: Box<dyn RootPackageInterface>,
+ pub(crate) fixed_root_package: RootPackageInterfaceHandle,
pub(crate) download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
pub(crate) repository_manager: std::rc::Rc<std::cell::RefCell<RepositoryManager>>,
pub(crate) locker: std::rc::Rc<std::cell::RefCell<Locker>>,
@@ -161,7 +163,7 @@ impl Installer {
pub fn new(
io: Box<dyn IOInterface>,
config: std::rc::Rc<std::cell::RefCell<Config>>,
- package: Box<dyn RootPackageInterface>,
+ package: RootPackageInterfaceHandle,
download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
repository_manager: std::rc::Rc<std::cell::RefCell<RepositoryManager>>,
locker: std::rc::Rc<std::cell::RefCell<Locker>>,
@@ -176,8 +178,8 @@ impl Installer {
Self {
io,
config,
- package: package.clone_box(),
- fixed_root_package: package.clone_box(),
+ package: package.clone(),
+ fixed_root_package: package.clone(),
download_manager,
repository_manager,
locker,
@@ -353,11 +355,13 @@ impl Installer {
let installed_repo = InstalledRepository::new(vec![
locked_repository_box,
Box::new(self.create_platform_repo(false)),
- Box::new(RootPackageRepository::new(self.package.clone_box())),
+ Box::new(RootPackageRepository::new(self.package.clone())),
]);
if is_fresh_install {
self.suggested_packages_reporter
- .add_suggestions_from_package(&*self.package);
+ .add_suggestions_from_package(
+ self.package.as_rc().borrow().as_package_interface(),
+ );
}
self.suggested_packages_reporter
.output_minimalistic(Some(&installed_repo), None);
@@ -408,7 +412,11 @@ impl Installer {
self.autoload_generator.borrow_mut().dump(
&*self.config.borrow(),
self.repository_manager.borrow().get_local_repository(),
- &*self.package,
+ self.package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
&mut *self.installation_manager.borrow_mut(),
"composer",
self.optimize_autoloader,
@@ -423,9 +431,11 @@ impl Installer {
let repository_manager = self.repository_manager.clone();
let repository_manager = repository_manager.borrow();
for package in repository_manager.get_local_repository().get_packages() {
+ // TODO(phase-c): InstallationManager APIs still take &dyn PackageInterface; bridge
+ // the handle until they migrate to handles.
self.installation_manager
.borrow_mut()
- .ensure_binaries_presence(&*package);
+ .ensure_binaries_presence(package.as_rc().borrow().as_package_interface());
}
}
@@ -443,8 +453,8 @@ impl Installer {
let repository_manager = self.repository_manager.clone();
let repository_manager = repository_manager.borrow();
for package in repository_manager.get_local_repository().get_packages() {
- if let Some(cp) = package.as_complete_package_interface() {
- if package.as_alias_package().is_none() && !cp.get_funding().is_empty() {
+ if let Some(cp) = package.as_complete() {
+ if package.as_alias().is_none() && !cp.get_funding().is_empty() {
funding_count += 1;
}
}
@@ -622,8 +632,13 @@ impl Installer {
repository_set.add_repository(lr.clone_box())?;
}
+ let fixed_root_package = self.fixed_root_package.clone();
let mut request = self.create_request(
- &*self.fixed_root_package,
+ fixed_root_package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
&platform_repo,
locked_repository.as_ref(),
);
@@ -819,7 +834,9 @@ impl Installer {
// collect suggestions
if let Some(io) = operation.as_install_operation() {
self.suggested_packages_reporter
- .add_suggestions_from_package(&*io.get_package());
+ .add_suggestions_from_package(
+ io.get_package().as_rc().borrow().as_package_interface(),
+ );
}
// output op if lock file is enabled, but alias op only in debug verbosity
@@ -836,13 +853,9 @@ impl Installer {
if self.io.is_very_verbose()
&& strpos(&operation.get_operation_type(), "Alias").is_none()
{
- let operation_pkg: Box<dyn PackageInterface> =
- if let Some(uo) = operation.as_update_operation() {
- uo.get_target_package().clone_package_box()
- } else {
- operation.get_package().clone_package_box()
- };
- if let Some(repo) = operation_pkg.get_repository() {
+ // TODO(phase-c): package->repository back-reference not yet on handles
+ let repository: Option<&dyn RepositoryInterface> = None;
+ if let Some(repo) = repository {
source_repo = format!(" from {}", repo.get_repo_name());
}
}
@@ -877,8 +890,8 @@ impl Installer {
platform_reqs,
platform_dev_reqs,
aliases_php_mixed,
- self.package.get_minimum_stability(),
- self.package.get_stability_flags().clone(),
+ &self.package.get_minimum_stability(),
+ self.package.get_stability_flags(),
self.prefer_stable || self.package.get_prefer_stable(),
self.prefer_lowest,
platform_overrides,
@@ -915,16 +928,24 @@ impl Installer {
let dumper = ArrayDumper::new();
for pkg in lock_transaction.get_new_lock_packages(false, false) {
let loaded = loader.load(
- dumper.dump(&*pkg),
+ dumper.dump(pkg.as_rc().borrow().as_package_interface()),
Some("Composer\\Package\\CompletePackage".to_string()),
)?;
- result_repo.add_package(loaded.clone_package_box())?;
+ result_repo.add_package(loaded)?;
}
let mut repository_set = self.create_repository_set(true, platform_repo, aliases, None);
repository_set.add_repository(Box::new(result_repo))?;
- let mut request = self.create_request(&*self.fixed_root_package, platform_repo, None);
+ let mut request = self.create_request(
+ self.fixed_root_package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
+ platform_repo,
+ None,
+ );
self.require_packages_for_update(&mut request, locked_repository, false)?;
let pool = repository_set.create_pool_with_all_packages()?;
@@ -1005,8 +1026,13 @@ impl Installer {
repository_set.add_repository(locked_repository.clone_box())?;
// creating requirements request
+ let fixed_root_package = self.fixed_root_package.clone();
let mut request = self.create_request(
- &*self.fixed_root_package,
+ fixed_root_package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
&platform_repo,
Some(&locked_repository),
);
@@ -1019,10 +1045,15 @@ impl Installer {
);
}
- let missing_requirement_info = self
- .locker
- .borrow_mut()
- .get_missing_requirement_info(&*self.package, self.dev_mode)?;
+ let package_for_missing = self.package.clone();
+ let missing_requirement_info = self.locker.borrow_mut().get_missing_requirement_info(
+ package_for_missing
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
+ self.dev_mode,
+ )?;
if !missing_requirement_info.is_empty() {
self.io.write_error(&missing_requirement_info.join("\n"));
@@ -1038,7 +1069,7 @@ impl Installer {
}
for package in RepositoryInterface::get_packages(&locked_repository) {
- request.fix_locked_package(package);
+ request.fix_locked_package(package.clone());
}
let mut root_requires = self.package.get_requires();
@@ -1316,16 +1347,17 @@ impl Installer {
root_requires.insert(req, constraint);
}
- // TODO(phase-b): self.package is Box<dyn RootPackageInterface>; cannot clone a trait
- // object without Clone. PHP shares the reference. Skipping fixed_root_package assignment.
- // self.fixed_root_package = clone(&self.package);
+ // TODO(phase-c): PHP does `$this->fixedRootPackage = clone($this->package)` (a deep,
+ // fresh-identity copy) and then strips its requires below. A handle clone only shares the
+ // same Rc, so a real deep clone of the package is needed to avoid mutating self.package.
+ // self.fixed_root_package = deep_clone(&self.package);
self.fixed_root_package.set_requires(vec![]);
self.fixed_root_package.set_dev_requires(vec![]);
stability_flags.insert(
- self.package.get_name().to_string(),
+ self.package.get_name(),
base_package::STABILITIES
- [VersionParser::parse_stability(self.package.get_version()).as_str()],
+ [VersionParser::parse_stability(&self.package.get_version()).as_str()],
);
// TODO(phase-b): convert root_aliases (Vec<IndexMap<String, String>>) into Vec<RootAliasInput>
@@ -1405,18 +1437,18 @@ impl Installer {
if for_update && self.minimal_update && locked_repo.is_some() {
let mut versions: IndexMap<String, String> = IndexMap::new();
for pkg in CanonicalPackagesTrait::get_packages(locked_repo.unwrap()) {
- if pkg.as_alias_package().is_some()
+ if pkg.as_alias().is_some()
|| (self.update_allow_list.is_some()
&& self
.update_allow_list
.as_ref()
.unwrap()
.iter()
- .any(|s| s == pkg.get_name()))
+ .any(|s| s == &pkg.get_name()))
{
continue;
}
- versions.insert(pkg.get_name().to_string(), pkg.get_version().to_string());
+ versions.insert(pkg.get_name(), pkg.get_version());
}
preferred_versions = Some(versions);
}
@@ -1439,7 +1471,8 @@ impl Installer {
let _ = locked_repository;
let mut request = Request::new(None);
- // TODO(phase-b): request.fix_package wants Box<dyn BasePackage>; root_package is &dyn RootPackageInterface
+ // TODO(phase-c): request.fix_package wants a BasePackageHandle; root_package is only a
+ // borrowed &dyn RootPackageInterface here and cannot be lifted back into a handle.
let _ = root_package;
// request.fix_package(root_package);
if let Some(_alias) = root_package.as_any().downcast_ref::<RootAliasPackage>() {
@@ -1457,17 +1490,13 @@ impl Installer {
let provided = root_package.get_provides();
for package in fixed_packages {
// skip platform packages that are provided by the root package
- let pkg_repo_is_platform = match package.get_repository() {
- Some(r) => std::ptr::eq(
- r.as_any() as *const _ as *const u8,
- platform_repo.as_any() as *const _ as *const u8,
- ),
- None => false,
- };
+ // TODO(phase-c): the handle does not expose get_repository (a `RefCell`-borrowed
+ // back-reference); detecting the platform repo needs repository back-refs on handles.
+ let pkg_repo_is_platform = false;
if !pkg_repo_is_platform
- || !provided.contains_key(package.get_name())
+ || !provided.contains_key(&package.get_name())
|| !provided
- .get(package.get_name())
+ .get(&package.get_name())
.unwrap()
.get_constraint()
.matches(
@@ -1479,9 +1508,9 @@ impl Installer {
.into(),
)
{
- // TODO(phase-b): fix_package needs owned Box<dyn BasePackage>
+ // TODO(phase-c): wire up once the platform-repo detection above is implemented.
let _ = &package;
- // request.fix_package(&*package);
+ // request.fix_package(package.clone());
}
}
@@ -1515,15 +1544,15 @@ impl Installer {
for locked_package in CanonicalPackagesTrait::get_packages(locked_repository.unwrap()) {
// exclude alias packages here as for root aliases, both alias and aliased are
// present in the lock repo and we only want to require the aliased version
- if locked_package.as_alias_package().is_none()
- && !excluded_packages.contains_key(locked_package.get_name())
+ if locked_package.as_alias().is_none()
+ && !excluded_packages.contains_key(&locked_package.get_name())
{
request.require_name(
- locked_package.get_name(),
+ &locked_package.get_name(),
Some(
SimpleConstraint::new(
"==".to_string(),
- locked_package.get_version().to_string(),
+ locked_package.get_version(),
None,
)
.into(),
@@ -1576,21 +1605,21 @@ impl Installer {
///
/// This is to prevent any accidental modification of the existing repos on disk
fn mock_local_repositories(&self, rm: &mut RepositoryManager) {
- let mut packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ let mut packages: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
for package in rm.get_local_repository().get_packages() {
- packages.insert(package.to_string(), package.clone_box());
+ packages.insert(package.to_string(), package.clone().into());
}
let keys: Vec<String> = packages.keys().cloned().collect();
for key in keys {
- let package_clone = packages.get(&key).unwrap().clone_package_box();
- if let Some(alias_pkg) = package_clone.as_alias_package() {
+ let package_clone = packages.get(&key).unwrap().clone();
+ if let Some(alias_pkg) = package_clone.as_alias() {
let alias_key = alias_pkg.get_alias_of().to_string();
// TODO(phase-b): get_class on dyn PackageInterface; skipped because PhpMixed shim only
let _class_name = "Composer\\Package\\AliasPackage".to_string();
// PHP: $packages[$key] = new $className($packages[$alias], $package->getVersion(), $package->getPrettyVersion());
- // TODO(phase-b): AliasPackage::new expects Box<dyn BasePackage>; have Box<dyn PackageInterface>
- let _aliased = packages.get(&alias_key).unwrap().clone_package_box();
- let new_alias_package: Box<dyn PackageInterface> = todo!();
+ // TODO(phase-c): re-create the alias package over the mocked aliased handle.
+ let _aliased = packages.get(&alias_key).unwrap().clone();
+ let new_alias_package: PackageInterfaceHandle = todo!();
packages.insert(key, new_alias_package);
}
}
@@ -1653,7 +1682,7 @@ impl Installer {
Self::new(
io,
composer.get_config(),
- composer.get_package().clone_box(),
+ composer.get_package().clone(),
composer.get_download_manager(),
composer.get_repository_manager(),
composer.get_locker(),
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 7a05324..3e455b4 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -23,8 +23,8 @@ use crate::installer::PackageEvents;
use crate::installer::PluginInstaller;
use crate::io::ConsoleIO;
use crate::io::IOInterface;
-use crate::package::AliasPackage;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use crate::util::Platform;
use crate::util::r#loop::Loop;
@@ -37,7 +37,7 @@ pub struct InstallationManager {
/// @var array<string, InstallerInterface>
cache: IndexMap<String, Box<dyn InstallerInterface>>,
/// @var array<string, array<PackageInterface>>
- notifiable_packages: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
+ notifiable_packages: IndexMap<String, Vec<PackageInterfaceHandle>>,
loop_: std::rc::Rc<std::cell::RefCell<Loop>>,
io: Box<dyn IOInterface>,
event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
@@ -133,18 +133,19 @@ impl InstallationManager {
pub fn is_package_installed(
&mut self,
repo: &dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<bool> {
- // TODO(phase-b): $package instanceof AliasPackage downcast
- let package_as_alias: Option<&AliasPackage> = None;
- if let Some(alias) = package_as_alias {
- return Ok(repo.has_package(package)
- && self.is_package_installed(repo, alias.get_alias_of())?);
+ if let Some(alias) = package.as_alias() {
+ let alias_of: PackageInterfaceHandle = alias.get_alias_of().into();
+ return Ok(
+ repo.has_package(package.as_rc().borrow().as_package_interface())
+ && self.is_package_installed(repo, &alias_of)?,
+ );
}
Ok(self
- .get_installer(package.get_type())?
- .is_installed(repo, package))
+ .get_installer(&package.get_type())?
+ .is_installed(repo, package.as_rc().borrow().as_package_interface()))
}
/// Install binary for the given package.
@@ -311,9 +312,10 @@ impl InstallationManager {
let update_op: Option<&UpdateOperation> = None;
if op_type == "update" {
// @var UpdateOperation $operation
- if let Some(u) = update_op {
- package = u.get_target_package();
- initial_package = Some(u.get_initial_package());
+ if let Some(_u) = update_op {
+ // TODO(phase-c): bridge UpdateOperation handles (target/initial) into the
+ // &dyn PackageInterface that the installer APIs still expect.
+ continue;
} else {
continue;
}
@@ -451,9 +453,10 @@ impl InstallationManager {
let initial_package: Option<&dyn PackageInterface>;
let update_op: Option<&UpdateOperation> = None;
if op_type == "update" {
- if let Some(u) = update_op {
- package = u.get_target_package();
- initial_package = Some(u.get_initial_package());
+ if let Some(_u) = update_op {
+ // TODO(phase-c): bridge UpdateOperation handles (target/initial) into the
+ // &dyn PackageInterface that the installer APIs still expect.
+ continue;
} else {
continue;
}
@@ -537,10 +540,11 @@ impl InstallationManager {
repo: &mut dyn InstalledRepositoryInterface,
operation: &InstallOperation,
) -> Option<PhpMixed> {
- let package = operation.get_package();
- let installer = self.get_installer(package.get_type()).ok()?;
- let promise = installer.install(repo, package).await.ok()?;
- self.mark_for_notification(package);
+ let package = operation.get_package().clone();
+ let package_type = package.get_type();
+ let installer = self.get_installer(&package_type).ok()?;
+ let promise = installer.install(repo, &package).await.ok()?;
+ self.mark_for_notification(&package);
promise
}
@@ -553,27 +557,27 @@ impl InstallationManager {
repo: &mut dyn InstalledRepositoryInterface,
operation: &UpdateOperation,
) -> Option<PhpMixed> {
- let initial = operation.get_initial_package();
- let target = operation.get_target_package();
+ let initial = operation.get_initial_package().clone();
+ let target = operation.get_target_package().clone();
let initial_type = initial.get_type();
let target_type = target.get_type();
let promise = if initial_type == target_type {
- let installer = self.get_installer(initial_type).ok()?;
- let promise = installer.update(repo, initial, target).await.ok()?;
- self.mark_for_notification(target);
+ let installer = self.get_installer(&initial_type).ok()?;
+ let promise = installer.update(repo, &initial, &target).await.ok()?;
+ self.mark_for_notification(&target);
promise
} else {
// PHP: uninstall initial, then install target via the target-type installer.
let _ = self
- .get_installer(initial_type)
+ .get_installer(&initial_type)
.ok()?
- .uninstall(repo, initial)
+ .uninstall(repo, &initial)
.await
.ok()?;
- let installer = self.get_installer(target_type).ok()?;
- installer.install(repo, target).await.ok()?
+ let installer = self.get_installer(&target_type).ok()?;
+ installer.install(repo, &target).await.ok()?
};
promise
@@ -587,10 +591,11 @@ impl InstallationManager {
repo: &mut dyn InstalledRepositoryInterface,
operation: &UninstallOperation,
) -> Option<PhpMixed> {
- let package = operation.get_package();
- let installer = self.get_installer(package.get_type()).ok()?;
+ let package = operation.get_package().clone();
+ let package_type = package.get_type();
+ let installer = self.get_installer(&package_type).ok()?;
- installer.uninstall(repo, package).await.ok()?
+ installer.uninstall(repo, &package).await.ok()?
}
/// Executes markAliasInstalled operation.
@@ -602,7 +607,10 @@ impl InstallationManager {
let package = operation.get_package();
if !repo.has_package(package) {
- repo.add_package(package.clone_package_box());
+ // TODO(phase-c): MarkAliasInstalledOperation::get_package() yields a borrowed
+ // &AliasPackage; add_package now wants a shared PackageInterfaceHandle.
+ let package_handle: PackageInterfaceHandle = todo!();
+ repo.add_package(package_handle);
}
}
@@ -638,17 +646,11 @@ impl InstallationManager {
// non-batch API, deprecated
if str_contains(repo_url, "%package%") {
for package in packages {
- let url = str_replace("%package%", package.get_pretty_name(), repo_url);
+ let url = str_replace("%package%", &package.get_pretty_name(), repo_url);
let mut params: IndexMap<String, String> = IndexMap::new();
- params.insert(
- "version".to_string(),
- package.get_pretty_version().to_string(),
- );
- params.insert(
- "version_normalized".to_string(),
- package.get_version().to_string(),
- );
+ params.insert("version".to_string(), package.get_pretty_version());
+ params.insert("version_normalized".to_string(), package.get_version());
let mut opts: IndexMap<String, PhpMixed> = IndexMap::new();
opts.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false));
let mut http: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -693,15 +695,15 @@ impl InstallationManager {
let mut package_notification: IndexMap<String, PhpMixed> = IndexMap::new();
package_notification.insert(
"name".to_string(),
- PhpMixed::String(package.get_pretty_name().to_string()),
+ PhpMixed::String(package.get_pretty_name()),
);
package_notification.insert(
"version".to_string(),
- PhpMixed::String(package.get_version().to_string()),
+ PhpMixed::String(package.get_version()),
);
if strpos(repo_url, "packagist.org/").is_some() {
if let Some(metadata) =
- FileDownloader::download_metadata().get(package.get_name())
+ FileDownloader::download_metadata().get(&package.get_name())
{
package_notification.insert("downloaded".to_string(), metadata.clone());
} else {
@@ -764,12 +766,12 @@ impl InstallationManager {
self.reset();
}
- fn mark_for_notification(&mut self, package: &dyn PackageInterface) {
+ fn mark_for_notification(&mut self, package: &PackageInterfaceHandle) {
if let Some(notification_url) = package.get_notification_url() {
self.notifiable_packages
- .entry(notification_url.to_string())
+ .entry(notification_url)
.or_insert_with(Vec::new)
- .push(package.clone_package_box());
+ .push(package.clone());
}
}
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index bb510b8..8ab7efd 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Installer/InstallerInterface.php
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use shirabe_php_shim::PhpMixed;
@@ -30,20 +31,20 @@ pub trait InstallerInterface: std::fmt::Debug {
async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>>;
async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- initial: &dyn PackageInterface,
- target: &dyn PackageInterface,
+ initial: &PackageInterfaceHandle,
+ target: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>>;
async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>>;
async fn cleanup(
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index 29cb455..332bdea 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -16,6 +16,7 @@ use crate::installer::BinaryPresenceInterface;
use crate::installer::InstallerInterface;
use crate::io::IOInterface;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use crate::util::Filesystem;
use crate::util::Platform;
@@ -305,24 +306,36 @@ impl InstallerInterface for LibraryInstaller {
async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
// TODO(phase-b): initialize_vendor_dir requires &mut self
// self.initialize_vendor_dir();
- let download_path = self.get_install_path(package).unwrap();
+ let download_path = self
+ .get_install_path(package.as_rc().borrow().as_package_interface())
+ .unwrap();
// remove the binaries if it appears the package files are missing
- if !Filesystem::is_readable(&download_path) && repo.has_package(package) {
- self.binary_installer.remove_binaries(package);
+ if !Filesystem::is_readable(&download_path)
+ && repo.has_package(package.as_rc().borrow().as_package_interface())
+ {
+ self.binary_installer
+ .remove_binaries(package.as_rc().borrow().as_package_interface());
}
- let _ = self.install_code(package).await?;
+ let _ = self
+ .install_code(package.as_rc().borrow().as_package_interface())
+ .await?;
- let install_path = self.get_install_path(package).unwrap();
- self.binary_installer
- .install_binaries(package, &install_path, true);
- if !repo.has_package(package) {
- repo.add_package(package.clone_package_box());
+ let install_path = self
+ .get_install_path(package.as_rc().borrow().as_package_interface())
+ .unwrap();
+ self.binary_installer.install_binaries(
+ package.as_rc().borrow().as_package_interface(),
+ &install_path,
+ true,
+ );
+ if !repo.has_package(package.as_rc().borrow().as_package_interface()) {
+ repo.add_package(package.clone());
}
Ok(None)
@@ -331,10 +344,10 @@ impl InstallerInterface for LibraryInstaller {
async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- initial: &dyn PackageInterface,
- target: &dyn PackageInterface,
+ initial: &PackageInterfaceHandle,
+ target: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
- if !repo.has_package(initial) {
+ if !repo.has_package(initial.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", initial),
code: 0,
@@ -345,15 +358,26 @@ impl InstallerInterface for LibraryInstaller {
// TODO(phase-b): initialize_vendor_dir requires &mut self
// self.initialize_vendor_dir();
- self.binary_installer.remove_binaries(initial);
- let _ = self.update_code(initial, target).await?;
-
- let install_path = self.get_install_path(target).unwrap();
self.binary_installer
- .install_binaries(target, &install_path, true);
- repo.remove_package(initial);
- if !repo.has_package(target) {
- repo.add_package(target.clone_package_box());
+ .remove_binaries(initial.as_rc().borrow().as_package_interface());
+ let _ = self
+ .update_code(
+ initial.as_rc().borrow().as_package_interface(),
+ target.as_rc().borrow().as_package_interface(),
+ )
+ .await?;
+
+ let install_path = self
+ .get_install_path(target.as_rc().borrow().as_package_interface())
+ .unwrap();
+ self.binary_installer.install_binaries(
+ target.as_rc().borrow().as_package_interface(),
+ &install_path,
+ true,
+ );
+ repo.remove_package(initial.as_rc().borrow().as_package_interface());
+ if !repo.has_package(target.as_rc().borrow().as_package_interface()) {
+ repo.add_package(target.clone());
}
Ok(None)
@@ -362,9 +386,9 @@ impl InstallerInterface for LibraryInstaller {
async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
- if !repo.has_package(package) {
+ if !repo.has_package(package.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", package),
code: 0,
@@ -372,13 +396,17 @@ impl InstallerInterface for LibraryInstaller {
.into());
}
- let _ = self.remove_code(package).await?;
+ let _ = self
+ .remove_code(package.as_rc().borrow().as_package_interface())
+ .await?;
- let download_path = self.get_package_base_path(package);
- self.binary_installer.remove_binaries(package);
- repo.remove_package(package);
+ let download_path =
+ self.get_package_base_path(package.as_rc().borrow().as_package_interface());
+ self.binary_installer
+ .remove_binaries(package.as_rc().borrow().as_package_interface());
+ repo.remove_package(package.as_rc().borrow().as_package_interface());
- if strpos(package.get_name(), "/").map_or(false, |pos| pos != 0) {
+ if strpos(&package.get_name(), "/").map_or(false, |pos| pos != 0) {
let package_vendor_dir = dirname(&download_path);
if is_dir(&package_vendor_dir)
&& self.filesystem.borrow().is_dir_empty(&package_vendor_dir)
diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs
index 2ea9685..cda7bf0 100644
--- a/crates/shirabe/src/installer/metapackage_installer.rs
+++ b/crates/shirabe/src/installer/metapackage_installer.rs
@@ -7,6 +7,7 @@ use crate::installer::InstallerInterface;
use crate::io::IOInterface;
use crate::io::io_interface;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use anyhow::Result;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed};
@@ -65,15 +66,18 @@ impl InstallerInterface for MetapackageInstaller {
async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
self.io.write_error3(
- &format!(" - {}", InstallOperation::format(package, false)),
+ &format!(
+ " - {}",
+ InstallOperation::format(package.as_rc().borrow().as_package_interface(), false)
+ ),
true,
io_interface::NORMAL,
);
- repo.add_package(package.clone_package_box());
+ repo.add_package(package.clone());
Ok(None)
}
@@ -81,10 +85,10 @@ impl InstallerInterface for MetapackageInstaller {
async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- initial: &dyn PackageInterface,
- target: &dyn PackageInterface,
+ initial: &PackageInterfaceHandle,
+ target: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
- if !repo.has_package(initial) {
+ if !repo.has_package(initial.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", initial),
code: 0,
@@ -93,13 +97,20 @@ impl InstallerInterface for MetapackageInstaller {
}
self.io.write_error3(
- &format!(" - {}", UpdateOperation::format(initial, target, false)),
+ &format!(
+ " - {}",
+ UpdateOperation::format(
+ initial.as_rc().borrow().as_package_interface(),
+ target.as_rc().borrow().as_package_interface(),
+ false
+ )
+ ),
true,
io_interface::NORMAL,
);
- repo.remove_package(initial);
- repo.add_package(target.clone_package_box());
+ repo.remove_package(initial.as_rc().borrow().as_package_interface());
+ repo.add_package(target.clone());
Ok(None)
}
@@ -107,9 +118,9 @@ impl InstallerInterface for MetapackageInstaller {
async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
- if !repo.has_package(package) {
+ if !repo.has_package(package.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", package),
code: 0,
@@ -118,12 +129,15 @@ impl InstallerInterface for MetapackageInstaller {
}
self.io.write_error3(
- &format!(" - {}", UninstallOperation::format(package, false)),
+ &format!(
+ " - {}",
+ UninstallOperation::format(package.as_rc().borrow().as_package_interface(), false)
+ ),
true,
io_interface::NORMAL,
);
- repo.remove_package(package);
+ repo.remove_package(package.as_rc().borrow().as_package_interface());
Ok(None)
}
diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs
index 8297165..12e7e95 100644
--- a/crates/shirabe/src/installer/noop_installer.rs
+++ b/crates/shirabe/src/installer/noop_installer.rs
@@ -2,6 +2,7 @@
use crate::installer::InstallerInterface;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed};
@@ -51,10 +52,10 @@ impl InstallerInterface for NoopInstaller {
async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- if !repo.has_package(package) {
- repo.add_package(package.clone_package_box());
+ if !repo.has_package(package.as_rc().borrow().as_package_interface()) {
+ repo.add_package(package.clone());
}
Ok(None)
@@ -63,10 +64,10 @@ impl InstallerInterface for NoopInstaller {
async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- initial: &dyn PackageInterface,
- target: &dyn PackageInterface,
+ initial: &PackageInterfaceHandle,
+ target: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- if !repo.has_package(initial) {
+ if !repo.has_package(initial.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", initial),
code: 0,
@@ -74,9 +75,9 @@ impl InstallerInterface for NoopInstaller {
.into());
}
- repo.remove_package(initial);
- if !repo.has_package(target) {
- repo.add_package(target.clone_package_box());
+ repo.remove_package(initial.as_rc().borrow().as_package_interface());
+ if !repo.has_package(target.as_rc().borrow().as_package_interface()) {
+ repo.add_package(target.clone());
}
Ok(None)
@@ -85,16 +86,16 @@ impl InstallerInterface for NoopInstaller {
async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- if !repo.has_package(package) {
+ if !repo.has_package(package.as_rc().borrow().as_package_interface()) {
return Err(InvalidArgumentException {
message: format!("Package is not installed: {}", package),
code: 0,
}
.into());
}
- repo.remove_package(package);
+ repo.remove_package(package.as_rc().borrow().as_package_interface());
Ok(None)
}
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index a540677..2135bb3 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -6,6 +6,7 @@ use crate::installer::InstallerInterface;
use crate::installer::LibraryInstaller;
use crate::io::IOInterface;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::plugin::PluginManager;
use crate::repository::InstalledRepositoryInterface;
use crate::util::Filesystem;
@@ -45,7 +46,7 @@ impl PluginInstaller {
&mut self,
e: anyhow::Error,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<()> {
self.inner.io.write_error(&format!(
"Plugin initialization failed ({}), uninstalling plugin",
@@ -123,7 +124,7 @@ impl InstallerInterface for PluginInstaller {
async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
self.inner.install(repo, package).await?;
@@ -137,8 +138,8 @@ impl InstallerInterface for PluginInstaller {
async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- initial: &dyn PackageInterface,
- target: &dyn PackageInterface,
+ initial: &PackageInterfaceHandle,
+ target: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
self.inner.update(repo, initial, target).await?;
@@ -153,12 +154,12 @@ impl InstallerInterface for PluginInstaller {
async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
// TODO(plugin): uninstall package from plugin manager
self.get_plugin_manager()
.borrow_mut()
- .uninstall_package(package);
+ .uninstall_package(package.as_rc().borrow().as_package_interface());
self.inner.uninstall(repo, package).await
}
diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs
index 8960854..129810b 100644
--- a/crates/shirabe/src/installer/project_installer.rs
+++ b/crates/shirabe/src/installer/project_installer.rs
@@ -3,6 +3,7 @@
use crate::downloader::DownloadManager;
use crate::installer::InstallerInterface;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
use crate::util::Filesystem;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed};
@@ -95,19 +96,22 @@ impl InstallerInterface for ProjectInstaller {
async fn install(
&mut self,
_repo: &mut dyn InstalledRepositoryInterface,
- package: &dyn PackageInterface,
+ package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
self.download_manager
.borrow()
- .install(package, &self.install_path)
+ .install(
+ package.as_rc().borrow().as_package_interface(),
+ &self.install_path,
+ )
.await
}
async fn update(
&mut self,
_repo: &mut dyn InstalledRepositoryInterface,
- _initial: &dyn PackageInterface,
- _target: &dyn PackageInterface,
+ _initial: &PackageInterfaceHandle,
+ _target: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
Err(InvalidArgumentException {
message: "not supported".to_string(),
@@ -119,7 +123,7 @@ impl InstallerInterface for ProjectInstaller {
async fn uninstall(
&mut self,
_repo: &mut dyn InstalledRepositoryInterface,
- _package: &dyn PackageInterface,
+ _package: &PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
Err(InvalidArgumentException {
message: "not supported".to_string(),
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs
index e9b5dbf..5558337 100644
--- a/crates/shirabe/src/package/alias_package.rs
+++ b/crates/shirabe/src/package/alias_package.rs
@@ -8,6 +8,7 @@ use shirabe_semver::constraint::SimpleConstraint;
use crate::package::BasePackage;
use crate::package::Link;
+use crate::package::PackageHandle;
use crate::package::PackageInterface;
use crate::package::version::VersionParser;
use crate::repository::RepositoryInterface;
@@ -34,7 +35,7 @@ pub struct AliasPackage {
pub(crate) has_self_version_requires: bool,
/// @var BasePackage
- pub(crate) alias_of: Box<dyn BasePackage>,
+ pub(crate) alias_of: PackageHandle,
/// @var Link[]
pub(crate) requires: IndexMap<String, Link>,
/// @var Link[]
@@ -53,8 +54,8 @@ impl AliasPackage {
/// @param BasePackage $aliasOf The package this package is an alias of
/// @param string $version The version the alias must report
/// @param string $prettyVersion The alias's non-normalized version
- pub fn new(alias_of: Box<dyn BasePackage>, version: String, pretty_version: String) -> Self {
- let alias_name = alias_of.get_name().to_string();
+ pub fn new(alias_of: PackageHandle, version: String, pretty_version: String) -> Self {
+ let alias_name = alias_of.get_name();
let stability = VersionParser::parse_stability(&version).to_string();
let dev = stability == "dev";
@@ -130,12 +131,8 @@ impl AliasPackage {
this
}
- pub fn get_alias_of(&self) -> &dyn BasePackage {
- self.alias_of.as_ref()
- }
-
- pub fn get_alias_of_mut(&mut self) -> &mut dyn BasePackage {
- &mut *self.alias_of
+ pub fn get_alias_of(&self) -> PackageHandle {
+ self.alias_of.clone()
}
/// Stores whether this is an alias created by an aliasing in the requirements of the root package or not
@@ -244,11 +241,13 @@ impl PackageInterface for AliasPackage {
}
fn get_name(&self) -> &str {
- self.alias_of.get_name()
+ // PHP delegates to aliasOf; the local name mirrors aliasOf->getName(),
+ // so it is returned here to avoid borrowing across the shared handle.
+ &self.name
}
fn get_pretty_name(&self) -> &str {
- self.alias_of.get_pretty_name()
+ &self.pretty_name
}
fn get_names(&self, provides: bool) -> Vec<String> {
@@ -306,11 +305,14 @@ impl PackageInterface for AliasPackage {
}
fn get_type(&self) -> &str {
- self.alias_of.get_type()
+ // Delegates to the shared `aliasOf` handle, whose getters yield owned
+ // `String`s; a borrow cannot escape the `RefCell`. Use the handle API
+ // (`AliasPackageHandle::get_alias_of().get_type()`) instead.
+ todo!("AliasPackage::get_type cannot return &str across the aliasOf handle")
}
fn get_target_dir(&self) -> Option<&str> {
- self.alias_of.get_target_dir()
+ todo!("AliasPackage::get_target_dir cannot return &str across the aliasOf handle")
}
fn get_extra(&self) -> IndexMap<String, PhpMixed> {
@@ -322,15 +324,15 @@ impl PackageInterface for AliasPackage {
}
fn get_installation_source(&self) -> Option<&str> {
- self.alias_of.get_installation_source()
+ todo!("AliasPackage::get_installation_source cannot return &str across the aliasOf handle")
}
fn get_source_type(&self) -> Option<&str> {
- self.alias_of.get_source_type()
+ todo!("AliasPackage::get_source_type cannot return &str across the aliasOf handle")
}
fn get_source_url(&self) -> Option<&str> {
- self.alias_of.get_source_url()
+ todo!("AliasPackage::get_source_url cannot return &str across the aliasOf handle")
}
fn get_source_urls(&self) -> Vec<String> {
@@ -338,7 +340,7 @@ impl PackageInterface for AliasPackage {
}
fn get_source_reference(&self) -> Option<&str> {
- self.alias_of.get_source_reference()
+ todo!("AliasPackage::get_source_reference cannot return &str across the aliasOf handle")
}
fn set_source_reference(&mut self, reference: Option<String>) {
@@ -354,11 +356,11 @@ impl PackageInterface for AliasPackage {
}
fn get_dist_type(&self) -> Option<&str> {
- self.alias_of.get_dist_type()
+ todo!("AliasPackage::get_dist_type cannot return &str across the aliasOf handle")
}
fn get_dist_url(&self) -> Option<&str> {
- self.alias_of.get_dist_url()
+ todo!("AliasPackage::get_dist_url cannot return &str across the aliasOf handle")
}
fn get_dist_urls(&self) -> Vec<String> {
@@ -366,7 +368,7 @@ impl PackageInterface for AliasPackage {
}
fn get_dist_reference(&self) -> Option<&str> {
- self.alias_of.get_dist_reference()
+ todo!("AliasPackage::get_dist_reference cannot return &str across the aliasOf handle")
}
fn set_dist_reference(&mut self, reference: Option<String>) {
@@ -374,7 +376,7 @@ impl PackageInterface for AliasPackage {
}
fn get_dist_sha1_checksum(&self) -> Option<&str> {
- self.alias_of.get_dist_sha1_checksum()
+ todo!("AliasPackage::get_dist_sha1_checksum cannot return &str across the aliasOf handle")
}
fn set_transport_options(&mut self, options: IndexMap<String, PhpMixed>) {
@@ -422,7 +424,7 @@ impl PackageInterface for AliasPackage {
}
fn get_notification_url(&self) -> Option<&str> {
- self.alias_of.get_notification_url()
+ todo!("AliasPackage::get_notification_url cannot return &str across the aliasOf handle")
}
fn is_default_branch(&self) -> bool {
@@ -442,9 +444,8 @@ impl PackageInterface for AliasPackage {
}
fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String {
- // TODO(phase-b): BasePackage.get_full_pretty_version returns Result; bridge here
- BasePackage::get_full_pretty_version(self.alias_of.as_ref(), truncate, display_mode)
- .unwrap_or_default()
+ self.alias_of
+ .get_full_pretty_version(truncate, display_mode)
}
fn get_unique_name(&self) -> String {
@@ -460,7 +461,7 @@ impl PackageInterface for AliasPackage {
}
fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
- self.alias_of.get_repository()
+ todo!("AliasPackage::get_repository cannot return a borrow across the aliasOf handle")
}
}
@@ -500,8 +501,4 @@ impl BasePackage for AliasPackage {
fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>> {
todo!()
}
-
- fn clone_box(&self) -> Box<dyn BasePackage> {
- todo!()
- }
}
diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs
index 75bd22f..64a5919 100644
--- a/crates/shirabe/src/package/base_package.rs
+++ b/crates/shirabe/src/package/base_package.rs
@@ -89,8 +89,6 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
// TODO(phase-b): wire up a back-reference to the containing repository when needed.
}
- fn clone_box(&self) -> Box<dyn BasePackage>;
-
// as_alias_package / as_complete_package_interface inherited from PackageInterface.
fn as_alias_package_mut(&mut self) -> Option<&mut crate::package::AliasPackage> {
diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs
index 530bd03..6a23bee 100644
--- a/crates/shirabe/src/package/complete_alias_package.rs
+++ b/crates/shirabe/src/package/complete_alias_package.rs
@@ -1,136 +1,156 @@
//! ref: composer/src/Composer/Package/CompleteAliasPackage.php
+use indexmap::IndexMap;
+use shirabe_php_shim::PhpMixed;
+
use crate::package::AliasPackage;
-use crate::package::CompletePackage;
+use crate::package::CompletePackageHandle;
use crate::package::CompletePackageInterface;
+use crate::package::PackageHandle;
+use crate::package::handle::delegate_package_interface_to_inner;
#[derive(Debug)]
pub struct CompleteAliasPackage {
inner: AliasPackage,
// overrides AliasPackage::alias_of with the more specific CompletePackage type
- pub(crate) alias_of: CompletePackage,
+ pub(crate) alias_of: CompletePackageHandle,
}
impl CompleteAliasPackage {
- pub fn new(alias_of: CompletePackage, version: String, pretty_version: String) -> Self {
- // TODO(phase-b): alias_of is a PHP class (shared semantics); cloning is wrong.
- // Use a dummy BasePackage placeholder until the field is migrated to Rc<CompletePackage>.
+ pub fn new(alias_of: CompletePackageHandle, version: String, pretty_version: String) -> Self {
let inner = AliasPackage::new(
- todo!("share CompletePackage via Rc"),
+ PackageHandle::from(alias_of.clone()),
version,
pretty_version,
);
Self { inner, alias_of }
}
- pub fn get_alias_of(&self) -> &CompletePackage {
- &self.alias_of
+ pub fn get_alias_of(&self) -> CompletePackageHandle {
+ self.alias_of.clone()
+ }
+
+ pub fn set_root_package_alias(&mut self, value: bool) {
+ self.inner.set_root_package_alias(value);
+ }
+
+ pub fn is_root_package_alias(&self) -> bool {
+ self.inner.is_root_package_alias()
+ }
+
+ pub fn has_self_version_requires(&self) -> bool {
+ self.inner.has_self_version_requires()
+ }
+}
+
+delegate_package_interface_to_inner!(CompleteAliasPackage, inner);
+
+impl std::fmt::Display for CompleteAliasPackage {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.inner, f)
}
+}
- pub fn get_scripts(&self) -> indexmap::IndexMap<String, Vec<String>> {
+impl CompletePackageInterface for CompleteAliasPackage {
+ fn get_scripts(&self) -> IndexMap<String, Vec<String>> {
self.alias_of.get_scripts()
}
- pub fn set_scripts(&mut self, scripts: indexmap::IndexMap<String, Vec<String>>) {
+ fn set_scripts(&mut self, scripts: IndexMap<String, Vec<String>>) {
self.alias_of.set_scripts(scripts);
}
- pub fn get_repositories(&self) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ fn get_repositories(&self) -> Vec<IndexMap<String, PhpMixed>> {
self.alias_of.get_repositories()
}
- pub fn set_repositories(
- &mut self,
- repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>,
- ) {
+ fn set_repositories(&mut self, repositories: Vec<IndexMap<String, PhpMixed>>) {
self.alias_of.set_repositories(repositories);
}
- pub fn get_license(&self) -> Vec<String> {
+ fn get_license(&self) -> Vec<String> {
self.alias_of.get_license()
}
- pub fn set_license(&mut self, license: Vec<String>) {
+ fn set_license(&mut self, license: Vec<String>) {
self.alias_of.set_license(license);
}
- pub fn get_keywords(&self) -> Vec<String> {
+ fn get_keywords(&self) -> Vec<String> {
self.alias_of.get_keywords()
}
- pub fn set_keywords(&mut self, keywords: Vec<String>) {
+ fn set_keywords(&mut self, keywords: Vec<String>) {
self.alias_of.set_keywords(keywords);
}
- pub fn get_description(&self) -> Option<&str> {
- self.alias_of.get_description()
+ fn get_description(&self) -> Option<&str> {
+ todo!("CompleteAliasPackage::get_description cannot return &str across the aliasOf handle")
}
- pub fn set_description(&mut self, description: Option<String>) {
- self.alias_of
- .set_description(description.unwrap_or_default());
+ fn set_description(&mut self, description: String) {
+ self.alias_of.set_description(description);
}
- pub fn get_homepage(&self) -> Option<&str> {
- self.alias_of.get_homepage()
+ fn get_homepage(&self) -> Option<&str> {
+ todo!("CompleteAliasPackage::get_homepage cannot return &str across the aliasOf handle")
}
- pub fn set_homepage(&mut self, homepage: Option<String>) {
- self.alias_of.set_homepage(homepage.unwrap_or_default());
+ fn set_homepage(&mut self, homepage: String) {
+ self.alias_of.set_homepage(homepage);
}
- pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> {
+ fn get_authors(&self) -> Vec<IndexMap<String, String>> {
self.alias_of.get_authors()
}
- pub fn set_authors(&mut self, authors: Vec<indexmap::IndexMap<String, String>>) {
+ fn set_authors(&mut self, authors: Vec<IndexMap<String, String>>) {
self.alias_of.set_authors(authors);
}
- pub fn get_support(&self) -> indexmap::IndexMap<String, String> {
+ fn get_support(&self) -> IndexMap<String, String> {
self.alias_of.get_support()
}
- pub fn set_support(&mut self, support: indexmap::IndexMap<String, String>) {
+ fn set_support(&mut self, support: IndexMap<String, String>) {
self.alias_of.set_support(support);
}
- pub fn get_funding(&self) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ fn get_funding(&self) -> Vec<IndexMap<String, PhpMixed>> {
self.alias_of.get_funding()
}
- pub fn set_funding(
- &mut self,
- funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>,
- ) {
+ fn set_funding(&mut self, funding: Vec<IndexMap<String, PhpMixed>>) {
self.alias_of.set_funding(funding);
}
- pub fn is_abandoned(&self) -> bool {
+ fn is_abandoned(&self) -> bool {
self.alias_of.is_abandoned()
}
- pub fn get_replacement_package(&self) -> Option<&str> {
- self.alias_of.get_replacement_package()
+ fn get_replacement_package(&self) -> Option<&str> {
+ todo!(
+ "CompleteAliasPackage::get_replacement_package cannot return &str across the aliasOf handle"
+ )
}
- pub fn set_abandoned(&mut self, abandoned: shirabe_php_shim::PhpMixed) {
+ fn set_abandoned(&mut self, abandoned: PhpMixed) {
self.alias_of.set_abandoned(abandoned);
}
- pub fn get_archive_name(&self) -> Option<&str> {
- self.alias_of.get_archive_name()
+ fn get_archive_name(&self) -> Option<&str> {
+ todo!("CompleteAliasPackage::get_archive_name cannot return &str across the aliasOf handle")
}
- pub fn set_archive_name(&mut self, name: Option<String>) {
- self.alias_of.set_archive_name(name.unwrap_or_default());
+ fn set_archive_name(&mut self, name: String) {
+ self.alias_of.set_archive_name(name);
}
- pub fn get_archive_excludes(&self) -> Vec<String> {
+ fn get_archive_excludes(&self) -> Vec<String> {
self.alias_of.get_archive_excludes()
}
- pub fn set_archive_excludes(&mut self, excludes: Vec<String>) {
+ fn set_archive_excludes(&mut self, excludes: Vec<String>) {
self.alias_of.set_archive_excludes(excludes);
}
}
diff --git a/crates/shirabe/src/package/handle.rs b/crates/shirabe/src/package/handle.rs
new file mode 100644
index 0000000..e1f0570
--- /dev/null
+++ b/crates/shirabe/src/package/handle.rs
@@ -0,0 +1,1440 @@
+//! Shared handles over the package types.
+//!
+//! No weak handles are provided: an alias package never aliases another alias package, so the
+//! `alias_of` references are acyclic.
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use crate::package::{
+ AliasPackage, CompleteAliasPackage, CompletePackage, CompletePackageInterface, Package,
+ PackageInterface, RootAliasPackage, RootPackage, RootPackageInterface,
+};
+
+/// Any package type.
+#[derive(Debug)]
+pub enum AnyPackage {
+ Package(Package),
+ CompletePackage(CompletePackage),
+ RootPackage(RootPackage),
+ AliasPackage(AliasPackage),
+ CompleteAliasPackage(CompleteAliasPackage),
+ RootAliasPackage(RootAliasPackage),
+}
+
+impl AnyPackage {
+ pub fn as_package_interface(&self) -> &dyn PackageInterface {
+ match self {
+ Self::Package(p) => p,
+ Self::CompletePackage(p) => p,
+ Self::RootPackage(p) => p,
+ Self::AliasPackage(p) => p,
+ Self::CompleteAliasPackage(p) => p,
+ Self::RootAliasPackage(p) => p,
+ }
+ }
+
+ pub fn as_package_interface_mut(&mut self) -> &mut dyn PackageInterface {
+ match self {
+ Self::Package(p) => p,
+ Self::CompletePackage(p) => p,
+ Self::RootPackage(p) => p,
+ Self::AliasPackage(p) => p,
+ Self::CompleteAliasPackage(p) => p,
+ Self::RootAliasPackage(p) => p,
+ }
+ }
+
+ pub fn as_complete_package_interface(&self) -> Option<&dyn CompletePackageInterface> {
+ match self {
+ Self::CompletePackage(p) => Some(p),
+ Self::RootPackage(p) => Some(p),
+ Self::CompleteAliasPackage(p) => Some(p),
+ Self::RootAliasPackage(p) => Some(p),
+ _ => None,
+ }
+ }
+
+ pub fn as_complete_package_interface_mut(
+ &mut self,
+ ) -> Option<&mut dyn CompletePackageInterface> {
+ match self {
+ Self::CompletePackage(p) => Some(p),
+ Self::RootPackage(p) => Some(p),
+ Self::CompleteAliasPackage(p) => Some(p),
+ Self::RootAliasPackage(p) => Some(p),
+ _ => None,
+ }
+ }
+
+ pub fn as_root_package_interface(&self) -> Option<&dyn RootPackageInterface> {
+ match self {
+ Self::RootPackage(p) => Some(p),
+ Self::RootAliasPackage(p) => Some(p),
+ _ => None,
+ }
+ }
+
+ pub fn as_root_package_interface_mut(&mut self) -> Option<&mut dyn RootPackageInterface> {
+ match self {
+ Self::RootPackage(p) => Some(p),
+ Self::RootAliasPackage(p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// PHP `$p instanceof AliasPackage`.
+ pub fn is_alias(&self) -> bool {
+ matches!(
+ self,
+ Self::AliasPackage(_) | Self::CompleteAliasPackage(_) | Self::RootAliasPackage(_)
+ )
+ }
+
+ /// PHP `$p instanceof CompletePackageInterface`.
+ pub fn is_complete(&self) -> bool {
+ matches!(
+ self,
+ Self::CompletePackage(_)
+ | Self::RootPackage(_)
+ | Self::CompleteAliasPackage(_)
+ | Self::RootAliasPackage(_)
+ )
+ }
+
+ /// PHP `$p instanceof RootPackageInterface`.
+ pub fn is_root(&self) -> bool {
+ matches!(self, Self::RootPackage(_) | Self::RootAliasPackage(_))
+ }
+
+ /// A real (non-alias) package: `Package` / `CompletePackage` / `RootPackage`.
+ pub fn is_real(&self) -> bool {
+ matches!(
+ self,
+ Self::Package(_) | Self::CompletePackage(_) | Self::RootPackage(_)
+ )
+ }
+
+ /// A real `CompletePackage` or `RootPackage`.
+ pub fn is_complete_real(&self) -> bool {
+ matches!(self, Self::CompletePackage(_) | Self::RootPackage(_))
+ }
+
+ /// A real `RootPackage`.
+ pub fn is_root_real(&self) -> bool {
+ matches!(self, Self::RootPackage(_))
+ }
+
+ /// A `CompleteAliasPackage` or `RootAliasPackage`.
+ pub fn is_complete_alias(&self) -> bool {
+ matches!(
+ self,
+ Self::CompleteAliasPackage(_) | Self::RootAliasPackage(_)
+ )
+ }
+
+ /// A `RootAliasPackage`.
+ pub fn is_root_alias(&self) -> bool {
+ matches!(self, Self::RootAliasPackage(_))
+ }
+}
+
+macro_rules! delegate_package_interface_to_inner {
+ ($Type:ty, $field:ident) => {
+ impl crate::package::PackageInterface for $Type {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+ fn get_name(&self) -> &str {
+ self.$field.get_name()
+ }
+ fn get_pretty_name(&self) -> &str {
+ self.$field.get_pretty_name()
+ }
+ fn get_names(&self, provides: bool) -> Vec<String> {
+ self.$field.get_names(provides)
+ }
+ fn set_id(&mut self, id: i64) {
+ self.$field.set_id(id);
+ }
+ fn get_id(&self) -> i64 {
+ self.$field.get_id()
+ }
+ fn is_dev(&self) -> bool {
+ self.$field.is_dev()
+ }
+ fn get_type(&self) -> &str {
+ self.$field.get_type()
+ }
+ fn get_target_dir(&self) -> Option<&str> {
+ self.$field.get_target_dir()
+ }
+ fn get_extra(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.$field.get_extra()
+ }
+ fn set_installation_source(&mut self, r#type: Option<String>) {
+ self.$field.set_installation_source(r#type);
+ }
+ fn get_installation_source(&self) -> Option<&str> {
+ self.$field.get_installation_source()
+ }
+ fn get_source_type(&self) -> Option<&str> {
+ self.$field.get_source_type()
+ }
+ fn get_source_url(&self) -> Option<&str> {
+ self.$field.get_source_url()
+ }
+ fn get_source_urls(&self) -> Vec<String> {
+ self.$field.get_source_urls()
+ }
+ fn get_source_reference(&self) -> Option<&str> {
+ self.$field.get_source_reference()
+ }
+ fn get_source_mirrors(
+ &self,
+ ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> {
+ self.$field.get_source_mirrors()
+ }
+ fn set_source_mirrors(
+ &mut self,
+ mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>,
+ ) {
+ self.$field.set_source_mirrors(mirrors);
+ }
+ fn get_dist_type(&self) -> Option<&str> {
+ self.$field.get_dist_type()
+ }
+ fn get_dist_url(&self) -> Option<&str> {
+ self.$field.get_dist_url()
+ }
+ fn get_dist_urls(&self) -> Vec<String> {
+ self.$field.get_dist_urls()
+ }
+ fn get_dist_reference(&self) -> Option<&str> {
+ self.$field.get_dist_reference()
+ }
+ fn get_dist_sha1_checksum(&self) -> Option<&str> {
+ self.$field.get_dist_sha1_checksum()
+ }
+ fn get_dist_mirrors(
+ &self,
+ ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> {
+ self.$field.get_dist_mirrors()
+ }
+ fn set_dist_mirrors(
+ &mut self,
+ mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>,
+ ) {
+ self.$field.set_dist_mirrors(mirrors);
+ }
+ fn get_version(&self) -> &str {
+ self.$field.get_version()
+ }
+ fn get_pretty_version(&self) -> &str {
+ self.$field.get_pretty_version()
+ }
+ fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String {
+ self.$field.get_full_pretty_version(truncate, display_mode)
+ }
+ fn get_release_date(&self) -> Option<chrono::DateTime<chrono::Utc>> {
+ self.$field.get_release_date()
+ }
+ fn get_stability(&self) -> &str {
+ self.$field.get_stability()
+ }
+ fn get_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.$field.get_requires()
+ }
+ fn get_conflicts(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.$field.get_conflicts()
+ }
+ fn get_provides(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.$field.get_provides()
+ }
+ fn get_replaces(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.$field.get_replaces()
+ }
+ fn get_dev_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.$field.get_dev_requires()
+ }
+ fn get_suggests(&self) -> indexmap::IndexMap<String, String> {
+ self.$field.get_suggests()
+ }
+ fn get_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.$field.get_autoload()
+ }
+ fn get_dev_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.$field.get_dev_autoload()
+ }
+ fn get_include_paths(&self) -> Vec<String> {
+ self.$field.get_include_paths()
+ }
+ fn get_php_ext(
+ &self,
+ ) -> Option<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ self.$field.get_php_ext()
+ }
+ fn set_repository(
+ &mut self,
+ repository: Box<dyn crate::repository::RepositoryInterface>,
+ ) -> anyhow::Result<()> {
+ self.$field.set_repository(repository)
+ }
+ fn get_repository(&self) -> Option<&dyn crate::repository::RepositoryInterface> {
+ self.$field.get_repository()
+ }
+ fn get_binaries(&self) -> Vec<String> {
+ self.$field.get_binaries()
+ }
+ fn get_unique_name(&self) -> String {
+ self.$field.get_unique_name()
+ }
+ fn get_notification_url(&self) -> Option<&str> {
+ self.$field.get_notification_url()
+ }
+ fn get_pretty_string(&self) -> String {
+ self.$field.get_pretty_string()
+ }
+ fn is_default_branch(&self) -> bool {
+ self.$field.is_default_branch()
+ }
+ fn get_transport_options(
+ &self,
+ ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.$field.get_transport_options()
+ }
+ fn set_transport_options(
+ &mut self,
+ options: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>,
+ ) {
+ self.$field.set_transport_options(options);
+ }
+ fn set_source_reference(&mut self, reference: Option<String>) {
+ self.$field.set_source_reference(reference);
+ }
+ fn set_dist_url(&mut self, url: Option<String>) {
+ self.$field.set_dist_url(url);
+ }
+ fn set_dist_type(&mut self, r#type: Option<String>) {
+ self.$field.set_dist_type(r#type);
+ }
+ fn set_dist_reference(&mut self, reference: Option<String>) {
+ self.$field.set_dist_reference(reference);
+ }
+ fn set_source_dist_references(&mut self, reference: &str) {
+ self.$field.set_source_dist_references(reference);
+ }
+ }
+ };
+}
+pub(crate) use delegate_package_interface_to_inner;
+
+macro_rules! impl_package_interface_handle {
+ ($Handle:ty) => {
+ impl $Handle {
+ pub fn get_name(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_name()
+ .to_string()
+ }
+
+ pub fn get_pretty_name(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_pretty_name()
+ .to_string()
+ }
+
+ pub fn get_names(&self, provides: bool) -> Vec<String> {
+ self.0.borrow().as_package_interface().get_names(provides)
+ }
+
+ pub fn set_id(&self, id: i64) {
+ self.0.borrow_mut().as_package_interface_mut().set_id(id);
+ }
+
+ pub fn get_id(&self) -> i64 {
+ self.0.borrow().as_package_interface().get_id()
+ }
+
+ /// PHP `BasePackage::$id` accessor; alias of [`get_id`](Self::get_id).
+ pub fn id(&self) -> i64 {
+ self.0.borrow().as_package_interface().get_id()
+ }
+
+ pub fn is_dev(&self) -> bool {
+ self.0.borrow().as_package_interface().is_dev()
+ }
+
+ pub fn get_type(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_type()
+ .to_string()
+ }
+
+ pub fn get_target_dir(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_target_dir()
+ .map(str::to_string)
+ }
+
+ pub fn get_extra(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.0.borrow().as_package_interface().get_extra()
+ }
+
+ pub fn set_installation_source(&self, r#type: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_installation_source(r#type);
+ }
+
+ pub fn get_installation_source(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_installation_source()
+ .map(str::to_string)
+ }
+
+ pub fn get_source_type(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_source_type()
+ .map(str::to_string)
+ }
+
+ pub fn get_source_url(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_source_url()
+ .map(str::to_string)
+ }
+
+ pub fn get_source_urls(&self) -> Vec<String> {
+ self.0.borrow().as_package_interface().get_source_urls()
+ }
+
+ pub fn get_source_reference(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_source_reference()
+ .map(str::to_string)
+ }
+
+ pub fn get_source_mirrors(
+ &self,
+ ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> {
+ self.0.borrow().as_package_interface().get_source_mirrors()
+ }
+
+ pub fn set_source_mirrors(
+ &self,
+ mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_source_mirrors(mirrors);
+ }
+
+ pub fn get_dist_type(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_dist_type()
+ .map(str::to_string)
+ }
+
+ pub fn get_dist_url(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_dist_url()
+ .map(str::to_string)
+ }
+
+ pub fn get_dist_urls(&self) -> Vec<String> {
+ self.0.borrow().as_package_interface().get_dist_urls()
+ }
+
+ pub fn get_dist_reference(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_dist_reference()
+ .map(str::to_string)
+ }
+
+ pub fn get_dist_sha1_checksum(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_dist_sha1_checksum()
+ .map(str::to_string)
+ }
+
+ pub fn get_dist_mirrors(
+ &self,
+ ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> {
+ self.0.borrow().as_package_interface().get_dist_mirrors()
+ }
+
+ pub fn set_dist_mirrors(
+ &self,
+ mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_dist_mirrors(mirrors);
+ }
+
+ pub fn get_version(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_version()
+ .to_string()
+ }
+
+ pub fn get_pretty_version(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_pretty_version()
+ .to_string()
+ }
+
+ pub fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_full_pretty_version(truncate, display_mode)
+ }
+
+ pub fn get_release_date(&self) -> Option<chrono::DateTime<chrono::Utc>> {
+ self.0.borrow().as_package_interface().get_release_date()
+ }
+
+ pub fn get_stability(&self) -> String {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_stability()
+ .to_string()
+ }
+
+ pub fn get_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0.borrow().as_package_interface().get_requires()
+ }
+
+ pub fn get_conflicts(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0.borrow().as_package_interface().get_conflicts()
+ }
+
+ pub fn get_provides(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0.borrow().as_package_interface().get_provides()
+ }
+
+ pub fn get_replaces(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0.borrow().as_package_interface().get_replaces()
+ }
+
+ pub fn get_dev_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0.borrow().as_package_interface().get_dev_requires()
+ }
+
+ pub fn get_suggests(&self) -> indexmap::IndexMap<String, String> {
+ self.0.borrow().as_package_interface().get_suggests()
+ }
+
+ pub fn get_links_for_type(
+ &self,
+ link_type: &str,
+ ) -> indexmap::IndexMap<String, crate::package::Link> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_links_for_type(link_type)
+ }
+
+ pub fn get_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.0.borrow().as_package_interface().get_autoload()
+ }
+
+ pub fn get_dev_autoload(
+ &self,
+ ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.0.borrow().as_package_interface().get_dev_autoload()
+ }
+
+ pub fn get_include_paths(&self) -> Vec<String> {
+ self.0.borrow().as_package_interface().get_include_paths()
+ }
+
+ pub fn get_php_ext(
+ &self,
+ ) -> Option<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ self.0.borrow().as_package_interface().get_php_ext()
+ }
+
+ pub fn set_repository(
+ &self,
+ repository: Box<dyn crate::repository::RepositoryInterface>,
+ ) -> anyhow::Result<()> {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_repository(repository)
+ }
+
+ pub fn get_binaries(&self) -> Vec<String> {
+ self.0.borrow().as_package_interface().get_binaries()
+ }
+
+ pub fn get_unique_name(&self) -> String {
+ self.0.borrow().as_package_interface().get_unique_name()
+ }
+
+ pub fn get_notification_url(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_notification_url()
+ .map(str::to_string)
+ }
+
+ pub fn get_pretty_string(&self) -> String {
+ self.0.borrow().as_package_interface().get_pretty_string()
+ }
+
+ pub fn is_default_branch(&self) -> bool {
+ self.0.borrow().as_package_interface().is_default_branch()
+ }
+
+ pub fn get_transport_options(
+ &self,
+ ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.0
+ .borrow()
+ .as_package_interface()
+ .get_transport_options()
+ }
+
+ pub fn set_transport_options(
+ &self,
+ options: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_transport_options(options);
+ }
+
+ pub fn set_source_reference(&self, reference: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_source_reference(reference);
+ }
+
+ pub fn set_dist_url(&self, url: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_dist_url(url);
+ }
+
+ pub fn set_dist_type(&self, r#type: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_dist_type(r#type);
+ }
+
+ pub fn set_dist_reference(&self, reference: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_dist_reference(reference);
+ }
+
+ pub fn set_source_dist_references(&self, reference: &str) {
+ self.0
+ .borrow_mut()
+ .as_package_interface_mut()
+ .set_source_dist_references(reference);
+ }
+ }
+ };
+}
+
+macro_rules! impl_complete_package_interface_handle {
+ ($Handle:ty) => {
+ impl $Handle {
+ pub fn get_scripts(&self) -> indexmap::IndexMap<String, Vec<String>> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_scripts()
+ }
+
+ pub fn set_scripts(&self, scripts: indexmap::IndexMap<String, Vec<String>>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_scripts(scripts);
+ }
+
+ pub fn get_repositories(
+ &self,
+ ) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_repositories()
+ }
+
+ pub fn set_repositories(
+ &self,
+ repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_repositories(repositories);
+ }
+
+ pub fn get_license(&self) -> Vec<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_license()
+ }
+
+ pub fn set_license(&self, license: Vec<String>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_license(license);
+ }
+
+ pub fn get_keywords(&self) -> Vec<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_keywords()
+ }
+
+ pub fn set_keywords(&self, keywords: Vec<String>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_keywords(keywords);
+ }
+
+ pub fn get_description(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_description()
+ .map(str::to_string)
+ }
+
+ pub fn set_description(&self, description: String) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_description(description);
+ }
+
+ pub fn get_homepage(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_homepage()
+ .map(str::to_string)
+ }
+
+ pub fn set_homepage(&self, homepage: String) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_homepage(homepage);
+ }
+
+ pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_authors()
+ }
+
+ pub fn set_authors(&self, authors: Vec<indexmap::IndexMap<String, String>>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_authors(authors);
+ }
+
+ pub fn get_support(&self) -> indexmap::IndexMap<String, String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_support()
+ }
+
+ pub fn set_support(&self, support: indexmap::IndexMap<String, String>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_support(support);
+ }
+
+ pub fn get_funding(
+ &self,
+ ) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_funding()
+ }
+
+ pub fn set_funding(
+ &self,
+ funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_funding(funding);
+ }
+
+ pub fn is_abandoned(&self) -> bool {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .is_abandoned()
+ }
+
+ pub fn get_replacement_package(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_replacement_package()
+ .map(str::to_string)
+ }
+
+ pub fn set_abandoned(&self, abandoned: shirabe_php_shim::PhpMixed) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_abandoned(abandoned);
+ }
+
+ pub fn get_archive_name(&self) -> Option<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_archive_name()
+ .map(str::to_string)
+ }
+
+ pub fn set_archive_name(&self, name: String) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_archive_name(name);
+ }
+
+ pub fn get_archive_excludes(&self) -> Vec<String> {
+ self.0
+ .borrow()
+ .as_complete_package_interface()
+ .expect("CompletePackage handle invariant")
+ .get_archive_excludes()
+ }
+
+ pub fn set_archive_excludes(&self, excludes: Vec<String>) {
+ self.0
+ .borrow_mut()
+ .as_complete_package_interface_mut()
+ .expect("CompletePackage handle invariant")
+ .set_archive_excludes(excludes);
+ }
+ }
+ };
+}
+
+macro_rules! impl_root_package_interface_handle {
+ ($Handle:ty) => {
+ impl $Handle {
+ pub fn get_aliases(&self) -> Vec<indexmap::IndexMap<String, String>> {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_aliases()
+ .to_vec()
+ }
+
+ pub fn get_minimum_stability(&self) -> String {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_minimum_stability()
+ .to_string()
+ }
+
+ pub fn get_stability_flags(&self) -> indexmap::IndexMap<String, i64> {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_stability_flags()
+ .clone()
+ }
+
+ pub fn get_references(&self) -> indexmap::IndexMap<String, String> {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_references()
+ .clone()
+ }
+
+ pub fn get_prefer_stable(&self) -> bool {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_prefer_stable()
+ }
+
+ pub fn get_config(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> {
+ self.0
+ .borrow()
+ .as_root_package_interface()
+ .expect("RootPackage handle invariant")
+ .get_config()
+ .clone()
+ }
+
+ pub fn set_requires(&self, requires: Vec<crate::package::Link>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_requires(requires);
+ }
+
+ pub fn set_dev_requires(&self, dev_requires: Vec<crate::package::Link>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_dev_requires(dev_requires);
+ }
+
+ pub fn set_conflicts(&self, conflicts: Vec<crate::package::Link>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_conflicts(conflicts);
+ }
+
+ pub fn set_provides(&self, provides: Vec<crate::package::Link>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_provides(provides);
+ }
+
+ pub fn set_replaces(&self, replaces: Vec<crate::package::Link>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_replaces(replaces);
+ }
+
+ pub fn set_autoload(
+ &self,
+ autoload: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_autoload(autoload);
+ }
+
+ pub fn set_dev_autoload(
+ &self,
+ dev_autoload: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_dev_autoload(dev_autoload);
+ }
+
+ pub fn set_stability_flags(&self, stability_flags: indexmap::IndexMap<String, i64>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_stability_flags(stability_flags);
+ }
+
+ pub fn set_minimum_stability(&self, minimum_stability: String) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_minimum_stability(minimum_stability);
+ }
+
+ pub fn set_prefer_stable(&self, prefer_stable: bool) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_prefer_stable(prefer_stable);
+ }
+
+ pub fn set_config(
+ &self,
+ config: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>,
+ ) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_config(config);
+ }
+
+ pub fn set_references(&self, references: indexmap::IndexMap<String, String>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_references(references);
+ }
+
+ pub fn set_aliases(&self, aliases: Vec<indexmap::IndexMap<String, String>>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_aliases(aliases);
+ }
+
+ pub fn set_suggests(&self, suggests: indexmap::IndexMap<String, String>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_suggests(suggests);
+ }
+
+ pub fn set_extra(&self, extra: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>) {
+ self.0
+ .borrow_mut()
+ .as_root_package_interface_mut()
+ .expect("RootPackage handle invariant")
+ .set_extra(extra);
+ }
+ }
+ };
+}
+
+macro_rules! impl_handle_common {
+ ($Handle:ty) => {
+ impl $Handle {
+ pub fn as_rc(&self) -> &std::rc::Rc<std::cell::RefCell<AnyPackage>> {
+ &self.0
+ }
+
+ pub fn from_rc_unchecked(rc: std::rc::Rc<std::cell::RefCell<AnyPackage>>) -> Self {
+ Self(rc)
+ }
+
+ /// Stable identity usable as a map key (PHP `spl_object_hash`).
+ pub fn ptr_id(&self) -> usize {
+ std::rc::Rc::as_ptr(&self.0) as *const () as usize
+ }
+
+ /// PHP `===` (reference identity).
+ pub fn ptr_eq(&self, other: &Self) -> bool {
+ std::rc::Rc::ptr_eq(&self.0, &other.0)
+ }
+ }
+
+ impl PartialEq for $Handle {
+ fn eq(&self, other: &Self) -> bool {
+ std::rc::Rc::ptr_eq(&self.0, &other.0)
+ }
+ }
+
+ impl Eq for $Handle {}
+
+ impl std::hash::Hash for $Handle {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.ptr_id().hash(state);
+ }
+ }
+
+ impl std::fmt::Display for $Handle {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.0.borrow().as_package_interface(), f)
+ }
+ }
+ };
+}
+
+macro_rules! impl_handle_upcast {
+ ($Narrow:ty => $Wide:ty) => {
+ impl From<$Narrow> for $Wide {
+ fn from(h: $Narrow) -> Self {
+ Self(h.0)
+ }
+ }
+ };
+}
+
+/// Shared reference to any package. Corresponds to PHP `PackageInterface`.
+#[derive(Debug, Clone)]
+pub struct PackageInterfaceHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to any package. Corresponds to PHP `BasePackage`.
+/// It is exactly the same as `PackageInterface` in Shirabe. It is only for mirroing PHP type
+/// annotations.
+pub type BasePackageHandle = PackageInterfaceHandle;
+
+/// Shared reference to a complete package. Corresponds to PHP `CompletePackageInterface`.
+#[derive(Debug, Clone)]
+pub struct CompletePackageInterfaceHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a root package. Corresponds to PHP `RootPackageInterface`.
+#[derive(Debug, Clone)]
+pub struct RootPackageInterfaceHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a real (non-alias) package. Corresponds to PHP `Package`.
+#[derive(Debug, Clone)]
+pub struct PackageHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a real complete package. Corresponds to PHP `CompletePackage`.
+#[derive(Debug, Clone)]
+pub struct CompletePackageHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a real root package. Corresponds to PHP `RootPackage`.
+#[derive(Debug, Clone)]
+pub struct RootPackageHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to an alias package. Corresponds to PHP `AliasPackage`.
+#[derive(Debug, Clone)]
+pub struct AliasPackageHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a complete alias package. Corresponds to PHP `CompleteAliasPackage`.
+#[derive(Debug, Clone)]
+pub struct CompleteAliasPackageHandle(Rc<RefCell<AnyPackage>>);
+
+/// Shared reference to a root alias package. Corresponds to PHP `RootAliasPackage`.
+#[derive(Debug, Clone)]
+pub struct RootAliasPackageHandle(Rc<RefCell<AnyPackage>>);
+
+impl_handle_common!(PackageInterfaceHandle);
+impl_handle_common!(CompletePackageInterfaceHandle);
+impl_handle_common!(RootPackageInterfaceHandle);
+impl_handle_common!(PackageHandle);
+impl_handle_common!(CompletePackageHandle);
+impl_handle_common!(RootPackageHandle);
+impl_handle_common!(AliasPackageHandle);
+impl_handle_common!(CompleteAliasPackageHandle);
+impl_handle_common!(RootAliasPackageHandle);
+
+impl_package_interface_handle!(PackageInterfaceHandle);
+impl_package_interface_handle!(CompletePackageInterfaceHandle);
+impl_package_interface_handle!(RootPackageInterfaceHandle);
+impl_package_interface_handle!(PackageHandle);
+impl_package_interface_handle!(CompletePackageHandle);
+impl_package_interface_handle!(RootPackageHandle);
+impl_package_interface_handle!(AliasPackageHandle);
+impl_package_interface_handle!(CompleteAliasPackageHandle);
+impl_package_interface_handle!(RootAliasPackageHandle);
+
+impl_complete_package_interface_handle!(CompletePackageInterfaceHandle);
+impl_complete_package_interface_handle!(RootPackageInterfaceHandle);
+impl_complete_package_interface_handle!(CompletePackageHandle);
+impl_complete_package_interface_handle!(RootPackageHandle);
+impl_complete_package_interface_handle!(CompleteAliasPackageHandle);
+impl_complete_package_interface_handle!(RootAliasPackageHandle);
+
+impl_root_package_interface_handle!(RootPackageInterfaceHandle);
+impl_root_package_interface_handle!(RootPackageHandle);
+impl_root_package_interface_handle!(RootAliasPackageHandle);
+
+impl_handle_upcast!(CompletePackageInterfaceHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(RootPackageInterfaceHandle => CompletePackageInterfaceHandle);
+impl_handle_upcast!(RootPackageInterfaceHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(PackageHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(CompletePackageHandle => PackageHandle);
+impl_handle_upcast!(CompletePackageHandle => CompletePackageInterfaceHandle);
+impl_handle_upcast!(CompletePackageHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(RootPackageHandle => CompletePackageHandle);
+impl_handle_upcast!(RootPackageHandle => PackageHandle);
+impl_handle_upcast!(RootPackageHandle => RootPackageInterfaceHandle);
+impl_handle_upcast!(RootPackageHandle => CompletePackageInterfaceHandle);
+impl_handle_upcast!(RootPackageHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(AliasPackageHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(CompleteAliasPackageHandle => AliasPackageHandle);
+impl_handle_upcast!(CompleteAliasPackageHandle => CompletePackageInterfaceHandle);
+impl_handle_upcast!(CompleteAliasPackageHandle => PackageInterfaceHandle);
+
+impl_handle_upcast!(RootAliasPackageHandle => CompleteAliasPackageHandle);
+impl_handle_upcast!(RootAliasPackageHandle => AliasPackageHandle);
+impl_handle_upcast!(RootAliasPackageHandle => RootPackageInterfaceHandle);
+impl_handle_upcast!(RootAliasPackageHandle => CompletePackageInterfaceHandle);
+impl_handle_upcast!(RootAliasPackageHandle => PackageInterfaceHandle);
+
+macro_rules! impl_handle_downcasts {
+ ($Handle:ty) => {
+ impl $Handle {
+ /// PHP `$p instanceof AliasPackage`.
+ pub fn as_alias(&self) -> Option<AliasPackageHandle> {
+ self.0
+ .borrow()
+ .is_alias()
+ .then(|| AliasPackageHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof CompletePackageInterface`.
+ pub fn as_complete(&self) -> Option<CompletePackageInterfaceHandle> {
+ self.0
+ .borrow()
+ .is_complete()
+ .then(|| CompletePackageInterfaceHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof RootPackageInterface`.
+ pub fn as_root(&self) -> Option<RootPackageInterfaceHandle> {
+ self.0
+ .borrow()
+ .is_root()
+ .then(|| RootPackageInterfaceHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof Package` (real, non-alias).
+ pub fn as_package(&self) -> Option<PackageHandle> {
+ self.0
+ .borrow()
+ .is_real()
+ .then(|| PackageHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof CompletePackage` (real).
+ pub fn as_complete_package(&self) -> Option<CompletePackageHandle> {
+ self.0
+ .borrow()
+ .is_complete_real()
+ .then(|| CompletePackageHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof RootPackage` (real).
+ pub fn as_root_package(&self) -> Option<RootPackageHandle> {
+ self.0
+ .borrow()
+ .is_root_real()
+ .then(|| RootPackageHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof CompleteAliasPackage`.
+ pub fn as_complete_alias_package(&self) -> Option<CompleteAliasPackageHandle> {
+ self.0
+ .borrow()
+ .is_complete_alias()
+ .then(|| CompleteAliasPackageHandle(self.0.clone()))
+ }
+
+ /// PHP `$p instanceof RootAliasPackage`.
+ pub fn as_root_alias_package(&self) -> Option<RootAliasPackageHandle> {
+ self.0
+ .borrow()
+ .is_root_alias()
+ .then(|| RootAliasPackageHandle(self.0.clone()))
+ }
+
+ pub fn is_alias(&self) -> bool {
+ self.0.borrow().is_alias()
+ }
+ }
+ };
+}
+
+impl_handle_downcasts!(PackageInterfaceHandle);
+
+impl PackageHandle {
+ pub fn from_package(package: Package) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::Package(package))))
+ }
+
+ pub fn new(name: String, version: String, pretty_version: String) -> Self {
+ Self::from_package(Package::new(name, version, pretty_version))
+ }
+}
+
+impl CompletePackageHandle {
+ pub fn from_complete_package(package: CompletePackage) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::CompletePackage(package))))
+ }
+
+ pub fn new(name: String, version: String, pretty_version: String) -> Self {
+ Self::from_complete_package(CompletePackage::new(name, version, pretty_version))
+ }
+}
+
+impl RootPackageHandle {
+ pub fn from_root_package(package: RootPackage) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::RootPackage(package))))
+ }
+
+ pub fn new(name: String, version: String, pretty_version: String) -> Self {
+ Self::from_root_package(RootPackage::new(name, version, pretty_version))
+ }
+}
+
+impl AliasPackageHandle {
+ pub fn from_alias_package(package: AliasPackage) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::AliasPackage(package))))
+ }
+
+ pub fn new(alias_of: PackageHandle, version: String, pretty_version: String) -> Self {
+ Self::from_alias_package(AliasPackage::new(alias_of, version, pretty_version))
+ }
+
+ /// PHP `getAliasOf()`. The aliased package is always real.
+ pub fn get_alias_of(&self) -> PackageHandle {
+ match &*self.0.borrow() {
+ AnyPackage::AliasPackage(p) => p.alias_of.clone(),
+ AnyPackage::CompleteAliasPackage(p) => PackageHandle::from(p.alias_of.clone()),
+ AnyPackage::RootAliasPackage(p) => PackageHandle::from(p.alias_of.clone()),
+ _ => unreachable!("AliasPackageHandle invariant violated"),
+ }
+ }
+
+ pub fn set_root_package_alias(&self, value: bool) {
+ match &mut *self.0.borrow_mut() {
+ AnyPackage::AliasPackage(p) => p.set_root_package_alias(value),
+ AnyPackage::CompleteAliasPackage(p) => p.set_root_package_alias(value),
+ AnyPackage::RootAliasPackage(p) => p.set_root_package_alias(value),
+ _ => unreachable!("AliasPackageHandle invariant violated"),
+ }
+ }
+
+ pub fn is_root_package_alias(&self) -> bool {
+ match &*self.0.borrow() {
+ AnyPackage::AliasPackage(p) => p.is_root_package_alias(),
+ AnyPackage::CompleteAliasPackage(p) => p.is_root_package_alias(),
+ AnyPackage::RootAliasPackage(p) => p.is_root_package_alias(),
+ _ => unreachable!("AliasPackageHandle invariant violated"),
+ }
+ }
+
+ pub fn has_self_version_requires(&self) -> bool {
+ match &*self.0.borrow() {
+ AnyPackage::AliasPackage(p) => p.has_self_version_requires(),
+ AnyPackage::CompleteAliasPackage(p) => p.has_self_version_requires(),
+ AnyPackage::RootAliasPackage(p) => p.has_self_version_requires(),
+ _ => unreachable!("AliasPackageHandle invariant violated"),
+ }
+ }
+}
+
+impl CompleteAliasPackageHandle {
+ pub fn from_complete_alias_package(package: CompleteAliasPackage) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::CompleteAliasPackage(
+ package,
+ ))))
+ }
+
+ pub fn new(alias_of: CompletePackageHandle, version: String, pretty_version: String) -> Self {
+ Self::from_complete_alias_package(CompleteAliasPackage::new(
+ alias_of,
+ version,
+ pretty_version,
+ ))
+ }
+
+ /// PHP `getAliasOf()` narrowed to `CompletePackage`.
+ pub fn get_alias_of(&self) -> CompletePackageHandle {
+ match &*self.0.borrow() {
+ AnyPackage::CompleteAliasPackage(p) => p.alias_of.clone(),
+ AnyPackage::RootAliasPackage(p) => CompletePackageHandle::from(p.alias_of.clone()),
+ _ => unreachable!("CompleteAliasPackageHandle invariant violated"),
+ }
+ }
+}
+
+impl RootAliasPackageHandle {
+ pub fn from_root_alias_package(package: RootAliasPackage) -> Self {
+ Self(Rc::new(RefCell::new(AnyPackage::RootAliasPackage(package))))
+ }
+
+ pub fn new(alias_of: RootPackageHandle, version: String, pretty_version: String) -> Self {
+ Self::from_root_alias_package(RootAliasPackage::new(alias_of, version, pretty_version))
+ }
+
+ /// PHP `getAliasOf()` narrowed to `RootPackage`.
+ pub fn get_alias_of(&self) -> RootPackageHandle {
+ match &*self.0.borrow() {
+ AnyPackage::RootAliasPackage(p) => p.alias_of.clone(),
+ _ => unreachable!("RootAliasPackageHandle invariant violated"),
+ }
+ }
+}
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 87d6e35..e1cc60b 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -12,11 +12,14 @@ use shirabe_php_shim::{
use crate::package::CompleteAliasPackage;
use crate::package::CompletePackage;
+use crate::package::CompletePackageHandle;
use crate::package::CompletePackageInterface;
use crate::package::Link;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootAliasPackage;
use crate::package::RootPackage;
+use crate::package::RootPackageHandle;
use crate::package::loader::LoaderInterface;
use crate::package::version::VersionParser;
use crate::package::{BasePackage, SUPPORTED_LINK_TYPES};
@@ -51,7 +54,7 @@ impl LoaderInterface for ArrayLoader {
&self,
mut config: IndexMap<String, PhpMixed>,
class: Option<String>,
- ) -> Result<Box<dyn BasePackage>> {
+ ) -> Result<PackageInterfaceHandle> {
let class = class.unwrap_or_else(|| "Composer\\Package\\CompletePackage".to_string());
if class != "Composer\\Package\\CompletePackage"
@@ -104,8 +107,8 @@ impl ArrayLoader {
pub fn load_packages(
&self,
versions: Vec<IndexMap<String, PhpMixed>>,
- ) -> Result<Vec<Box<dyn BasePackage>>> {
- let mut packages: Vec<Box<dyn BasePackage>> = vec![];
+ ) -> Result<Vec<PackageInterfaceHandle>> {
+ let mut packages: Vec<PackageInterfaceHandle> = vec![];
let mut link_cache: IndexMap<
String,
IndexMap<String, IndexMap<String, IndexMap<String, (String, Link)>>>,
@@ -226,7 +229,7 @@ impl ArrayLoader {
&self,
mut package: Box<CompletePackage>,
config: &mut IndexMap<String, PhpMixed>,
- ) -> Result<Box<dyn BasePackage>> {
+ ) -> Result<PackageInterfaceHandle> {
// PHP: if (!$package instanceof CompletePackage) — true by construction in Rust
// (create_object always returns Box<CompletePackage>); kept as a no-op for parity.
let _ = LogicException {
@@ -593,12 +596,20 @@ impl ArrayLoader {
// TODO(phase-b): `$package instanceof RootPackage` downcast from CompletePackage
let package_as_root: Option<RootPackage> = None;
if let Some(root) = package_as_root {
- let _ = RootAliasPackage::new(root, alias_normalized, pretty_alias);
+ let _ = RootAliasPackage::new(
+ RootPackageHandle::from_root_package(root),
+ alias_normalized,
+ pretty_alias,
+ );
// TODO(phase-b): return Box<RootAliasPackage> wrapped as Box<BasePackage>
todo!("phase-b: return RootAliasPackage as Box<BasePackage>")
}
- let _ = CompleteAliasPackage::new(*package, alias_normalized, pretty_alias);
+ let _ = CompleteAliasPackage::new(
+ CompletePackageHandle::from_complete_package(*package),
+ alias_normalized,
+ pretty_alias,
+ );
// TODO(phase-b): return Box<CompleteAliasPackage> wrapped as Box<BasePackage>
todo!("phase-b: return CompleteAliasPackage as Box<BasePackage>")
}
diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs
index e0589bb..5f8cc4f 100644
--- a/crates/shirabe/src/package/loader/json_loader.rs
+++ b/crates/shirabe/src/package/loader/json_loader.rs
@@ -1,7 +1,7 @@
//! ref: composer/src/Composer/Package/Loader/JsonLoader.php
use crate::json::JsonFile;
-use crate::package::BasePackage;
+use crate::package::PackageInterfaceHandle;
use crate::package::loader::LoaderInterface;
use anyhow::Result;
use std::path::Path;
@@ -20,7 +20,7 @@ impl JsonLoader {
Self { loader }
}
- pub fn load(&self, json: JsonLoaderInput) -> Result<Box<dyn BasePackage>> {
+ pub fn load(&self, json: JsonLoaderInput) -> Result<PackageInterfaceHandle> {
let config = match json {
JsonLoaderInput::File(mut json_file) => json_file.read()?,
JsonLoaderInput::String(ref s) if Path::new(s).exists() => {
diff --git a/crates/shirabe/src/package/loader/loader_interface.rs b/crates/shirabe/src/package/loader/loader_interface.rs
index d10b7be..586905f 100644
--- a/crates/shirabe/src/package/loader/loader_interface.rs
+++ b/crates/shirabe/src/package/loader/loader_interface.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Package/Loader/LoaderInterface.php
-use crate::package::BasePackage;
+use crate::package::PackageInterfaceHandle;
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
@@ -9,5 +9,5 @@ pub trait LoaderInterface: std::fmt::Debug {
&self,
config: IndexMap<String, PhpMixed>,
class: Option<String>,
- ) -> anyhow::Result<Box<dyn BasePackage>>;
+ ) -> anyhow::Result<PackageInterfaceHandle>;
}
diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs
index 3cdaaa7..e235e31 100644
--- a/crates/shirabe/src/package/loader/root_package_loader.rs
+++ b/crates/shirabe/src/package/loader/root_package_loader.rs
@@ -66,7 +66,7 @@ impl RootPackageLoader {
config: IndexMap<String, Box<shirabe_php_shim::PhpMixed>>,
class: &str,
cwd: Option<&str>,
- ) -> anyhow::Result<Box<dyn PackageInterface>> {
+ ) -> anyhow::Result<crate::package::PackageInterfaceHandle> {
if class != "Composer\\Package\\RootPackage" {
shirabe_php_shim::trigger_error(
"The $class arg is deprecated, please reach out to Composer maintainers ASAP if you still need this.",
@@ -193,11 +193,14 @@ impl RootPackageLoader {
Some("Composer\\Package\\RootPackage".to_string()),
)?;
- // TODO(phase-b): as_any_mut is not available on BasePackage; downcast via Any is not
- // possible without it. Skipping real downcast and using todo!() placeholder.
+ // TODO(phase-c): mutating the loaded RootPackage through a PackageInterfaceHandle
+ // requires going through as_root_package() + a RefCell borrow; the inherent
+ // RootPackage mutators used below are not yet reachable that way.
let real_package: &mut RootPackage = {
let _ = &mut package;
- todo!("downcast Box<dyn BasePackage> to &mut RootPackage requires as_any_mut on trait")
+ todo!(
+ "mutate RootPackage through PackageInterfaceHandle (as_root_package + borrow_mut)"
+ )
};
if auto_versioned {
diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs
index a2dd5a7..fbaa0f8 100644
--- a/crates/shirabe/src/package/loader/validating_array_loader.rs
+++ b/crates/shirabe/src/package/loader/validating_array_loader.rs
@@ -66,7 +66,7 @@ impl ValidatingArrayLoader {
&mut self,
config: IndexMap<String, Box<PhpMixed>>,
class: &str,
- ) -> anyhow::Result<Box<dyn BasePackage>> {
+ ) -> anyhow::Result<crate::package::PackageInterfaceHandle> {
self.errors = Vec::new();
self.warnings = Vec::new();
self.config = config.clone();
diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs
index 9522e55..bdeb391 100644
--- a/crates/shirabe/src/package/locker.rs
+++ b/crates/shirabe/src/package/locker.rs
@@ -15,11 +15,11 @@ use shirabe_php_shim::{
use crate::installer::InstallationManager;
use crate::io::IOInterface;
use crate::json::JsonFile;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::CompleteAliasPackage;
use crate::package::Link;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterface;
use crate::package::dumper::ArrayDumper;
use crate::package::loader::ArrayLoader;
@@ -219,15 +219,15 @@ impl Locker {
false
};
if has_name {
- let mut package_by_name: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut package_by_name: IndexMap<String, BasePackageHandle> = IndexMap::new();
if let PhpMixed::List(list) = locked_packages {
for info in list {
if let PhpMixed::Array(m) = info.as_ref() {
let info_map: IndexMap<String, PhpMixed> =
m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect();
let package = self.loader.load(info_map, None)?;
- // TODO(phase-b): PHP shares the package between repository and map (Rc<dyn BasePackage>)
- let _name = package.get_name().to_string();
+ // PHP shares the package between repository and map; the handle is the shared Rc.
+ let _name = package.get_name();
let _ = (&mut packages, &mut package_by_name, package);
todo!(
"packages.add_package(package); package_by_name.insert(name, package); + AliasPackage downcast"
@@ -245,10 +245,11 @@ impl Locker {
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- // TODO(phase-b): Box<dyn BasePackage> is not Clone; PHP semantics need Rc<dyn BasePackage>
if let Some(base_pkg) = package_by_name.get(&alias_pkg_name) {
let mut alias_pkg = CompleteAliasPackage::new(
- todo!("phase-b: downcast Box<BasePackage> to CompletePackage"),
+ todo!(
+ "phase-c: narrow base_pkg handle to CompletePackageHandle"
+ ),
m.get("alias_normalized")
.and_then(|v| v.as_string())
.unwrap_or("")
@@ -463,8 +464,8 @@ impl Locker {
/// Locks provided data into lockfile.
pub fn set_lock_data(
&mut self,
- packages: Vec<Box<dyn PackageInterface>>,
- dev_packages: Option<Vec<Box<dyn PackageInterface>>>,
+ packages: Vec<PackageInterfaceHandle>,
+ dev_packages: Option<Vec<PackageInterfaceHandle>>,
platform_reqs: IndexMap<String, String>,
platform_dev_reqs: IndexMap<String, String>,
aliases: Vec<IndexMap<String, PhpMixed>>,
@@ -743,13 +744,11 @@ impl Locker {
}
/// @param PackageInterface[] $packages
- fn lock_packages(&mut self, packages: &[Box<dyn PackageInterface>]) -> Result<PhpMixed> {
+ fn lock_packages(&mut self, packages: &[PackageInterfaceHandle]) -> Result<PhpMixed> {
let mut locked: Vec<IndexMap<String, PhpMixed>> = vec![];
for package in packages {
- // TODO(phase-b): `$package instanceof AliasPackage` downcast
- let package_as_alias: Option<&AliasPackage> = None;
- if package_as_alias.is_some() {
+ if package.as_alias().is_some() {
continue;
}
@@ -767,15 +766,20 @@ impl Locker {
.into());
}
- let mut spec = self.dumper.dump(&**package);
+ let mut spec = self
+ .dumper
+ .dump(package.as_rc().borrow().as_package_interface());
spec.shift_remove("version_normalized");
// always move time to the end of the package definition
let time = spec.get("time").cloned();
spec.shift_remove("time");
- let time = if package.is_dev() && package.get_installation_source() == Some("source") {
+ let time = if package.is_dev()
+ && package.get_installation_source() == Some("source".to_string())
+ {
// use the exact commit time of the current reference if it's a dev package
- let pkg_time = self.get_package_time(&**package)?;
+ let pkg_time =
+ self.get_package_time(package.as_rc().borrow().as_package_interface())?;
pkg_time.map(PhpMixed::String).or(time)
} else {
time
@@ -976,11 +980,10 @@ impl Locker {
.find_packages_with_replacers_and_providers(&link.get_target(), None);
if !results.is_empty() {
- // TODO(phase-b): reset_first requires Clone on dyn BasePackage; PHP returns shared reference
- let provider: &Box<dyn BasePackage> =
- todo!("reset_first(&results) shared ref");
+ // PHP `reset($results)` returns the first shared package; clone the handle.
+ let provider: BasePackageHandle = results.first().unwrap().clone();
let _ = &results;
- let mut description = provider.get_pretty_version().to_string();
+ let mut description = provider.get_pretty_version();
if provider.get_name() != link.get_target() {
'outer: for (method, text) in [
("getReplaces", "replaced as %s by %s"),
diff --git a/crates/shirabe/src/package/mod.rs b/crates/shirabe/src/package/mod.rs
index 611f36a..903056f 100644
--- a/crates/shirabe/src/package/mod.rs
+++ b/crates/shirabe/src/package/mod.rs
@@ -6,6 +6,7 @@ pub mod complete_alias_package;
pub mod complete_package;
pub mod complete_package_interface;
pub mod dumper;
+pub mod handle;
pub mod link;
pub mod loader;
pub mod locker;
@@ -24,6 +25,7 @@ pub use complete_alias_package::*;
pub use complete_package::*;
pub use complete_package_interface::*;
pub use dumper::*;
+pub use handle::*;
pub use link::*;
pub use loader::*;
pub use locker::*;
diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs
index 75b4e54..da8451e 100644
--- a/crates/shirabe/src/package/package.rs
+++ b/crates/shirabe/src/package/package.rs
@@ -570,10 +570,6 @@ impl BasePackage for Package {
fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>> {
todo!()
}
-
- fn clone_box(&self) -> Box<dyn BasePackage> {
- todo!()
- }
}
impl std::fmt::Display for Package {
diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs
index 1c9cf2e..97aac26 100644
--- a/crates/shirabe/src/package/package_interface.rs
+++ b/crates/shirabe/src/package/package_interface.rs
@@ -290,14 +290,6 @@ pub trait PackageInterface: std::fmt::Display + std::fmt::Debug {
/// Set dist and source references and update dist URL for ones that contain a reference
fn set_source_dist_references(&mut self, reference: &str);
- // clone_box was moved to BasePackage with a Box<dyn BasePackage> return type;
- // exposing it here too caused trait-method ambiguity at every BasePackage call site.
- // Callers holding `&dyn PackageInterface` (rather than `&dyn BasePackage`) can use
- // `clone_package_box` instead.
- fn clone_package_box(&self) -> Box<dyn PackageInterface> {
- todo!()
- }
-
fn as_alias_package(&self) -> Option<&crate::package::AliasPackage> {
None
}
diff --git a/crates/shirabe/src/package/root_alias_package.rs b/crates/shirabe/src/package/root_alias_package.rs
index d6dafa9..7298c1f 100644
--- a/crates/shirabe/src/package/root_alias_package.rs
+++ b/crates/shirabe/src/package/root_alias_package.rs
@@ -1,51 +1,77 @@
//! ref: composer/src/Composer/Package/RootAliasPackage.php
-use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
use crate::package::CompleteAliasPackage;
+use crate::package::CompletePackageHandle;
use crate::package::CompletePackageInterface;
use crate::package::Link;
-use crate::package::PackageInterface;
-use crate::package::RootPackage;
+use crate::package::RootPackageHandle;
use crate::package::RootPackageInterface;
-use crate::repository::RepositoryInterface;
+use crate::package::handle::delegate_package_interface_to_inner;
#[derive(Debug)]
pub struct RootAliasPackage {
inner: CompleteAliasPackage,
// overrides CompleteAliasPackage::alias_of with the more specific RootPackage type
- pub(crate) alias_of: RootPackage,
+ pub(crate) alias_of: RootPackageHandle,
}
impl RootAliasPackage {
- pub fn new(alias_of: RootPackage, version: String, pretty_version: String) -> Self {
- // TODO(phase-b): RootPackage.inner (CompletePackage) is not accessible here
- let inner: CompleteAliasPackage = todo!();
+ pub fn new(alias_of: RootPackageHandle, version: String, pretty_version: String) -> Self {
+ let inner = CompleteAliasPackage::new(
+ CompletePackageHandle::from(alias_of.clone()),
+ version,
+ pretty_version,
+ );
Self { inner, alias_of }
}
- pub fn get_alias_of(&self) -> &RootPackage {
- &self.alias_of
+ pub fn get_alias_of(&self) -> RootPackageHandle {
+ self.alias_of.clone()
+ }
+
+ pub fn set_root_package_alias(&mut self, value: bool) {
+ self.inner.set_root_package_alias(value);
+ }
+
+ pub fn is_root_package_alias(&self) -> bool {
+ self.inner.is_root_package_alias()
+ }
+
+ pub fn has_self_version_requires(&self) -> bool {
+ self.inner.has_self_version_requires()
+ }
+}
+
+delegate_package_interface_to_inner!(RootAliasPackage, inner);
+
+impl std::fmt::Display for RootAliasPackage {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.inner, f)
}
}
impl RootPackageInterface for RootAliasPackage {
fn get_aliases(&self) -> &[IndexMap<String, String>] {
- self.alias_of.get_aliases()
+ todo!("RootAliasPackage::get_aliases cannot return a borrow across the aliasOf handle")
}
fn get_minimum_stability(&self) -> &str {
- self.alias_of.get_minimum_stability()
+ todo!(
+ "RootAliasPackage::get_minimum_stability cannot return &str across the aliasOf handle"
+ )
}
fn get_stability_flags(&self) -> &IndexMap<String, i64> {
- self.alias_of.get_stability_flags()
+ todo!(
+ "RootAliasPackage::get_stability_flags cannot return a borrow across the aliasOf handle"
+ )
}
fn get_references(&self) -> &IndexMap<String, String> {
- self.alias_of.get_references()
+ todo!("RootAliasPackage::get_references cannot return a borrow across the aliasOf handle")
}
fn get_prefer_stable(&self) -> bool {
@@ -53,31 +79,28 @@ impl RootPackageInterface for RootAliasPackage {
}
fn get_config(&self) -> &IndexMap<String, PhpMixed> {
- self.alias_of.get_config()
+ todo!("RootAliasPackage::get_config cannot return a borrow across the aliasOf handle")
}
fn set_requires(&mut self, requires: Vec<Link>) {
- // TODO(phase-b): self.inner.requires = self.replace_self_version_dependencies(requires.clone(), Link::TYPE_REQUIRE)
+ // TODO(phase-c): PHP re-derives the local links via
+ // replaceSelfVersionDependencies before forwarding to aliasOf.
self.alias_of.set_requires(requires);
}
fn set_dev_requires(&mut self, dev_requires: Vec<Link>) {
- // TODO(phase-b): self.inner.dev_requires = self.replace_self_version_dependencies(dev_requires.clone(), Link::TYPE_DEV_REQUIRE)
self.alias_of.set_dev_requires(dev_requires);
}
fn set_conflicts(&mut self, conflicts: Vec<Link>) {
- // TODO(phase-b): self.inner.conflicts = self.replace_self_version_dependencies(conflicts.clone(), Link::TYPE_CONFLICT)
self.alias_of.set_conflicts(conflicts);
}
fn set_provides(&mut self, provides: Vec<Link>) {
- // TODO(phase-b): self.inner.provides = self.replace_self_version_dependencies(provides.clone(), Link::TYPE_PROVIDE)
self.alias_of.set_provides(provides);
}
fn set_replaces(&mut self, replaces: Vec<Link>) {
- // TODO(phase-b): self.inner.replaces = self.replace_self_version_dependencies(replaces.clone(), Link::TYPE_REPLACE)
self.alias_of.set_replaces(replaces);
}
@@ -124,274 +147,102 @@ impl RootPackageInterface for RootAliasPackage {
impl CompletePackageInterface for RootAliasPackage {
fn get_scripts(&self) -> IndexMap<String, Vec<String>> {
- todo!()
+ self.inner.get_scripts()
}
fn set_scripts(&mut self, scripts: IndexMap<String, Vec<String>>) {
- todo!()
+ self.inner.set_scripts(scripts);
}
fn get_repositories(&self) -> Vec<IndexMap<String, PhpMixed>> {
- todo!()
+ self.inner.get_repositories()
}
fn set_repositories(&mut self, repositories: Vec<IndexMap<String, PhpMixed>>) {
- todo!()
+ self.inner.set_repositories(repositories);
}
fn get_license(&self) -> Vec<String> {
- todo!()
+ self.inner.get_license()
}
fn set_license(&mut self, license: Vec<String>) {
- todo!()
+ self.inner.set_license(license);
}
fn get_keywords(&self) -> Vec<String> {
- todo!()
+ self.inner.get_keywords()
}
fn set_keywords(&mut self, keywords: Vec<String>) {
- todo!()
+ self.inner.set_keywords(keywords);
}
fn get_description(&self) -> Option<&str> {
- todo!()
+ self.inner.get_description()
}
fn set_description(&mut self, description: String) {
- todo!()
+ self.inner.set_description(description);
}
fn get_homepage(&self) -> Option<&str> {
- todo!()
+ self.inner.get_homepage()
}
fn set_homepage(&mut self, homepage: String) {
- todo!()
+ self.inner.set_homepage(homepage);
}
fn get_authors(&self) -> Vec<IndexMap<String, String>> {
- todo!()
+ self.inner.get_authors()
}
fn set_authors(&mut self, authors: Vec<IndexMap<String, String>>) {
- todo!()
+ self.inner.set_authors(authors);
}
fn get_support(&self) -> IndexMap<String, String> {
- todo!()
+ self.inner.get_support()
}
fn set_support(&mut self, support: IndexMap<String, String>) {
- todo!()
+ self.inner.set_support(support);
}
fn get_funding(&self) -> Vec<IndexMap<String, PhpMixed>> {
- todo!()
+ self.inner.get_funding()
}
fn set_funding(&mut self, funding: Vec<IndexMap<String, PhpMixed>>) {
- todo!()
+ self.inner.set_funding(funding);
}
fn is_abandoned(&self) -> bool {
- todo!()
+ self.inner.is_abandoned()
}
fn get_replacement_package(&self) -> Option<&str> {
- todo!()
+ self.inner.get_replacement_package()
}
fn set_abandoned(&mut self, abandoned: PhpMixed) {
- todo!()
+ self.inner.set_abandoned(abandoned);
}
fn get_archive_name(&self) -> Option<&str> {
- todo!()
+ self.inner.get_archive_name()
}
fn set_archive_name(&mut self, name: String) {
- todo!()
+ self.inner.set_archive_name(name);
}
fn get_archive_excludes(&self) -> Vec<String> {
- todo!()
+ self.inner.get_archive_excludes()
}
fn set_archive_excludes(&mut self, excludes: Vec<String>) {
- todo!()
- }
-}
-
-impl std::fmt::Display for RootAliasPackage {
- fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- todo!()
- }
-}
-
-impl PackageInterface for RootAliasPackage {
- fn as_any(&self) -> &dyn std::any::Any {
- self
- }
-
- fn get_name(&self) -> &str {
- todo!()
- }
- fn get_pretty_name(&self) -> &str {
- todo!()
- }
- fn get_names(&self, _provides: bool) -> Vec<String> {
- todo!()
- }
- fn set_id(&mut self, _id: i64) {
- todo!()
- }
- fn get_id(&self) -> i64 {
- todo!()
- }
- fn is_dev(&self) -> bool {
- todo!()
- }
- fn get_type(&self) -> &str {
- todo!()
- }
- fn get_target_dir(&self) -> Option<&str> {
- todo!()
- }
- fn get_extra(&self) -> IndexMap<String, PhpMixed> {
- todo!()
- }
- fn set_installation_source(&mut self, _type: Option<String>) {
- todo!()
- }
- fn get_installation_source(&self) -> Option<&str> {
- todo!()
- }
- fn get_source_type(&self) -> Option<&str> {
- todo!()
- }
- fn get_source_url(&self) -> Option<&str> {
- todo!()
- }
- fn get_source_urls(&self) -> Vec<String> {
- todo!()
- }
- fn get_source_reference(&self) -> Option<&str> {
- todo!()
- }
- fn get_source_mirrors(&self) -> Option<Vec<IndexMap<String, PhpMixed>>> {
- todo!()
- }
- fn set_source_mirrors(&mut self, _mirrors: Option<Vec<IndexMap<String, PhpMixed>>>) {
- todo!()
- }
- fn get_dist_type(&self) -> Option<&str> {
- todo!()
- }
- fn get_dist_url(&self) -> Option<&str> {
- todo!()
- }
- fn get_dist_urls(&self) -> Vec<String> {
- todo!()
- }
- fn get_dist_reference(&self) -> Option<&str> {
- todo!()
- }
- fn get_dist_sha1_checksum(&self) -> Option<&str> {
- todo!()
- }
- fn get_dist_mirrors(&self) -> Option<Vec<IndexMap<String, PhpMixed>>> {
- todo!()
- }
- fn set_dist_mirrors(&mut self, _mirrors: Option<Vec<IndexMap<String, PhpMixed>>>) {
- todo!()
- }
- fn get_version(&self) -> &str {
- todo!()
- }
- fn get_pretty_version(&self) -> &str {
- todo!()
- }
- fn get_full_pretty_version(&self, _truncate: bool, _display_mode: i64) -> String {
- todo!()
- }
- fn get_release_date(&self) -> Option<DateTime<Utc>> {
- todo!()
- }
- fn get_stability(&self) -> &str {
- todo!()
- }
- fn get_requires(&self) -> IndexMap<String, Link> {
- todo!()
- }
- fn get_conflicts(&self) -> IndexMap<String, Link> {
- todo!()
- }
- fn get_provides(&self) -> IndexMap<String, Link> {
- todo!()
- }
- fn get_replaces(&self) -> IndexMap<String, Link> {
- todo!()
- }
- fn get_dev_requires(&self) -> IndexMap<String, Link> {
- todo!()
- }
- fn get_suggests(&self) -> IndexMap<String, String> {
- todo!()
- }
- fn get_autoload(&self) -> IndexMap<String, PhpMixed> {
- todo!()
- }
- fn get_dev_autoload(&self) -> IndexMap<String, PhpMixed> {
- todo!()
- }
- fn get_include_paths(&self) -> Vec<String> {
- todo!()
- }
- fn get_php_ext(&self) -> Option<IndexMap<String, PhpMixed>> {
- todo!()
- }
- fn set_repository(&mut self, _repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> {
- todo!()
- }
- fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
- todo!()
- }
- fn get_binaries(&self) -> Vec<String> {
- todo!()
- }
- fn get_unique_name(&self) -> String {
- todo!()
- }
- fn get_notification_url(&self) -> Option<&str> {
- todo!()
- }
- fn get_pretty_string(&self) -> String {
- todo!()
- }
- fn is_default_branch(&self) -> bool {
- todo!()
- }
- fn get_transport_options(&self) -> IndexMap<String, PhpMixed> {
- todo!()
- }
- fn set_transport_options(&mut self, _options: IndexMap<String, PhpMixed>) {
- todo!()
- }
- fn set_source_reference(&mut self, _reference: Option<String>) {
- todo!()
- }
- fn set_dist_url(&mut self, _url: Option<String>) {
- todo!()
- }
- fn set_dist_type(&mut self, _type: Option<String>) {
- todo!()
- }
- fn set_dist_reference(&mut self, _reference: Option<String>) {
- todo!()
- }
- fn set_source_dist_references(&mut self, _reference: &str) {
- todo!()
+ self.inner.set_archive_excludes(excludes);
}
}
diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs
index 7e79530..b0fdf26 100644
--- a/crates/shirabe/src/package/root_package.rs
+++ b/crates/shirabe/src/package/root_package.rs
@@ -26,9 +26,7 @@ impl RootPackage {
pub const DEFAULT_PRETTY_VERSION: &'static str = "1.0.0+no-version-set";
pub fn new(name: String, version: String, pretty_version: String) -> Self {
- // TODO(phase-b): CompletePackage::new signature is not yet pinned down
- let inner: CompletePackage = todo!();
- let _ = (name, version, pretty_version);
+ let inner = CompletePackage::new(name, version, pretty_version);
Self {
inner,
minimum_stability: "stable".to_string(),
diff --git a/crates/shirabe/src/package/root_package_interface.rs b/crates/shirabe/src/package/root_package_interface.rs
index 2370e25..5f47232 100644
--- a/crates/shirabe/src/package/root_package_interface.rs
+++ b/crates/shirabe/src/package/root_package_interface.rs
@@ -51,14 +51,6 @@ pub trait RootPackageInterface: CompletePackageInterface {
fn set_extra(&mut self, extra: IndexMap<String, PhpMixed>);
- fn clone_as_package_interface(&self) -> Box<dyn PackageInterface> {
- todo!()
- }
-
- fn clone_box(&self) -> Box<dyn RootPackageInterface> {
- todo!()
- }
-
fn as_package_interface(&self) -> &dyn PackageInterface {
todo!()
}
diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs
index 3bd0191..28b9277 100644
--- a/crates/shirabe/src/package/version/version_selector.rs
+++ b/crates/shirabe/src/package/version/version_selector.rs
@@ -16,9 +16,8 @@ use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFil
use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
use crate::io::IOInterface;
-use crate::package::AliasPackage;
use crate::package::PackageInterface;
-use crate::package::base_package::{self, BasePackage};
+use crate::package::base_package;
use crate::package::dumper::ArrayDumper;
use crate::package::loader::ArrayLoader;
use crate::package::version::VersionParser;
@@ -69,7 +68,7 @@ impl VersionSelector {
repo_set_flags: i64,
io: Option<&dyn IOInterface>,
show_warnings: shirabe_php_shim::PhpMixed,
- ) -> anyhow::Result<Option<Box<dyn PackageInterface>>> {
+ ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> {
if !base_package::STABILITIES.contains_key(preferred_stability) {
return Err(shirabe_php_shim::UnexpectedValueException {
message: format!(
@@ -99,8 +98,14 @@ impl VersionSelector {
let min_priority = *base_package::STABILITIES.get(preferred_stability).unwrap();
candidates.sort_by(|a, b| {
- let a_priority = a.get_stability_priority();
- let b_priority = b.get_stability_priority();
+ // BasePackage::get_stability_priority() is not forwarded by the handle; compute it
+ // directly from the stability name.
+ let a_priority = *base_package::STABILITIES
+ .get(a.get_stability().as_str())
+ .unwrap();
+ let b_priority = *base_package::STABILITIES
+ .get(b.get_stability().as_str())
+ .unwrap();
if min_priority < a_priority && b_priority < a_priority {
return std::cmp::Ordering::Greater;
@@ -112,9 +117,9 @@ impl VersionSelector {
return std::cmp::Ordering::Less;
}
- if version_compare(b.get_version(), a.get_version(), ">") {
+ if version_compare(&b.get_version(), &a.get_version(), ">") {
std::cmp::Ordering::Greater
- } else if version_compare(b.get_version(), a.get_version(), "<") {
+ } else if version_compare(&b.get_version(), &a.get_version(), "<") {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Equal
@@ -127,11 +132,11 @@ impl VersionSelector {
.downcast_ref::<IgnoreAllPlatformRequirementFilter>()
.is_some();
- let package: Option<Box<dyn PackageInterface>>;
+ let package: Option<crate::package::PackageInterfaceHandle>;
if !self.platform_constraints.is_empty() && !is_ignore_all {
let mut already_warned_names: IndexMap<String, bool> = IndexMap::new();
let mut already_seen_names: IndexMap<String, bool> = IndexMap::new();
- let mut found_package: Option<Box<dyn PackageInterface>> = None;
+ let mut found_package: Option<crate::package::PackageInterfaceHandle> = None;
'pkgs: for pkg in candidates.iter() {
let reqs = pkg.get_requires();
@@ -171,7 +176,7 @@ impl VersionSelector {
reason = "is missing from your platform";
}
- let is_latest_version = !already_seen_names.contains_key(pkg.get_name());
+ let is_latest_version = !already_seen_names.contains_key(&pkg.get_name());
already_seen_names.insert(pkg.get_name().to_string(), true);
if let Some(io) = io {
let should_warn = match &show_warnings {
@@ -215,13 +220,13 @@ impl VersionSelector {
continue;
}
- found_package = Some(pkg.clone_box());
+ found_package = Some(pkg.clone().into());
break;
}
package = found_package;
} else {
package = if !candidates.is_empty() {
- Some(candidates.remove(0).clone_package_box())
+ Some(candidates.remove(0).into())
} else {
None
};
@@ -232,10 +237,9 @@ impl VersionSelector {
Some(p) => p,
};
- let package = if let Some(alias) = package.as_ref().as_any().downcast_ref::<AliasPackage>()
- {
+ let package = if let Some(alias) = package.as_alias() {
if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
- alias.get_alias_of().clone_package_box()
+ alias.get_alias_of().into()
} else {
package
}
diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs
index 77ac577..ddde387 100644
--- a/crates/shirabe/src/plugin/plugin_manager.rs
+++ b/crates/shirabe/src/plugin/plugin_manager.rs
@@ -25,6 +25,7 @@ use crate::package::CompletePackage;
use crate::package::Link;
use crate::package::Locker;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterface;
use crate::package::base_package::{self, BasePackage};
use crate::package::version::VersionParser;
@@ -143,10 +144,20 @@ impl PluginManager {
.borrow()
.get_local_repository()
.clone_box();
- // The root package borrow is also tied to `self.composer`; clone the package box
- // for the same reason as above.
- let root_package = self.composer_full().borrow().get_package().clone_box();
- self.load_repository(&*repo, false, Some(&*root_package))?;
+ // The root package borrow is also tied to `self.composer`; clone the package handle
+ // (shared Rc) for the same reason as above.
+ let root_package = self.composer_full().borrow().get_package().clone();
+ self.load_repository(
+ &*repo,
+ false,
+ Some(
+ root_package
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .unwrap(),
+ ),
+ )?;
}
if self.global_composer.is_some() && !self.are_plugins_disabled("global") {
@@ -532,23 +543,18 @@ impl PluginManager {
}
let sorted_packages = PackageSorter::sort_packages(
- packages.iter().map(|p| p.clone_package_box()).collect(),
+ packages.iter().map(|p| p.clone().into()).collect(),
weights,
);
- let required_packages: Vec<Box<dyn PackageInterface>> = if !is_global_repo {
+ let required_packages: Vec<crate::package::BasePackageHandle> = if !is_global_repo {
// PHP: $requiredPackages = RepositoryUtils::filterRequiredPackages($packages, $rootPackage, true);
- // RepositoryUtils::filter_required_packages takes &[Box<dyn BasePackage>] plus a bucket.
- // We need to convert &[Box<dyn BasePackage>] from packages.
- let bucket: Vec<Box<dyn crate::package::BasePackage>> = vec![];
+ let bucket: Vec<crate::package::BasePackageHandle> = vec![];
RepositoryUtils::filter_required_packages(
packages.as_slice(),
- root_package.unwrap(),
+ root_package.unwrap() as &dyn PackageInterface,
true,
bucket,
)
- .iter()
- .map(|p| p.clone_package_box())
- .collect()
} else {
vec![]
};
@@ -565,26 +571,33 @@ impl PluginManager {
}
// PHP: !in_array($package, $requiredPackages, true) — identity-based comparison.
- // Compare data pointers since `sorted_packages` and `required_packages` are both
- // `Box<dyn PackageInterface>`.
- let package_addr =
- package.as_ref() as *const dyn PackageInterface as *const () as usize;
- let in_required = required_packages.iter().any(|rp| {
- (rp.as_ref() as *const dyn PackageInterface as *const () as usize) == package_addr
- });
+ // Both `sorted_packages` and `required_packages` are package handles, so compare
+ // by shared-Rc pointer identity.
+ let package_addr = package.ptr_id();
+ let in_required = required_packages
+ .iter()
+ .any(|rp| rp.ptr_id() == package_addr);
if !is_global_repo
&& !in_required
- && !self.is_plugin_allowed(package.get_name(), false, true, false)?
+ && !self.is_plugin_allowed(&package.get_name(), false, true, false)?
{
self.io.write_error(&format!("<warning>The \"{}\" plugin was not loaded as it is not listed in allow-plugins and is not required by the root package anymore.</warning>", package.get_name()));
continue;
}
if "composer-plugin" == package.get_type() {
- self.register_package(&**package, false, is_global_repo)?;
+ self.register_package(
+ package.as_rc().borrow().as_package_interface(),
+ false,
+ is_global_repo,
+ )?;
// Backward compatibility
} else if "composer-installer" == package.get_type() {
- self.register_package(&**package, false, is_global_repo)?;
+ self.register_package(
+ package.as_rc().borrow().as_package_interface(),
+ false,
+ is_global_repo,
+ )?;
}
let _ = cp;
}
@@ -596,7 +609,7 @@ impl PluginManager {
let packages = repo.get_packages();
// PHP: $sortedPackages = array_reverse(PackageSorter::sortPackages($packages));
let mut sorted_packages = PackageSorter::sort_packages(
- packages.iter().map(|p| p.clone_package_box()).collect(),
+ packages.iter().map(|p| p.clone().into()).collect(),
IndexMap::new(),
);
sorted_packages.reverse();
@@ -606,10 +619,10 @@ impl PluginManager {
continue;
}
if "composer-plugin" == package.get_type() {
- self.deactivate_package(&**package);
+ self.deactivate_package(package.as_rc().borrow().as_package_interface());
// Backward compatibility
} else if "composer-installer" == package.get_type() {
- self.deactivate_package(&**package);
+ self.deactivate_package(package.as_rc().borrow().as_package_interface());
}
}
}
@@ -617,21 +630,21 @@ impl PluginManager {
fn collect_dependencies(
&self,
installed_repo: &InstalledRepository,
- mut collected: IndexMap<String, Box<dyn PackageInterface>>,
+ mut collected: IndexMap<String, PackageInterfaceHandle>,
package: &dyn PackageInterface,
- ) -> IndexMap<String, Box<dyn PackageInterface>> {
+ ) -> IndexMap<String, PackageInterfaceHandle> {
// TODO(plugin): used by registerPackage to assemble plugin dependency autoload map
for (_k, require_link) in &package.get_requires() {
for required_package in installed_repo
.find_packages_with_replacers_and_providers(require_link.get_target(), None)
{
- if !collected.contains_key(required_package.get_name()) {
- collected.insert(
- required_package.get_name().to_string(),
- required_package.clone_package_box(),
+ if !collected.contains_key(&required_package.get_name()) {
+ collected.insert(required_package.get_name(), required_package.clone().into());
+ collected = self.collect_dependencies(
+ installed_repo,
+ collected,
+ required_package.as_rc().borrow().as_package_interface(),
);
- collected =
- self.collect_dependencies(installed_repo, collected, &*required_package);
}
}
}
diff --git a/crates/shirabe/src/plugin/pre_pool_create_event.rs b/crates/shirabe/src/plugin/pre_pool_create_event.rs
index f4426f7..2bbaa6c 100644
--- a/crates/shirabe/src/plugin/pre_pool_create_event.rs
+++ b/crates/shirabe/src/plugin/pre_pool_create_event.rs
@@ -4,7 +4,7 @@ use indexmap::IndexMap;
use crate::dependency_resolver::Request;
use crate::event_dispatcher::Event;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::repository::RepositoryInterface;
#[derive(Debug)]
@@ -16,8 +16,8 @@ pub struct PrePoolCreateEvent {
stability_flags: IndexMap<String, i64>,
root_aliases: IndexMap<String, IndexMap<String, IndexMap<String, String>>>,
root_references: IndexMap<String, String>,
- packages: Vec<Box<dyn BasePackage>>,
- unacceptable_fixed_packages: Vec<Box<dyn BasePackage>>,
+ packages: Vec<BasePackageHandle>,
+ unacceptable_fixed_packages: Vec<BasePackageHandle>,
}
impl PrePoolCreateEvent {
@@ -34,8 +34,8 @@ impl PrePoolCreateEvent {
stability_flags: IndexMap<String, i64>,
root_aliases: IndexMap<String, IndexMap<String, IndexMap<String, String>>>,
root_references: IndexMap<String, String>,
- packages: Vec<Box<dyn BasePackage>>,
- unacceptable_fixed_packages: Vec<Box<dyn BasePackage>>,
+ packages: Vec<BasePackageHandle>,
+ unacceptable_fixed_packages: Vec<BasePackageHandle>,
) -> Self {
Self {
inner: Event::new(name, vec![], IndexMap::new()),
@@ -76,19 +76,19 @@ impl PrePoolCreateEvent {
&self.root_references
}
- pub fn get_packages(&self) -> &Vec<Box<dyn BasePackage>> {
+ pub fn get_packages(&self) -> &Vec<BasePackageHandle> {
&self.packages
}
- pub fn get_unacceptable_fixed_packages(&self) -> &Vec<Box<dyn BasePackage>> {
+ pub fn get_unacceptable_fixed_packages(&self) -> &Vec<BasePackageHandle> {
&self.unacceptable_fixed_packages
}
- pub fn set_packages(&mut self, packages: Vec<Box<dyn BasePackage>>) {
+ pub fn set_packages(&mut self, packages: Vec<BasePackageHandle>) {
self.packages = packages;
}
- pub fn set_unacceptable_fixed_packages(&mut self, packages: Vec<Box<dyn BasePackage>>) {
+ pub fn set_unacceptable_fixed_packages(&mut self, packages: Vec<BasePackageHandle>) {
self.unacceptable_fixed_packages = packages;
}
}
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index fe45849..16f5f30 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -6,19 +6,14 @@ use std::cell::RefCell;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::Preg;
-use shirabe_php_shim::{
- Countable, InvalidArgumentException, LogicException, implode, preg_quote, spl_object_hash,
- strtolower,
-};
+use shirabe_php_shim::{Countable, LogicException, implode, preg_quote, strtolower};
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::SimpleConstraint;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
-use crate::package::CompleteAliasPackage;
-use crate::package::CompletePackage;
-use crate::package::CompletePackageInterface;
+use crate::package::BasePackageHandle;
+use crate::package::PackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::version::StabilityFilter;
use crate::package::version::VersionParser;
use crate::repository::{
@@ -31,15 +26,15 @@ use crate::repository::{
pub struct ArrayRepository {
/// @var ?array<BasePackage>
// TODO(phase-b): RefCell models PHP's lazy init via getPackages()/count() under &self
- pub(crate) packages: RefCell<Option<Vec<Box<dyn BasePackage>>>>,
+ pub(crate) packages: RefCell<Option<Vec<BasePackageHandle>>>,
/// @var ?array<BasePackage> indexed by package unique name and used to cache hasPackage calls
- pub(crate) package_map: RefCell<Option<IndexMap<String, Box<dyn BasePackage>>>>,
+ pub(crate) package_map: RefCell<Option<IndexMap<String, BasePackageHandle>>>,
}
impl ArrayRepository {
/// @param array<PackageInterface> $packages
- pub fn new(packages: Vec<Box<dyn PackageInterface>>) -> Result<Self> {
+ pub fn new(packages: Vec<PackageInterfaceHandle>) -> Result<Self> {
let this = Self {
packages: RefCell::new(None),
package_map: RefCell::new(None),
@@ -51,41 +46,27 @@ impl ArrayRepository {
}
/// Adds a new package to the repository
- pub fn add_package(&self, package: Box<dyn PackageInterface>) -> Result<()> {
- // PHP: if (!$package instanceof BasePackage) throw new \InvalidArgumentException(...)
- // TODO(phase-b): need a real `instanceof BasePackage` check on dyn PackageInterface;
- // dyn-trait downcast requires Sized. Defer until BasePackage exposes an `as_base_package`.
- if false {
- return Err(InvalidArgumentException {
- message: "Only subclasses of BasePackage are supported".to_string(),
- code: 0,
- }
- .into());
- }
- // TODO(phase-b): convert Box<dyn PackageInterface> to Box<dyn BasePackage>
- let mut package: Box<dyn BasePackage> =
- todo!("downcast Box<dyn PackageInterface> to Box<dyn BasePackage>");
-
+ pub fn add_package(&self, package: PackageInterfaceHandle) -> Result<()> {
if self.packages.borrow().is_none() {
self.initialize();
}
// TODO(phase-b): pass a reference to self, not a clone
package.set_repository(todo!("self as Box<dyn RepositoryInterface>"))?;
- let aliased_package: Option<Box<dyn BasePackage>> =
- if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
- Some(alias.get_alias_of().clone_box())
- } else {
- None
- };
+ let aliased_package: Option<PackageHandle> =
+ package.as_alias().map(|alias| alias.get_alias_of());
- self.packages.borrow_mut().as_mut().unwrap().push(package);
+ self.packages
+ .borrow_mut()
+ .as_mut()
+ .unwrap()
+ .push(package.into());
if let Some(aliased_package) = aliased_package {
- if aliased_package.get_repository().is_none() {
- // TODO(phase-b): pass aliased_package as Box<dyn PackageInterface>
- self.add_package(todo!("Box<BasePackage> -> Box<dyn PackageInterface>"))?;
- }
+ // PHP: if ($aliasedPackage->getRepository() === null) $this->addPackage($aliasedPackage);
+ // TODO(phase-c): the handle does not expose get_repository (a `RefCell`-borrowed
+ // back-reference); this needs repository back-references on handles.
+ let _ = aliased_package;
}
// invalidate package map cache
@@ -96,16 +77,18 @@ impl ArrayRepository {
/// @return AliasPackage|CompleteAliasPackage
pub(crate) fn create_alias_package(
&self,
- mut package: Box<dyn BasePackage>,
+ package: BasePackageHandle,
alias: String,
pretty_alias: String,
- ) -> Box<dyn BasePackage> {
- while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
- package = alias_pkg.get_alias_of().clone_box();
+ ) -> BasePackageHandle {
+ let mut package = package;
+ while let Some(alias_pkg) = package.as_alias() {
+ package = alias_pkg.get_alias_of().into();
}
- if package.as_any().downcast_ref::<CompletePackage>().is_some() {
- // TODO(phase-b): construct CompleteAliasPackage/AliasPackage and return as Box<BasePackage>
+ let _ = (&package, &alias, &pretty_alias);
+ if package.as_complete_package().is_some() {
+ // TODO(phase-b): construct CompleteAliasPackage/AliasPackage and return as a handle
return todo!("new CompleteAliasPackage(package, alias, pretty_alias)");
}
@@ -165,17 +148,15 @@ impl RepositoryInterface for ArrayRepository {
package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
let packages = self.get_packages();
- let mut result: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut result: IndexMap<String, BasePackageHandle> = IndexMap::new();
let mut names_found: IndexMap<String, bool> = IndexMap::new();
for package in &packages {
- if package_name_map.contains_key(PackageInterface::get_name(package.as_ref())) {
- let constraint_opt = package_name_map
- .get(PackageInterface::get_name(package.as_ref()))
- .unwrap();
+ if package_name_map.contains_key(&package.get_name()) {
+ let constraint_opt = package_name_map.get(&package.get_name()).unwrap();
let constraint_matches = match constraint_opt {
None => true,
Some(c) => c.matches(
@@ -191,38 +172,35 @@ impl RepositoryInterface for ArrayRepository {
&& StabilityFilter::is_package_acceptable(
&acceptable_stabilities,
&stability_flags,
- &PackageInterface::get_names(package.as_ref(), true),
- package.get_stability(),
+ &package.get_names(true),
+ &package.get_stability(),
)
&& !already_loaded
- .get(PackageInterface::get_name(package.as_ref()))
- .map(|v| v.contains_key(package.get_version()))
+ .get(&package.get_name())
+ .map(|v| v.contains_key(&package.get_version()))
.unwrap_or(false)
{
// add selected packages which match stability requirements
- result.insert(spl_object_hash(package.as_ref()), package.clone_box());
+ result.insert(package.ptr_id().to_string(), package.clone());
// add the aliased package for packages where the alias matches
- if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_alias() {
let aliased = alias.get_alias_of();
- if !result.contains_key(&spl_object_hash(aliased)) {
- result.insert(spl_object_hash(aliased), aliased.clone_box());
+ if !result.contains_key(&aliased.ptr_id().to_string()) {
+ result.insert(aliased.ptr_id().to_string(), aliased.into());
}
}
}
- names_found.insert(
- PackageInterface::get_name(package.as_ref()).to_string(),
- true,
- );
+ names_found.insert(package.get_name(), true);
}
}
// add aliases of packages that were selected, even if the aliases did not match
for package in &packages {
- if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_alias() {
let aliased = alias.get_alias_of();
- if result.contains_key(&spl_object_hash(aliased)) {
- result.insert(spl_object_hash(package.as_ref()), package.clone_box());
+ if result.contains_key(&aliased.ptr_id().to_string()) {
+ result.insert(package.ptr_id().to_string(), package.clone());
}
}
}
@@ -237,7 +215,7 @@ impl RepositoryInterface for ArrayRepository {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
let name = strtolower(name);
let constraint: AnyConstraint = match constraint {
@@ -249,7 +227,7 @@ impl RepositoryInterface for ArrayRepository {
};
for package in self.get_packages() {
- if name == PackageInterface::get_name(package.as_ref()) {
+ if name == package.get_name() {
let pkg_constraint = SimpleConstraint::new(
"==".to_string(),
package.get_version().to_string(),
@@ -268,7 +246,7 @@ impl RepositoryInterface for ArrayRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
// normalize name
let name = strtolower(name);
let mut packages = vec![];
@@ -283,7 +261,7 @@ impl RepositoryInterface for ArrayRepository {
};
for package in self.get_packages() {
- if name == PackageInterface::get_name(package.as_ref()) {
+ if name == package.get_name() {
if constraint.is_none()
|| constraint.as_ref().unwrap().matches(
&SimpleConstraint::new(
@@ -314,7 +292,7 @@ impl RepositoryInterface for ArrayRepository {
let mut matches: IndexMap<String, SearchResult> = IndexMap::new();
for package in self.get_packages() {
- let mut name = PackageInterface::get_name(package.as_ref()).to_string();
+ let mut name = package.get_name();
if mode == crate::repository::SEARCH_VENDOR {
// PHP: [$name] = explode('/', $name);
let parts: Vec<&str> = name.splitn(2, '/').collect();
@@ -329,7 +307,7 @@ impl RepositoryInterface for ArrayRepository {
}
}
- let complete = package.as_any().downcast_ref::<CompletePackage>();
+ let complete = package.as_complete();
let fulltext_match = mode == crate::repository::SEARCH_FULLTEXT
&& complete.is_some()
@@ -337,8 +315,12 @@ impl RepositoryInterface for ArrayRepository {
&regex,
&format!(
"{} {}",
- implode(" ", &complete.unwrap().get_keywords()),
- complete.unwrap().get_description().unwrap_or("")
+ implode(" ", &complete.as_ref().unwrap().get_keywords()),
+ complete
+ .as_ref()
+ .unwrap()
+ .get_description()
+ .unwrap_or_default()
),
)
.unwrap_or(false);
@@ -355,14 +337,12 @@ impl RepositoryInterface for ArrayRepository {
},
);
} else {
- let description = complete.and_then(|c| c.get_description().map(String::from));
- let abandoned = if let Some(c) = complete {
+ let description = complete.as_ref().and_then(|c| c.get_description());
+ let abandoned = if let Some(c) = &complete {
if c.is_abandoned() {
// PHP: $package->getReplacementPackage() ?: true
match c.get_replacement_package() {
- Some(s) if !s.is_empty() => {
- Some(AbandonedInfo::Replacement(s.to_string()))
- }
+ Some(s) if !s.is_empty() => Some(AbandonedInfo::Replacement(s)),
_ => Some(AbandonedInfo::Abandoned),
}
} else {
@@ -374,7 +354,7 @@ impl RepositoryInterface for ArrayRepository {
matches.insert(
name.clone(),
SearchResult {
- name: package.get_pretty_name().to_string(),
+ name: package.get_pretty_name(),
description,
abandoned,
url: None,
@@ -389,7 +369,7 @@ impl RepositoryInterface for ArrayRepository {
fn has_package(&self, package: &dyn PackageInterface) -> bool {
if self.package_map.borrow().is_none() {
- let mut map: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut map: IndexMap<String, BasePackageHandle> = IndexMap::new();
for repo_package in self.get_packages() {
map.insert(repo_package.get_unique_name(), repo_package);
}
@@ -407,19 +387,19 @@ impl RepositoryInterface for ArrayRepository {
let mut result: IndexMap<String, ProviderInfo> = IndexMap::new();
'candidates: for candidate in self.get_packages() {
- if result.contains_key(PackageInterface::get_name(candidate.as_ref())) {
+ if result.contains_key(&candidate.get_name()) {
continue;
}
for link in candidate.get_provides().values() {
if package_name == link.get_target() {
- let complete = candidate.as_any().downcast_ref::<CompletePackage>();
- let description = complete.and_then(|c| c.get_description().map(String::from));
+ let complete = candidate.as_complete();
+ let description = complete.and_then(|c| c.get_description());
result.insert(
- PackageInterface::get_name(candidate.as_ref()).to_string(),
+ candidate.get_name(),
ProviderInfo {
- name: PackageInterface::get_name(candidate.as_ref()).to_string(),
+ name: candidate.get_name(),
description,
- r#type: candidate.get_type().to_string(),
+ r#type: candidate.get_type(),
},
);
continue 'candidates;
@@ -430,7 +410,7 @@ impl RepositoryInterface for ArrayRepository {
result
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
if self.packages.borrow().is_none() {
self.initialize();
}
@@ -447,13 +427,12 @@ impl RepositoryInterface for ArrayRepository {
);
}
- // TODO(phase-b): return references rather than cloning the whole vector
self.packages
.borrow()
.as_ref()
.unwrap()
.iter()
- .map(|p| p.clone_box())
+ .map(|p| p.clone())
.collect()
}
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index 674325d..eff8fef 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -132,7 +132,7 @@ impl ArtifactRepository {
fn get_composer_information(
&self,
file: &Path,
- ) -> anyhow::Result<Option<Box<dyn BasePackage>>> {
+ ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> {
let mut json: Option<String> = None;
let file_extension = file
.extension()
diff --git a/crates/shirabe/src/repository/canonical_packages_trait.rs b/crates/shirabe/src/repository/canonical_packages_trait.rs
index 8c6e7c2..5f0ecff 100644
--- a/crates/shirabe/src/repository/canonical_packages_trait.rs
+++ b/crates/shirabe/src/repository/canonical_packages_trait.rs
@@ -1,23 +1,23 @@
//! ref: composer/src/Composer/Repository/CanonicalPackagesTrait.php
-use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use indexmap::IndexMap;
/// Provides get_canonical_packages() to various repository implementations.
pub trait CanonicalPackagesTrait {
- fn get_packages(&self) -> Vec<Box<dyn PackageInterface>>;
+ fn get_packages(&self) -> Vec<PackageInterfaceHandle>;
/// Get unique packages (at most one package of each name), with aliases resolved and removed.
- fn get_canonical_packages(&self) -> Vec<Box<dyn PackageInterface>> {
+ fn get_canonical_packages(&self) -> Vec<PackageInterfaceHandle> {
let packages = self.get_packages();
// get at most one package of each name, preferring non-aliased ones
- let mut packages_by_name: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
+ let mut packages_by_name: IndexMap<String, PackageInterfaceHandle> = IndexMap::new();
for package in packages {
- let name = package.get_name().to_string();
+ let name = package.get_name();
let prefer_replace = packages_by_name
.get(&name)
- .map(|existing| existing.as_alias_package().is_some())
+ .map(|existing| existing.as_alias().is_some())
.unwrap_or(true);
if prefer_replace {
packages_by_name.insert(name, package);
@@ -27,10 +27,10 @@ pub trait CanonicalPackagesTrait {
let mut canonical_packages = Vec::new();
// unfold aliased packages
- for package in packages_by_name.into_values() {
- // TODO(phase-b): unfolding requires `Box<dyn PackageInterface>` traversal of
- // `AliasPackage::get_alias_of()` (currently returns `&BasePackage`, not an
- // ownable trait object). Push the alias as-is for now.
+ for mut package in packages_by_name.into_values() {
+ while let Some(alias) = package.as_alias() {
+ package = alias.get_alias_of().into();
+ }
canonical_packages.push(package);
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 9752bc0..5c16f8e 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -7,7 +7,7 @@ use shirabe_php_shim::{
Countable, InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE,
LogicException, PHP_EOL, PhpMixed, RuntimeException, UnexpectedValueException,
extension_loaded, hash, http_build_query, in_array, json_decode, parse_url_all, realpath,
- spl_object_hash, strtolower, strtr, urlencode, var_export,
+ strtolower, strtr, urlencode, var_export,
};
use shirabe_semver::compiling_matcher::CompilingMatcher;
@@ -22,7 +22,9 @@ use crate::downloader::TransportException;
use crate::event_dispatcher::EventDispatcher;
use crate::io::IOInterface;
use crate::json::JsonFile;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::base_package::{self, BasePackage};
use crate::package::loader::ArrayLoader;
use crate::package::version::StabilityFilter;
@@ -119,15 +121,15 @@ pub struct ComposerRepository {
#[derive(Debug)]
pub enum FindPackageReturn {
- Package(Box<dyn BasePackage>),
- Packages(Vec<Box<dyn BasePackage>>),
+ Package(BasePackageHandle),
+ Packages(Vec<BasePackageHandle>),
None,
}
#[derive(Debug)]
pub struct LoadAsyncPackagesResult {
pub names_found: IndexMap<String, bool>,
- pub packages: IndexMap<String, Box<dyn BasePackage>>,
+ pub packages: IndexMap<String, BasePackageHandle>,
}
impl ConfigurableRepositoryInterface for ComposerRepository {
@@ -322,7 +324,7 @@ impl ComposerRepository {
&mut self,
name: String,
constraint: PhpMixed,
- ) -> anyhow::Result<Option<Box<dyn BasePackage>>> {
+ ) -> anyhow::Result<Option<BasePackageHandle>> {
// this call initializes loadRootServerFile which is needed for the rest below to work
let has_providers = self.has_providers()?;
@@ -343,7 +345,7 @@ impl ComposerRepository {
.map_or(false, |m| m.contains_key(&name))
{
let packages = self.what_provides(&name, None, None, IndexMap::new())?;
- let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect();
+ let packages_vec: Vec<BasePackageHandle> = packages.into_values().collect();
return Ok(
match self.filter_packages(packages_vec, Some(&constraint), true) {
FindPackageReturn::Package(p) => Some(p),
@@ -372,7 +374,7 @@ impl ComposerRepository {
if name == provider_name {
let packages =
self.what_provides(&provider_name, None, None, IndexMap::new())?;
- let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect();
+ let packages_vec: Vec<BasePackageHandle> = packages.into_values().collect();
return Ok(
match self.filter_packages(packages_vec, Some(&constraint), true) {
FindPackageReturn::Package(p) => Some(p),
@@ -396,7 +398,7 @@ impl ComposerRepository {
&mut self,
name: String,
constraint: Option<PhpMixed>,
- ) -> anyhow::Result<Vec<Box<dyn BasePackage>>> {
+ ) -> anyhow::Result<Vec<BasePackageHandle>> {
// this call initializes loadRootServerFile which is needed for the rest below to work
let has_providers = self.has_providers()?;
@@ -415,7 +417,7 @@ impl ComposerRepository {
.map_or(false, |m| m.contains_key(&name))
{
let packages = self.what_provides(&name, None, None, IndexMap::new())?;
- let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect();
+ let packages_vec: Vec<BasePackageHandle> = packages.into_values().collect();
return Ok(
match self.filter_packages(packages_vec, constraint.as_ref(), false) {
FindPackageReturn::Packages(v) => v,
@@ -440,7 +442,7 @@ impl ComposerRepository {
if name == provider_name {
let packages =
self.what_provides(&provider_name, None, None, IndexMap::new())?;
- let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect();
+ let packages_vec: Vec<BasePackageHandle> = packages.into_values().collect();
return Ok(
match self.filter_packages(packages_vec, constraint.as_ref(), false) {
FindPackageReturn::Packages(v) => v,
@@ -461,7 +463,7 @@ impl ComposerRepository {
fn filter_packages(
&self,
- packages: Vec<Box<dyn BasePackage>>,
+ packages: Vec<BasePackageHandle>,
constraint: Option<&AnyConstraint>,
return_first_match: bool,
) -> FindPackageReturn {
@@ -477,7 +479,7 @@ impl ComposerRepository {
}
let constraint = constraint.unwrap();
- let mut filtered_packages: Vec<Box<dyn BasePackage>> = Vec::new();
+ let mut filtered_packages: Vec<BasePackageHandle> = Vec::new();
for package in packages.into_iter() {
let pkg_constraint =
@@ -499,7 +501,7 @@ impl ComposerRepository {
FindPackageReturn::Packages(filtered_packages)
}
- pub fn get_packages(&mut self) -> anyhow::Result<Vec<Box<dyn BasePackage>>> {
+ pub fn get_packages(&mut self) -> anyhow::Result<Vec<BasePackageHandle>> {
let has_providers = self.has_providers()?;
if self.lazy_providers_url.is_some() {
@@ -712,7 +714,7 @@ impl ComposerRepository {
mut package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> anyhow::Result<LoadPackagesResult> {
// this call initializes loadRootServerFile which is needed for the rest below to work
let has_providers = self.has_providers()?;
@@ -724,22 +726,16 @@ impl ComposerRepository {
stability_flags,
already_loaded,
);
- // TODO(phase-b): repository_interface::LoadPackagesResult uses Vec<Box<dyn BasePackage>>
- // for `packages`; this fn returns IndexMap. Reconciliation needs structural changes.
- let _ = inner_result;
- return Ok(LoadPackagesResult {
- names_found: Vec::new(),
- packages: IndexMap::new(),
- });
+ return Ok(inner_result);
}
- let mut packages: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut packages: IndexMap<String, BasePackageHandle> = IndexMap::new();
let mut names_found: IndexMap<String, bool> = IndexMap::new();
if has_providers || self.has_partial_packages()? {
let names: Vec<String> = package_name_map.keys().cloned().collect();
for name in names {
- let mut matches: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut matches: IndexMap<String, BasePackageHandle> = IndexMap::new();
// if a repo has no providers but only partial packages and the partial packages are missing
// then we don't want to call whatProvides as it would try to load from the providers and fail
@@ -752,12 +748,11 @@ impl ComposerRepository {
continue;
}
- // TODO(phase-b): Box<dyn PackageInterface> is not Clone; share via Rc
let candidates = self.what_provides(
&name,
Some(&acceptable_stabilities),
Some(&stability_flags),
- todo!("clone of already_loaded requires sharing Box<dyn PackageInterface>"),
+ already_loaded.clone(),
)?;
let constraint = package_name_map
.get(&name)
@@ -783,13 +778,13 @@ impl ComposerRepository {
}
};
if matches_constraint {
- let hash_c = spl_object_hash(&**candidate);
- matches.insert(hash_c, dyn_clone_box(&**candidate));
- if let Some(alias) = candidate.as_alias_package() {
+ let hash_c = candidate.ptr_id().to_string();
+ matches.insert(hash_c, candidate.clone());
+ if let Some(alias) = candidate.as_alias() {
let aliased = alias.get_alias_of();
- let aliased_hash = spl_object_hash(aliased);
+ let aliased_hash = aliased.ptr_id().to_string();
if !matches.contains_key(&aliased_hash) {
- matches.insert(aliased_hash, dyn_clone_box(aliased));
+ matches.insert(aliased_hash, aliased.into());
}
}
}
@@ -797,12 +792,12 @@ impl ComposerRepository {
// add aliases of matched packages even if they did not match the constraint
for (_uid, candidate) in candidates.iter() {
- if let Some(alias) = candidate.as_alias_package() {
+ if let Some(alias) = candidate.as_alias() {
let aliased = alias.get_alias_of();
- let aliased_hash = spl_object_hash(aliased);
+ let aliased_hash = aliased.ptr_id().to_string();
if matches.contains_key(&aliased_hash) {
- let hash_c = spl_object_hash(&**candidate);
- matches.insert(hash_c, dyn_clone_box(&**candidate));
+ let hash_c = candidate.ptr_id().to_string();
+ matches.insert(hash_c, candidate.clone());
}
}
}
@@ -1412,8 +1407,8 @@ impl ComposerRepository {
name: &str,
acceptable_stabilities: Option<&IndexMap<String, i64>>,
stability_flags: Option<&IndexMap<String, i64>>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
- ) -> anyhow::Result<IndexMap<String, Box<dyn BasePackage>>> {
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
+ ) -> anyhow::Result<IndexMap<String, BasePackageHandle>> {
let mut packages_source: Option<String> = None;
let packages: IndexMap<String, PhpMixed>;
let loading_partial_package: bool;
@@ -1635,7 +1630,7 @@ impl ComposerRepository {
loading_partial_package = true;
}
- let mut result: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut result: IndexMap<String, BasePackageHandle> = IndexMap::new();
let mut versions_to_load: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new();
let packages_inner = packages
.get("packages")
@@ -1749,15 +1744,13 @@ impl ComposerRepository {
let loaded_packages = self.create_packages_flat(versions_to_load_vec, packages_source)?;
let uids: Vec<String> = versions_to_load.keys().cloned().collect();
- for (index, mut package) in loaded_packages.into_iter().enumerate() {
- package.set_repository_self();
+ for (index, package) in loaded_packages.into_iter().enumerate() {
+ // TODO(phase-c): wire the repository back-reference onto the shared package handle.
let uid = &uids[index];
- if let Some(alias) = package.as_alias_package_mut() {
- let aliased = alias.get_alias_of_mut();
- aliased.set_repository_self();
-
- result.insert(uid.clone(), dyn_clone_box(aliased));
+ if let Some(alias) = package.as_alias() {
+ let aliased = alias.get_alias_of();
+ result.insert(uid.clone(), aliased.into());
result.insert(format!("{}-alias", uid), package);
} else {
result.insert(uid.clone(), package);
@@ -1784,10 +1777,11 @@ impl ComposerRepository {
}
/// Adds a new package to the repository
- pub fn add_package(&mut self, mut package: Box<dyn BasePackage>) {
- // configurePackageTransportOptions(*package);
- self.configure_package_transport_options(&mut *package);
- self.inner.add_package(package);
+ pub fn add_package(&mut self, package: BasePackageHandle) {
+ self.configure_package_transport_options(
+ package.as_rc().borrow_mut().as_package_interface_mut(),
+ );
+ self.inner.add_package(package.into());
}
/// @param packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded
@@ -1796,11 +1790,11 @@ impl ComposerRepository {
mut package_names: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: Option<&IndexMap<String, i64>>,
stability_flags: Option<&IndexMap<String, i64>>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> anyhow::Result<LoadAsyncPackagesResult> {
self.load_root_server_file(None)?;
- let mut packages: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
+ let mut packages: IndexMap<String, BasePackageHandle> = IndexMap::new();
let mut names_found: IndexMap<String, bool> = IndexMap::new();
if self.lazy_providers_url.is_none() {
@@ -1980,17 +1974,16 @@ impl ComposerRepository {
}
}
- let loaded_packages: Vec<Box<dyn BasePackage>> =
+ let loaded_packages: Vec<BasePackageHandle> =
ComposerRepository::create_packages_static(versions_to_load, packages_source)?;
- for mut package in loaded_packages.into_iter() {
- package.set_repository_self();
- let hash_c = spl_object_hash(&*package);
- if let Some(alias) = package.as_alias_package_mut() {
- let aliased_hash = spl_object_hash(alias.get_alias_of());
+ for package in loaded_packages.into_iter() {
+ // TODO(phase-c): wire the repository back-reference onto the shared package handle.
+ let hash_c = package.ptr_id().to_string();
+ if let Some(alias) = package.as_alias() {
+ let aliased = alias.get_alias_of();
+ let aliased_hash = aliased.ptr_id().to_string();
if !packages.contains_key(&aliased_hash) {
- alias.get_alias_of_mut().set_repository_self();
- let aliased_clone = dyn_clone_box(alias.get_alias_of());
- packages.insert(aliased_hash, aliased_clone);
+ packages.insert(aliased_hash, aliased.into());
}
}
packages.insert(hash_c, package);
@@ -2716,13 +2709,13 @@ impl ComposerRepository {
&mut self,
packages: Vec<IndexMap<String, PhpMixed>>,
source: Option<String>,
- ) -> anyhow::Result<Vec<Box<dyn BasePackage>>> {
+ ) -> anyhow::Result<Vec<BasePackageHandle>> {
if packages.is_empty() {
return Ok(vec![]);
}
let mut packages = packages;
- let result = (|| -> anyhow::Result<Vec<Box<dyn BasePackage>>> {
+ let result = (|| -> anyhow::Result<Vec<BasePackageHandle>> {
for data in packages.iter_mut() {
if !data.contains_key("notification-url") {
data.insert(
@@ -2737,11 +2730,11 @@ impl ComposerRepository {
let package_instances = self.loader.load_packages(packages.clone())?;
- let mut results: Vec<Box<dyn BasePackage>> = Vec::new();
- for mut package in package_instances.into_iter() {
+ let mut results: Vec<BasePackageHandle> = Vec::new();
+ for package in package_instances.into_iter() {
if let Some(src_type) = package.get_source_type() {
if let Some(mirrors) =
- self.source_mirrors.as_ref().and_then(|m| m.get(src_type))
+ self.source_mirrors.as_ref().and_then(|m| m.get(&src_type))
{
let converted: Vec<IndexMap<String, PhpMixed>> = mirrors
.iter()
@@ -2767,8 +2760,10 @@ impl ComposerRepository {
.collect();
package.set_dist_mirrors(Some(converted));
}
- self.configure_package_transport_options(&mut *package);
- results.push(package);
+ self.configure_package_transport_options(
+ package.as_rc().borrow_mut().as_package_interface_mut(),
+ );
+ results.push(package.into());
}
Ok(results)
})();
@@ -2794,12 +2789,16 @@ impl ComposerRepository {
fn create_packages_static(
packages: Vec<IndexMap<String, PhpMixed>>,
_source: Option<String>,
- ) -> anyhow::Result<Vec<Box<dyn BasePackage>>> {
+ ) -> anyhow::Result<Vec<BasePackageHandle>> {
if packages.is_empty() {
return Ok(vec![]);
}
let loader = ArrayLoader::new(Some(VersionParser::new()), true);
- Ok(loader.load_packages(packages)?)
+ Ok(loader
+ .load_packages(packages)?
+ .into_iter()
+ .map(|p| p.into())
+ .collect())
}
fn fetch_file(
@@ -3485,7 +3484,3 @@ fn clone_root_data(rd: &RootData) -> RootData {
RootData::Data(d) => RootData::Data(d.clone()),
}
}
-
-fn dyn_clone_box(_pkg: &dyn BasePackage) -> Box<dyn BasePackage> {
- todo!()
-}
diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs
index 6671895..024f89a 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -5,8 +5,9 @@ use std::any::Any;
use indexmap::IndexMap;
use shirabe_semver::constraint::AnyConstraint;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::{
FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
};
@@ -79,7 +80,7 @@ impl RepositoryInterface for CompositeRepository {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
for repository in &self.repositories {
let package = repository.find_package(name, constraint.clone());
if package.is_some() {
@@ -93,7 +94,7 @@ impl RepositoryInterface for CompositeRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
let mut packages = vec![];
for repository in &self.repositories {
packages.extend(repository.find_packages(name, constraint.clone()));
@@ -101,7 +102,7 @@ impl RepositoryInterface for CompositeRepository {
packages
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
let mut packages = vec![];
for repository in &self.repositories {
packages.extend(repository.get_packages());
@@ -114,35 +115,21 @@ impl RepositoryInterface for CompositeRepository {
package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
let mut all_packages = IndexMap::new();
let mut all_names_found = vec![];
for repository in &self.repositories {
- // TODO(phase-b): manual deep clone since trait objects in maps don't derive Clone.
let name_map_cloned: IndexMap<String, Option<AnyConstraint>> = package_name_map
.iter()
.map(|(k, v)| (k.clone(), v.as_ref().map(|c| c.clone())))
.collect();
- let already_loaded_cloned: IndexMap<
- String,
- IndexMap<String, Box<dyn PackageInterface>>,
- > = already_loaded
- .iter()
- .map(|(k, inner)| {
- let inner_cloned: IndexMap<String, Box<dyn PackageInterface>> = inner
- .iter()
- .map(|(ik, iv)| (ik.clone(), iv.clone_package_box()))
- .collect();
- (k.clone(), inner_cloned)
- })
- .collect();
let result = repository.load_packages(
name_map_cloned,
acceptable_stabilities.clone(),
stability_flags.clone(),
- already_loaded_cloned,
+ already_loaded.clone(),
);
all_packages.extend(result.packages);
all_names_found.extend(result.names_found);
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 96c7b61..c8ffb7c 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -16,10 +16,10 @@ use shirabe_php_shim::{
use crate::installed_versions::InstalledVersions;
use crate::installer::InstallationManager;
use crate::json::JsonFile;
-use crate::package::AliasPackage;
use crate::package::PackageInterface;
-use crate::package::RootAliasPackage;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterface;
+use crate::package::RootPackageInterfaceHandle;
use crate::package::dumper::ArrayDumper;
use crate::package::loader::ArrayLoader;
use crate::package::loader::LoaderInterface;
@@ -38,7 +38,7 @@ pub struct FilesystemRepository {
/// @var bool
dump_versions: bool,
/// @var ?RootPackageInterface
- root_package: Option<Box<dyn RootPackageInterface>>,
+ root_package: Option<RootPackageInterfaceHandle>,
/// @var Filesystem
filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
/// @var bool|null
@@ -53,7 +53,7 @@ impl FilesystemRepository {
pub fn new(
repository_file: JsonFile,
dump_versions: bool,
- root_package: Option<Box<dyn RootPackageInterface>>,
+ root_package: Option<RootPackageInterfaceHandle>,
filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
let filesystem = filesystem
@@ -66,8 +66,7 @@ impl FilesystemRepository {
.into());
}
Ok(Self {
- // TODO(phase-b): WritableArrayRepository::new() needs to be exposed
- inner: todo!("WritableArrayRepository::new()"),
+ inner: WritableArrayRepository::new(vec![])?,
file: repository_file,
dump_versions,
root_package,
@@ -220,8 +219,9 @@ impl FilesystemRepository {
let mut install_paths: IndexMap<String, Option<String>> = IndexMap::new();
for package in self.inner.get_canonical_packages() {
- let mut pkg_array = dumper.dump(&*package);
- let path = installation_manager.get_install_path(&*package);
+ let mut pkg_array = dumper.dump(package.as_rc().borrow().as_package_interface());
+ let path = installation_manager
+ .get_install_path(package.as_rc().borrow().as_package_interface());
let mut install_path: Option<String> = None;
if let Some(path_str) = &path {
if !path_str.is_empty() {
@@ -476,14 +476,13 @@ impl FilesystemRepository {
.map(|s| Box::new(PhpMixed::String(s.clone())))
.collect(),
));
- let mut packages: Vec<Box<dyn PackageInterface>> = self
+ let mut packages: Vec<PackageInterfaceHandle> = self
.inner
.get_packages()
.into_iter()
- // TODO(phase-b): Box<BasePackage> -> Box<dyn PackageInterface>
- .map(|p| todo!("Box<BasePackage> to Box<dyn PackageInterface>"))
+ .map(|p| p.into())
.collect();
- let mut root_package = match &self.root_package {
+ let mut current_root: RootPackageInterfaceHandle = match &self.root_package {
None => {
return Err(LogicException {
message:
@@ -493,25 +492,27 @@ impl FilesystemRepository {
}
.into());
}
- // TODO(phase-b): clone root_package to push into packages list
- Some(_r) => todo!("clone root_package"),
+ Some(r) => r.clone(),
};
// packages[] = $rootPackage = $this->rootPackage;
- // TODO(phase-b): track current root_package in mutable variable
- let mut current_root: Box<dyn RootPackageInterface> = root_package;
- // packages.push(current_root.clone_box());
+ packages.push(current_root.clone().into());
- while let Some(_alias) = current_root.as_any().downcast_ref::<RootAliasPackage>() {
- current_root =
- todo!("RootAliasPackage::get_alias_of() returning Box<dyn RootPackageInterface>");
- // packages.push(current_root.clone_box());
+ while let Some(root_alias) =
+ PackageInterfaceHandle::from(current_root.clone()).as_root_alias_package()
+ {
+ current_root = root_alias.get_alias_of().into();
+ packages.push(current_root.clone().into());
}
let mut versions: IndexMap<String, PhpMixed> = IndexMap::new();
versions.insert(
"root".to_string(),
PhpMixed::Array(
self.dump_root_package(
- &*current_root,
+ current_root
+ .as_rc()
+ .borrow()
+ .as_root_package_interface()
+ .expect("current_root is a RootPackageInterface"),
install_paths,
dev_mode,
repo_dir,
@@ -526,12 +527,16 @@ impl FilesystemRepository {
// add real installed packages
for package in &packages {
- if package.as_any().downcast_ref::<AliasPackage>().is_some() {
+ if package.as_alias().is_some() {
continue;
}
- let dumped =
- self.dump_installed_package(&**package, install_paths, repo_dir, &dev_packages);
+ let dumped = self.dump_installed_package(
+ package.as_rc().borrow().as_package_interface(),
+ install_paths,
+ repo_dir,
+ &dev_packages,
+ );
if let Some(PhpMixed::Array(versions_map)) = versions.get_mut("versions") {
versions_map.insert(
package.get_name().to_string(),
@@ -546,7 +551,7 @@ impl FilesystemRepository {
for package in &packages {
let is_dev_package = dev_packages
.as_array()
- .map(|m| m.contains_key(package.get_name()))
+ .map(|m| m.contains_key(&package.get_name()))
.unwrap_or(false);
for (_, replace) in package.get_replaces() {
// exclude platform replaces as when they are really there we can not check for their presence
@@ -587,12 +592,13 @@ impl FilesystemRepository {
// add aliases
for package in &packages {
- let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() else {
+ let Some(_alias) = package.as_alias() else {
continue;
};
// TODO(phase-b): mutate nested versions['versions'][name]['aliases']
todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']");
- if package.as_root_package_interface().is_some() {
+ #[allow(unreachable_code)]
+ if package.as_root().is_some() {
// TODO(phase-b): same mutation on versions['root']['aliases']
todo!("append alias->getPrettyVersion() to versions['root']['aliases']");
}
@@ -721,9 +727,7 @@ impl FilesystemRepository {
repo_dir: &str,
dev_packages: &PhpMixed,
) -> IndexMap<String, PhpMixed> {
- let data =
- // TODO(phase-b): RootPackageInterface trait bound — pass as &dyn PackageInterface
- self.dump_installed_package(todo!("package as &dyn PackageInterface"), install_paths, repo_dir, dev_packages);
+ let data = self.dump_installed_package(package, install_paths, repo_dir, dev_packages);
let mut result: IndexMap<String, PhpMixed> = IndexMap::new();
result.insert(
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index a3cb18b..c67e4ff 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -1,7 +1,9 @@
//! ref: composer/src/Composer/Repository/FilterRepository.php
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
-use crate::package::base_package::{self, BasePackage};
+use crate::package::PackageInterfaceHandle;
+use crate::package::base_package::{self};
use crate::repository::{AdvisoryProviderInterface, SecurityAdvisoryResult};
use crate::repository::{
FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
@@ -165,7 +167,7 @@ impl RepositoryInterface for FilterRepository {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
if !self.is_allowed(name) {
return None;
}
@@ -177,7 +179,7 @@ impl RepositoryInterface for FilterRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
if !self.is_allowed(name) {
return Vec::new();
}
@@ -190,7 +192,7 @@ impl RepositoryInterface for FilterRepository {
mut package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
package_name_map.retain(|name, _| self.is_allowed(name));
@@ -226,10 +228,10 @@ impl RepositoryInterface for FilterRepository {
result
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
let mut result = Vec::new();
for package in self.repo.get_packages() {
- if self.is_allowed(PackageInterface::get_name(package.as_ref())) {
+ if self.is_allowed(&package.get_name()) {
result.push(package);
}
}
diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs
index b089df6..f0244b6 100644
--- a/crates/shirabe/src/repository/installed_array_repository.rs
+++ b/crates/shirabe/src/repository/installed_array_repository.rs
@@ -4,8 +4,9 @@ use indexmap::IndexMap;
use shirabe_php_shim::Countable;
use shirabe_semver::constraint::AnyConstraint;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::AdvisoryProviderInterface;
use crate::repository::InstalledRepositoryInterface;
use crate::repository::WritableArrayRepository;
@@ -24,7 +25,7 @@ impl InstalledArrayRepository {
Self::new_with_packages(Vec::new())
}
- pub fn new_with_packages(packages: Vec<Box<dyn PackageInterface>>) -> anyhow::Result<Self> {
+ pub fn new_with_packages(packages: Vec<PackageInterfaceHandle>) -> anyhow::Result<Self> {
Ok(Self {
inner: WritableArrayRepository::new(packages)?,
})
@@ -56,7 +57,7 @@ impl WritableRepositoryInterface for InstalledArrayRepository {
fn add_package(
&mut self,
- package: Box<dyn crate::package::PackageInterface>,
+ package: crate::package::PackageInterfaceHandle,
) -> anyhow::Result<()> {
todo!()
}
@@ -68,7 +69,7 @@ impl WritableRepositoryInterface for InstalledArrayRepository {
todo!()
}
- fn get_canonical_packages(&self) -> Vec<Box<dyn crate::package::PackageInterface>> {
+ fn get_canonical_packages(&self) -> Vec<crate::package::PackageInterfaceHandle> {
todo!()
}
@@ -99,17 +100,17 @@ impl RepositoryInterface for InstalledArrayRepository {
&self,
_name: &str,
_constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
todo!()
}
fn find_packages(
&self,
_name: &str,
_constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
todo!()
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
todo!()
}
fn load_packages(
@@ -117,7 +118,7 @@ impl RepositoryInterface for InstalledArrayRepository {
_package_name_map: IndexMap<String, Option<AnyConstraint>>,
_acceptable_stabilities: IndexMap<String, i64>,
_stability_flags: IndexMap<String, i64>,
- _already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ _already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
todo!()
}
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index 5c6fecf..8a6359a 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -6,9 +6,10 @@ use shirabe_php_shim::Countable;
use shirabe_semver::constraint::AnyConstraint;
use crate::json::JsonFile;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
-use crate::package::RootPackageInterface;
+use crate::package::PackageInterfaceHandle;
+use crate::package::RootPackageInterfaceHandle;
use crate::repository::AdvisoryProviderInterface;
use crate::repository::FilesystemRepository;
use crate::repository::InstalledRepositoryInterface;
@@ -27,7 +28,7 @@ impl InstalledFilesystemRepository {
pub fn new(
repository_file: JsonFile,
dump_versions: bool,
- root_package: Option<Box<dyn RootPackageInterface>>,
+ root_package: Option<RootPackageInterfaceHandle>,
filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
Ok(Self {
@@ -66,7 +67,7 @@ impl WritableRepositoryInterface for InstalledFilesystemRepository {
fn add_package(
&mut self,
- package: Box<dyn crate::package::PackageInterface>,
+ package: crate::package::PackageInterfaceHandle,
) -> anyhow::Result<()> {
todo!()
}
@@ -78,7 +79,7 @@ impl WritableRepositoryInterface for InstalledFilesystemRepository {
todo!()
}
- fn get_canonical_packages(&self) -> Vec<Box<dyn crate::package::PackageInterface>> {
+ fn get_canonical_packages(&self) -> Vec<crate::package::PackageInterfaceHandle> {
todo!()
}
@@ -109,17 +110,17 @@ impl RepositoryInterface for InstalledFilesystemRepository {
&self,
_name: &str,
_constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
todo!()
}
fn find_packages(
&self,
_name: &str,
_constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
todo!()
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
todo!()
}
fn load_packages(
@@ -127,7 +128,7 @@ impl RepositoryInterface for InstalledFilesystemRepository {
_package_name_map: IndexMap<String, Option<AnyConstraint>>,
_acceptable_stabilities: IndexMap<String, i64>,
_stability_flags: IndexMap<String, i64>,
- _already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ _already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
todo!()
}
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index 5c9e0d7..33356e3 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -6,10 +6,10 @@ use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::MatchAllConstraint;
use shirabe_semver::constraint::SimpleConstraint;
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::Link;
use crate::package::PackageInterface;
-use crate::package::RootPackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::version::VersionParser;
use crate::repository::CompositeRepository;
use crate::repository::InstalledRepositoryInterface;
@@ -26,7 +26,7 @@ pub enum NeedleInput {
}
pub struct DependentsEntry(
- pub Box<dyn BasePackage>,
+ pub BasePackageHandle,
pub Link,
pub Option<Vec<DependentsEntry>>,
);
@@ -53,7 +53,7 @@ impl InstalledRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
let name = name.to_lowercase();
let constraint: Option<AnyConstraint> = match constraint {
@@ -79,7 +79,7 @@ impl InstalledRepository {
.into(),
)
{
- matches.push(candidate);
+ matches.push(candidate.clone());
}
continue;
}
@@ -98,7 +98,7 @@ impl InstalledRepository {
&& (constraint.is_none()
|| constraint.as_ref().unwrap().matches(link.get_constraint()))
{
- matches.push(candidate);
+ matches.push(candidate.clone());
continue 'candidates;
}
}
@@ -124,10 +124,10 @@ impl InstalledRepository {
let mut packages_found = packages_found.unwrap_or_else(|| needles.clone());
- let mut root_package: Option<Box<dyn BasePackage>> = None;
+ let mut root_package: Option<BasePackageHandle> = None;
for package in self.inner.get_packages() {
- if package.as_root_package_interface().is_some() {
- root_package = Some(package);
+ if package.as_root().is_some() {
+ root_package = Some(package.clone());
break;
}
}
@@ -150,7 +150,7 @@ impl InstalledRepository {
{
if packages_in_tree.contains(&link.get_target().to_string()) {
results.push(DependentsEntry(
- package.clone_box(),
+ package.clone(),
link.clone(),
None,
));
@@ -169,7 +169,7 @@ impl InstalledRepository {
vec![]
};
results.push(DependentsEntry(
- package.clone_box(),
+ package.clone(),
link.clone(),
Some(dependents),
));
@@ -180,7 +180,7 @@ impl InstalledRepository {
}
}
- if package.as_root_package_interface().is_some() {
+ if package.as_root().is_some() {
for (k, v) in package.get_dev_requires() {
links.entry(k).or_insert(v);
}
@@ -194,11 +194,7 @@ impl InstalledRepository {
.map_or(true, |c| link.get_constraint().matches(c) == !invert);
if constraint.is_none() || matches_constraint {
if packages_in_tree.contains(&link.get_source().to_string()) {
- results.push(DependentsEntry(
- package.clone_box(),
- link.clone(),
- None,
- ));
+ results.push(DependentsEntry(package.clone(), link.clone(), None));
continue;
}
packages_in_tree.push(link.get_source().to_string());
@@ -214,7 +210,7 @@ impl InstalledRepository {
vec![]
};
results.push(DependentsEntry(
- package.clone_box(),
+ package.clone(),
link.clone(),
Some(dependents),
));
@@ -232,7 +228,7 @@ impl InstalledRepository {
None,
);
if link.get_constraint().matches(&version.into()) == invert {
- results.push(DependentsEntry(package.clone_box(), link.clone(), None));
+ results.push(DependentsEntry(package.clone(), link.clone(), None));
}
}
}
@@ -247,7 +243,7 @@ impl InstalledRepository {
None,
);
if link.get_constraint().matches(&version.into()) == invert {
- results.push(DependentsEntry(package.clone_box(), link.clone(), None));
+ results.push(DependentsEntry(package.clone(), link.clone(), None));
}
}
}
@@ -286,7 +282,7 @@ impl InstalledRepository {
.map(|p| format!("but {} is installed", p.get_pretty_version()))
.unwrap_or_else(|| "but it is missing".to_string());
results.push(DependentsEntry(
- package.clone_box(),
+ package.clone(),
Link::new(
package.get_name().to_string(),
link.get_target().to_string(),
@@ -316,7 +312,7 @@ impl InstalledRepository {
)
.into();
- if link.get_target() != pkg.get_name() {
+ if link.get_target() != pkg.get_name().as_str() {
let mut replaces_and_provides: IndexMap<String, Link> =
pkg.get_replaces();
for (k, v) in pkg.get_provides() {
@@ -343,12 +339,12 @@ impl InstalledRepository {
&& !root_req.get_constraint().matches(link.get_constraint())
{
results.push(DependentsEntry(
- package.clone_box(),
+ package.clone(),
link.clone(),
None,
));
results.push(DependentsEntry(
- root_pkg.clone_box(),
+ root_pkg.clone(),
root_req.clone(),
None,
));
@@ -356,13 +352,9 @@ impl InstalledRepository {
}
}
+ results.push(DependentsEntry(package.clone(), link.clone(), None));
results.push(DependentsEntry(
- package.clone_box(),
- link.clone(),
- None,
- ));
- results.push(DependentsEntry(
- root_pkg.clone_box(),
+ root_pkg.clone(),
Link::new(
root_pkg.get_name().to_string(),
link.get_target().to_string(),
@@ -376,11 +368,7 @@ impl InstalledRepository {
None,
));
} else {
- results.push(DependentsEntry(
- package.clone_box(),
- link.clone(),
- None,
- ));
+ results.push(DependentsEntry(package.clone(), link.clone(), None));
}
}
@@ -444,7 +432,7 @@ impl RepositoryInterface for InstalledRepository {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
self.inner.find_package(name, constraint)
}
@@ -452,11 +440,11 @@ impl RepositoryInterface for InstalledRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
self.inner.find_packages(name, constraint)
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
self.inner.get_packages()
}
@@ -465,7 +453,7 @@ impl RepositoryInterface for InstalledRepository {
package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
self.inner.load_packages(
package_name_map,
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index 57abe56..3d00ecf 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -1,7 +1,8 @@
//! ref: composer/src/Composer/Repository/LockArrayRepository.php
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::ArrayRepository;
use crate::repository::CanonicalPackagesTrait;
use crate::repository::{
@@ -17,7 +18,7 @@ pub struct LockArrayRepository {
}
impl CanonicalPackagesTrait for LockArrayRepository {
- fn get_packages(&self) -> Vec<Box<dyn PackageInterface>> {
+ fn get_packages(&self) -> Vec<PackageInterfaceHandle> {
todo!()
}
}
@@ -43,7 +44,7 @@ impl RepositoryInterface for LockArrayRepository {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
self.inner.find_package(name, constraint)
}
@@ -51,11 +52,11 @@ impl RepositoryInterface for LockArrayRepository {
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
self.inner.find_packages(name, constraint)
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
RepositoryInterface::get_packages(&self.inner)
}
@@ -64,7 +65,7 @@ impl RepositoryInterface for LockArrayRepository {
package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult {
self.inner.load_packages(
package_name_map,
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index 93c7e31..b88de78 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -71,8 +71,7 @@ impl PackageRepository {
})));
}
};
- // TODO(phase-b): add_package expects Box<dyn PackageInterface>; loader returns Box<dyn BasePackage>
- let _ = package_loaded;
+ self.inner.add_package(package_loaded)?;
}
Ok(Ok(()))
}
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 0b3ccb1..31760bb 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -17,9 +17,12 @@ use shirabe_semver::constraint::SimpleConstraint;
use crate::composer;
use crate::composer::ComposerHandle;
use crate::package::CompletePackage;
+use crate::package::CompletePackageHandle;
use crate::package::CompletePackageInterface;
+use crate::package::CompletePackageInterfaceHandle;
use crate::package::Link;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::version::VersionParser;
use crate::platform::HhvmDetector;
use crate::platform::Runtime;
@@ -46,7 +49,7 @@ pub struct PlatformRepository {
pub(crate) inner: ArrayRepository,
pub(crate) version_parser: Option<VersionParser>,
pub(crate) overrides: IndexMap<String, PlatformOverride>,
- pub(crate) disabled_packages: IndexMap<String, Box<dyn CompletePackageInterface>>,
+ pub(crate) disabled_packages: IndexMap<String, CompletePackageInterfaceHandle>,
pub(crate) runtime: Runtime,
pub(crate) hhvm_detector: HhvmDetector,
}
@@ -55,14 +58,14 @@ impl PlatformRepository {
pub const PLATFORM_PACKAGE_REGEX: &'static str = "{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer(?:-(?:plugin|runtime)-api)?)$}iD";
pub fn new(
- packages: Vec<Box<dyn PackageInterface>>,
+ packages: Vec<PackageInterfaceHandle>,
overrides: IndexMap<String, PhpMixed>,
) -> anyhow::Result<Self> {
Self::new4(packages, overrides, None, None)
}
pub fn new4(
- packages: Vec<Box<dyn PackageInterface>>,
+ packages: Vec<PackageInterfaceHandle>,
overrides: IndexMap<String, PhpMixed>,
runtime: Option<Runtime>,
hhvm_detector: Option<HhvmDetector>,
@@ -117,7 +120,7 @@ impl PlatformRepository {
self.disabled_packages.contains_key(name)
}
- pub fn get_disabled_packages(&self) -> &IndexMap<String, Box<dyn CompletePackageInterface>> {
+ pub fn get_disabled_packages(&self) -> &IndexMap<String, CompletePackageInterfaceHandle> {
&self.disabled_packages
}
@@ -167,7 +170,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
composer.set_description("Composer package".to_string());
- self.add_package(Box::new(composer))?;
+ self.add_package(CompletePackageHandle::from_complete_package(composer).into())?;
pretty_version = plugin_interface::PLUGIN_API_VERSION.to_string();
version = self
@@ -181,7 +184,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
composer_plugin_api.set_description("The Composer Plugin API".to_string());
- self.add_package(Box::new(composer_plugin_api))?;
+ self.add_package(CompletePackageHandle::from_complete_package(composer_plugin_api).into())?;
pretty_version = composer::RUNTIME_API_VERSION.to_string();
version = self
@@ -195,7 +198,9 @@ impl PlatformRepository {
pretty_version.clone(),
);
composer_runtime_api.set_description("The Composer Runtime API".to_string());
- self.add_package(Box::new(composer_runtime_api))?;
+ self.add_package(
+ CompletePackageHandle::from_complete_package(composer_runtime_api).into(),
+ )?;
let php_version_const = self.runtime.get_constant("PHP_VERSION", None);
let php_version_str = match &php_version_const {
@@ -226,7 +231,7 @@ impl PlatformRepository {
let mut php =
CompletePackage::new("php".to_string(), version.clone(), pretty_version.clone());
php.set_description("The PHP interpreter".to_string());
- self.add_package(Box::new(php))?;
+ self.add_package(CompletePackageHandle::from_complete_package(php).into())?;
if self
.runtime
@@ -240,7 +245,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
phpdebug.set_description("The PHP interpreter, with debugging symbols".to_string());
- self.add_package(Box::new(phpdebug))?;
+ self.add_package(CompletePackageHandle::from_complete_package(phpdebug).into())?;
}
if self.runtime.has_constant("PHP_ZTS", None)
@@ -256,7 +261,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
phpzts.set_description("The PHP interpreter, with Zend Thread Safety".to_string());
- self.add_package(Box::new(phpzts))?;
+ self.add_package(CompletePackageHandle::from_complete_package(phpzts).into())?;
}
if self
@@ -272,7 +277,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
php64.set_description("The PHP interpreter, 64bit".to_string());
- self.add_package(Box::new(php64))?;
+ self.add_package(CompletePackageHandle::from_complete_package(php64).into())?;
}
// The AF_INET6 constant is only defined if ext-sockets is available but
@@ -297,7 +302,7 @@ impl PlatformRepository {
pretty_version.clone(),
);
php_ipv6.set_description("The PHP interpreter, with IPv6 support".to_string());
- self.add_package(Box::new(php_ipv6))?;
+ self.add_package(CompletePackageHandle::from_complete_package(php_ipv6).into())?;
}
let loaded_extensions = self.runtime.get_extensions();
@@ -1582,14 +1587,13 @@ impl PlatformRepository {
let mut hhvm = CompletePackage::new("hhvm".to_string(), version, pretty_version);
hhvm.set_description("The HHVM Runtime (64bit)".to_string());
- self.add_package(Box::new(hhvm))?;
+ self.add_package(CompletePackageHandle::from_complete_package(hhvm).into())?;
}
Ok(())
}
- pub fn add_package(&mut self, package: Box<dyn PackageInterface>) -> anyhow::Result<()> {
- // TODO(phase-b): downcast `package` to CompletePackage; this stub keeps the structure.
- if !Self::is_complete_package(package.as_ref()) {
+ pub fn add_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<()> {
+ if package.as_complete().is_none() {
return Err(anyhow::anyhow!(UnexpectedValueException {
message: format!(
"Expected CompletePackage but got {}",
@@ -1599,18 +1603,17 @@ impl PlatformRepository {
}));
}
+ let name = package.get_name();
+
// Skip if overridden
- if self.overrides.contains_key(package.get_name()) {
- if matches!(
- self.overrides[package.get_name()].version,
- PhpMixed::Bool(false)
- ) {
+ if self.overrides.contains_key(&name) {
+ if matches!(self.overrides[&name].version, PhpMixed::Bool(false)) {
self.add_disabled_package_from_pkg(package);
return Ok(());
}
let overrider = self.inner.find_package(
- package.get_name(),
+ &name,
crate::repository::FindPackageConstraint::String("*".to_string()),
);
let actual_text = if let Some(ref ov) = overrider {
@@ -1622,24 +1625,24 @@ impl PlatformRepository {
} else {
format!("actual: {}", package.get_pretty_version())
};
- if let Some(_overrider_pkg) = overrider {
- // TODO(phase-b): downcast `overrider` to CompletePackageInterface for setDescription
- let _ = actual_text;
+ if let Some(overrider) = overrider {
+ if let Some(overrider) = overrider.as_complete() {
+ let description = overrider.get_description().unwrap_or_default();
+ overrider.set_description(format!("{}, {}", description, actual_text));
+ }
}
return Ok(());
}
// Skip if PHP is overridden and we are adding a php-* package
- if self.overrides.contains_key("php") && strpos(package.get_name(), "php-") == Some(0) {
+ if self.overrides.contains_key("php") && strpos(&name, "php-") == Some(0) {
let php_override = PlatformOverride {
name: self.overrides["php"].name.clone(),
version: self.overrides["php"].version.clone(),
};
- let mut overrider = self.add_overridden_package(
- &php_override,
- Some(package.get_pretty_name().to_string()),
- )?;
+ let mut overrider =
+ self.add_overridden_package(&php_override, Some(package.get_pretty_name()))?;
let actual_text = if package.get_version() == overrider.get_version() {
"same as actual".to_string()
} else {
@@ -1693,22 +1696,32 @@ impl PlatformRepository {
Ok(package)
}
- fn add_disabled_package_from_pkg(&mut self, _package: Box<dyn PackageInterface>) {
- // TODO(phase-b): downcast to CompletePackage and call `addDisabledPackage`.
+ fn add_disabled_package_from_pkg(&mut self, package: PackageInterfaceHandle) {
+ // PHP type-hints CompletePackage here; the handle is guaranteed complete by add_package.
+ let complete = package
+ .as_complete()
+ .expect("addDisabledPackage expects a CompletePackage");
+ self.add_disabled_package(complete);
}
- fn add_disabled_package(&mut self, mut package: CompletePackage) {
- let current_description = package.get_description().unwrap_or("").to_string();
+ fn add_disabled_package(&mut self, package: CompletePackageInterfaceHandle) {
+ let current_description = package.get_description().unwrap_or_default();
package.set_description(format!(
"{}. <warning>Package disabled via config.platform</warning>",
current_description
));
let mut extra: IndexMap<String, PhpMixed> = IndexMap::new();
extra.insert("config.platform".to_string(), PhpMixed::Bool(true));
- package.inner.set_extra(extra);
+ // NOTE(phase-c): neither PackageInterface nor CompletePackageInterface exposes
+ // setExtra (PHP defines it on BasePackage), and the handle API does not surface
+ // it. Disabled packages are always plain CompletePackage objects, so reach the
+ // concrete Package through the shared Rc.
+ match &mut *package.as_rc().borrow_mut() {
+ crate::package::AnyPackage::CompletePackage(p) => p.inner.set_extra(extra),
+ _ => unreachable!("disabled platform package must be a concrete CompletePackage"),
+ }
- self.disabled_packages
- .insert(package.get_name().to_string(), Box::new(package));
+ self.disabled_packages.insert(package.get_name(), package);
}
/// Parses the version and adds a new package to the repository
@@ -1768,7 +1781,7 @@ impl PlatformRepository {
ext.inner.set_replaces(replaces);
}
- self.add_package(Box::new(ext))?;
+ self.add_package(CompletePackageHandle::from_complete_package(ext).into())?;
Ok(())
}
@@ -1848,7 +1861,7 @@ impl PlatformRepository {
lib.inner.set_replaces(replace_links);
lib.inner.set_provides(provide_links);
- self.add_package(Box::new(lib))?;
+ self.add_package(CompletePackageHandle::from_complete_package(lib).into())?;
Ok(())
}
@@ -1945,7 +1958,7 @@ impl crate::repository::RepositoryInterface for PlatformRepository {
&self,
name: &str,
constraint: crate::repository::FindPackageConstraint,
- ) -> Option<Box<dyn crate::package::BasePackage>> {
+ ) -> Option<crate::package::BasePackageHandle> {
self.inner.find_package(name, constraint)
}
@@ -1953,11 +1966,11 @@ impl crate::repository::RepositoryInterface for PlatformRepository {
&self,
name: &str,
constraint: Option<crate::repository::FindPackageConstraint>,
- ) -> Vec<Box<dyn crate::package::BasePackage>> {
+ ) -> Vec<crate::package::BasePackageHandle> {
self.inner.find_packages(name, constraint)
}
- fn get_packages(&self) -> Vec<Box<dyn crate::package::BasePackage>> {
+ fn get_packages(&self) -> Vec<crate::package::BasePackageHandle> {
self.inner.get_packages()
}
@@ -1966,7 +1979,7 @@ impl crate::repository::RepositoryInterface for PlatformRepository {
package_name_map: IndexMap<String, Option<shirabe_semver::constraint::AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, crate::package::PackageInterfaceHandle>>,
) -> crate::repository::LoadPackagesResult {
self.inner.load_packages(
package_name_map,
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 4bd1f3e..9cadc48 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -1,7 +1,8 @@
//! ref: composer/src/Composer/Repository/RepositoryInterface.php
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::AdvisoryProviderInterface;
use indexmap::IndexMap;
use shirabe_php_shim::Countable;
@@ -24,7 +25,7 @@ impl Clone for FindPackageConstraint {
#[derive(Debug)]
pub struct LoadPackagesResult {
pub names_found: Vec<String>,
- pub packages: IndexMap<String, Box<dyn BasePackage>>,
+ pub packages: IndexMap<String, BasePackageHandle>,
}
#[derive(Debug, Clone)]
@@ -59,22 +60,22 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug {
&self,
name: &str,
constraint: FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>>;
+ ) -> Option<BasePackageHandle>;
fn find_packages(
&self,
name: &str,
constraint: Option<FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>>;
+ ) -> Vec<BasePackageHandle>;
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>>;
+ fn get_packages(&self) -> Vec<BasePackageHandle>;
fn load_packages(
&self,
package_name_map: IndexMap<String, Option<AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> LoadPackagesResult;
fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult>;
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index e85312a..f432d6e 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -7,7 +7,7 @@ use shirabe_semver::constraint::AnyConstraint;
use crate::config::Config;
use crate::event_dispatcher::EventDispatcher;
use crate::io::IOInterface;
-use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::FilterRepository;
use crate::repository::InstalledRepositoryInterface;
use crate::repository::RepositoryInterface;
@@ -52,13 +52,13 @@ impl RepositoryManager {
&self,
name: &str,
constraint: &AnyConstraint,
- ) -> Option<Box<dyn PackageInterface>> {
+ ) -> Option<PackageInterfaceHandle> {
for repository in &self.repositories {
if let Some(package) = repository.find_package(
name,
crate::repository::FindPackageConstraint::Constraint(constraint.clone()),
) {
- return Some(package.clone_package_box());
+ return Some(package.clone().into());
}
}
None
@@ -68,8 +68,8 @@ impl RepositoryManager {
&self,
name: &str,
constraint: &AnyConstraint,
- ) -> Vec<Box<dyn PackageInterface>> {
- let mut packages: Vec<Box<dyn PackageInterface>> = vec![];
+ ) -> Vec<PackageInterfaceHandle> {
+ let mut packages: Vec<PackageInterfaceHandle> = vec![];
for repository in self.get_repositories() {
for p in repository.find_packages(
name,
@@ -77,7 +77,7 @@ impl RepositoryManager {
constraint.clone(),
)),
) {
- packages.push(p.clone_package_box());
+ packages.push(p.clone().into());
}
}
packages
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 84159d4..1c9c4a3 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -24,11 +24,10 @@ use crate::downloader::TransportException;
use crate::event_dispatcher::EventDispatcher;
use crate::io::IOInterface;
use crate::io::NullIO;
-use crate::package::AliasPackage;
-use crate::package::BasePackage;
-use crate::package::CompleteAliasPackage;
-use crate::package::CompletePackage;
-use crate::package::PackageInterface;
+use crate::package::AliasPackageHandle;
+use crate::package::BasePackageHandle;
+use crate::package::CompleteAliasPackageHandle;
+use crate::package::PackageInterfaceHandle;
use crate::package::version::StabilityFilter;
use crate::repository::CompositeRepository;
use crate::repository::InstalledRepository;
@@ -210,11 +209,11 @@ impl RepositorySet {
name: &str,
constraint: Option<AnyConstraint>,
flags: i64,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
let ignore_stability = (flags & Self::ALLOW_UNACCEPTABLE_STABILITIES) != 0;
let load_from_all_repos = (flags & Self::ALLOW_SHADOWED_REPOSITORIES) != 0;
- let mut packages: Vec<Vec<Box<dyn BasePackage>>> = vec![];
+ let mut packages: Vec<Vec<BasePackageHandle>> = vec![];
if load_from_all_repos {
for repository in &self.repositories {
// PHP: $repository->findPackages($name, $constraint) ?: []
@@ -260,7 +259,7 @@ impl RepositorySet {
}
// PHP: $candidates = $packages ? array_merge(...$packages) : [];
- let candidates: Vec<Box<dyn BasePackage>> = if !packages.is_empty() {
+ let candidates: Vec<BasePackageHandle> = if !packages.is_empty() {
packages.into_iter().flatten().collect()
} else {
vec![]
@@ -271,9 +270,9 @@ impl RepositorySet {
return candidates;
}
- let mut result: Vec<Box<dyn BasePackage>> = vec![];
+ let mut result: Vec<BasePackageHandle> = vec![];
for candidate in candidates {
- if self.is_package_acceptable(&candidate.get_names(true), candidate.get_stability()) {
+ if self.is_package_acceptable(&candidate.get_names(true), &candidate.get_stability()) {
result.push(candidate);
}
}
@@ -312,14 +311,14 @@ impl RepositorySet {
/// @return ($allowPartialAdvisories is true ? array{advisories: array<string, array<PartialSecurityAdvisory|SecurityAdvisory>>, unreachableRepos: array<string>} : array{advisories: array<string, array<SecurityAdvisory>>, unreachableRepos: array<string>})
pub fn get_matching_security_advisories(
&self,
- packages: Vec<Box<dyn PackageInterface>>,
+ packages: Vec<PackageInterfaceHandle>,
allow_partial_advisories: bool,
ignore_unreachable: bool,
) -> Result<SecurityAdvisoriesResult> {
let mut map: IndexMap<String, AnyConstraint> = IndexMap::new();
for package in packages {
// ignore root alias versions as they are not actual package versions and should not matter when it comes to vulnerabilities
- if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_alias() {
if alias.is_root_package_alias() {
continue;
}
@@ -525,42 +524,43 @@ impl RepositorySet {
self.locked = true;
- let mut packages: Vec<Box<dyn BasePackage>> = vec![];
+ let mut packages: Vec<BasePackageHandle> = vec![];
for repository in &self.repositories {
for mut package in repository.get_packages() {
- let name = package.get_name().to_string();
- let version = package.get_version().to_string();
- packages.push(package.clone_box());
+ let name = package.get_name();
+ let version = package.get_version();
+ packages.push(package.clone());
if let Some(versions) = self.root_aliases.get(&name) {
if let Some(alias) = versions.get(&version) {
- while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>()
- {
- package = alias_pkg.get_alias_of().clone_box();
+ while let Some(alias_pkg) = package.as_alias() {
+ package = alias_pkg.get_alias_of().into();
+ }
+ let alias_package: BasePackageHandle =
+ if let Some(complete) = package.as_complete_package() {
+ CompleteAliasPackageHandle::new(
+ complete,
+ alias.alias_normalized.clone(),
+ alias.alias.clone(),
+ )
+ .into()
+ } else {
+ AliasPackageHandle::new(
+ package.as_package().unwrap(),
+ alias.alias_normalized.clone(),
+ alias.alias.clone(),
+ )
+ .into()
+ };
+ if let Some(alias_handle) = alias_package.as_alias() {
+ alias_handle.set_root_package_alias(true);
}
- let alias_package: Box<dyn BasePackage> = if package
- .as_any()
- .downcast_ref::<CompletePackage>()
- .is_some()
- {
- // TODO(phase-b): construct CompleteAliasPackage and box as BasePackage
- todo!(
- "new CompleteAliasPackage(package, alias.alias_normalized, alias.alias)"
- )
- } else {
- // TODO(phase-b): construct AliasPackage and box as BasePackage
- todo!("new AliasPackage(package, alias.alias_normalized, alias.alias)")
- };
- // TODO(phase-b): set_root_package_alias on the wrapper
- todo!("alias_package.set_root_package_alias(true)");
- #[allow(unreachable_code)]
packages.push(alias_package);
}
}
}
}
- // TODO(phase-b): Pool::new signature
Ok(Pool::new(
packages,
vec![],
diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs
index 7fe98b0..51b8673 100644
--- a/crates/shirabe/src/repository/repository_utils.rs
+++ b/crates/shirabe/src/repository/repository_utils.rs
@@ -12,11 +12,11 @@ pub struct RepositoryUtils;
impl RepositoryUtils {
pub fn filter_required_packages(
- packages: &[Box<dyn crate::package::BasePackage>],
+ packages: &[crate::package::BasePackageHandle],
requirer: &dyn PackageInterface,
include_require_dev: bool,
- mut bucket: Vec<Box<dyn crate::package::BasePackage>>,
- ) -> Vec<Box<dyn crate::package::BasePackage>> {
+ mut bucket: Vec<crate::package::BasePackageHandle>,
+ ) -> Vec<crate::package::BasePackageHandle> {
let mut requires: IndexMap<String, Link> = requirer.get_requires();
if include_require_dev {
requires.extend(requirer.get_dev_requires());
@@ -25,15 +25,9 @@ impl RepositoryUtils {
for candidate in packages {
for name in candidate.get_names(true) {
if requires.contains_key(&name) {
- let already_in_bucket = bucket.iter().any(|b| {
- std::ptr::eq(
- b.as_ref() as *const dyn crate::package::BasePackage as *const (),
- candidate.as_ref() as *const dyn crate::package::BasePackage
- as *const (),
- )
- });
+ let already_in_bucket = bucket.iter().any(|b| b.ptr_eq(candidate));
if !already_in_bucket {
- bucket.push(candidate.clone_box());
+ bucket.push(candidate.clone());
// TODO(phase-b): recursion requires &dyn PackageInterface; cast pending.
let _ = (requires.contains_key("dummy"),);
}
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index 1487f3e..71cd270 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -1,8 +1,9 @@
//! ref: composer/src/Composer/Repository/RootPackageRepository.php
-use crate::package::BasePackage;
+use crate::package::BasePackageHandle;
use crate::package::PackageInterface;
-use crate::package::RootPackageInterface;
+use crate::package::PackageInterfaceHandle;
+use crate::package::RootPackageInterfaceHandle;
use crate::repository::ArrayRepository;
use crate::repository::{ProviderInfo, RepositoryInterface, SearchResult};
use indexmap::IndexMap;
@@ -13,13 +14,9 @@ pub struct RootPackageRepository {
}
impl RootPackageRepository {
- pub fn new(package: Box<dyn RootPackageInterface>) -> Self {
+ pub fn new(package: RootPackageInterfaceHandle) -> Self {
Self {
- // TODO(phase-b): RootPackageInterface vs BasePackage upcast + ArrayRepository::new error
- inner: ArrayRepository::new(vec![todo!(
- "convert Box<dyn RootPackageInterface> to Box<dyn BasePackage>"
- )])
- .expect("invalid root package"),
+ inner: ArrayRepository::new(vec![package.into()]).expect("invalid root package"),
}
}
@@ -43,7 +40,7 @@ impl RepositoryInterface for RootPackageRepository {
&self,
name: &str,
constraint: crate::repository::FindPackageConstraint,
- ) -> Option<Box<dyn BasePackage>> {
+ ) -> Option<BasePackageHandle> {
self.inner.find_package(name, constraint)
}
@@ -51,11 +48,11 @@ impl RepositoryInterface for RootPackageRepository {
&self,
name: &str,
constraint: Option<crate::repository::FindPackageConstraint>,
- ) -> Vec<Box<dyn BasePackage>> {
+ ) -> Vec<BasePackageHandle> {
self.inner.find_packages(name, constraint)
}
- fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ fn get_packages(&self) -> Vec<BasePackageHandle> {
self.inner.get_packages()
}
@@ -64,7 +61,7 @@ impl RepositoryInterface for RootPackageRepository {
package_name_map: IndexMap<String, Option<shirabe_semver::constraint::AnyConstraint>>,
acceptable_stabilities: IndexMap<String, i64>,
stability_flags: IndexMap<String, i64>,
- already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
) -> crate::repository::LoadPackagesResult {
self.inner.load_packages(
package_name_map,
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index 1ae7e40..a256fb8 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -15,7 +15,6 @@ use crate::config::Config;
use crate::downloader::TransportException;
use crate::event_dispatcher::EventDispatcher;
use crate::io::IOInterface;
-use crate::package::BasePackage;
use crate::package::loader::ArrayLoader;
use crate::package::loader::InvalidPackageException;
use crate::package::loader::LoaderInterface;
@@ -387,9 +386,7 @@ impl VcsRepository {
)?;
match cached_package {
CachedPackageResult::Package(pkg) => {
- // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
- let pkg_pi: Box<dyn crate::package::PackageInterface> = pkg.clone_package_box();
- self.inner.add_package(pkg_pi)?;
+ self.inner.add_package(pkg)?;
continue;
}
CachedPackageResult::Missing => {
@@ -541,10 +538,7 @@ impl VcsRepository {
let driver = self.driver.as_ref().unwrap();
let processed = self.pre_process(&**driver, data, &identifier)?;
let loaded = self.loader.as_ref().unwrap().load(processed, None)?;
- // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
- let loaded_pi: Box<dyn crate::package::PackageInterface> =
- loaded.clone_package_box();
- self.inner.add_package(loaded_pi)?;
+ self.inner.add_package(loaded)?;
Ok(())
})();
if let Err(e) = result {
@@ -662,9 +656,7 @@ impl VcsRepository {
)?;
match cached_package {
CachedPackageResult::Package(pkg) => {
- // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
- let pkg_pi: Box<dyn crate::package::PackageInterface> = pkg.clone_package_box();
- self.inner.add_package(pkg_pi)?;
+ self.inner.add_package(pkg)?;
continue;
}
CachedPackageResult::Missing => {
@@ -727,10 +719,7 @@ impl VcsRepository {
);
}
}
- // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
- let package_pi: Box<dyn crate::package::PackageInterface> =
- package.clone_package_box();
- self.inner.add_package(package_pi)?;
+ self.inner.add_package(package)?;
Ok(())
})();
if let Err(e) = result {
@@ -995,5 +984,5 @@ impl VcsRepository {
enum CachedPackageResult {
None,
Missing,
- Package(Box<dyn BasePackage>),
+ Package(crate::package::PackageInterfaceHandle),
}
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index 663ab52..ebe70c3 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -14,7 +14,7 @@ pub struct WritableArrayRepository {
}
impl WritableArrayRepository {
- pub fn new(packages: Vec<Box<dyn crate::package::PackageInterface>>) -> Result<Self> {
+ pub fn new(packages: Vec<crate::package::PackageInterfaceHandle>) -> Result<Self> {
Ok(Self {
inner: ArrayRepository::new(packages)?,
dev_package_names: Vec::new(),
@@ -48,10 +48,7 @@ impl WritableArrayRepository {
self.dev_mode = None;
}
- pub fn add_package(
- &mut self,
- package: Box<dyn crate::package::PackageInterface>,
- ) -> Result<()> {
+ pub fn add_package(&mut self, package: crate::package::PackageInterfaceHandle) -> Result<()> {
self.inner.add_package(package)
}
@@ -66,14 +63,13 @@ impl WritableArrayRepository {
Ok(())
}
- pub fn get_canonical_packages(&self) -> Vec<Box<dyn crate::package::PackageInterface>> {
+ pub fn get_canonical_packages(&self) -> Vec<crate::package::PackageInterfaceHandle> {
// TODO(phase-b): delegate to inner once it exposes get_canonical_packages
Vec::new()
}
- pub fn get_packages(&self) -> Vec<Box<dyn crate::package::PackageInterface>> {
- // TODO(phase-b): delegate to inner ArrayRepository::get_packages
- Vec::new()
+ pub fn get_packages(&self) -> Vec<crate::package::BasePackageHandle> {
+ crate::repository::RepositoryInterface::get_packages(&self.inner)
}
pub fn get_repo_name(&self) -> String {
diff --git a/crates/shirabe/src/repository/writable_repository_interface.rs b/crates/shirabe/src/repository/writable_repository_interface.rs
index 7094211..b5b1e35 100644
--- a/crates/shirabe/src/repository/writable_repository_interface.rs
+++ b/crates/shirabe/src/repository/writable_repository_interface.rs
@@ -2,17 +2,18 @@
use crate::installer::InstallationManager;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::repository::RepositoryInterface;
use anyhow::Result;
pub trait WritableRepositoryInterface: RepositoryInterface {
fn write(&mut self, dev_mode: bool, installation_manager: &InstallationManager) -> Result<()>;
- fn add_package(&mut self, package: Box<dyn PackageInterface>) -> Result<()>;
+ fn add_package(&mut self, package: PackageInterfaceHandle) -> Result<()>;
fn remove_package(&mut self, package: &dyn PackageInterface) -> Result<()>;
- fn get_canonical_packages(&self) -> Vec<Box<dyn PackageInterface>>;
+ fn get_canonical_packages(&self) -> Vec<PackageInterfaceHandle>;
fn reload(&mut self);
diff --git a/crates/shirabe/src/util/package_sorter.rs b/crates/shirabe/src/util/package_sorter.rs
index 185f595..9db56f5 100644
--- a/crates/shirabe/src/util/package_sorter.rs
+++ b/crates/shirabe/src/util/package_sorter.rs
@@ -1,20 +1,19 @@
//! ref: composer/src/Composer/Util/PackageSorter.php
-use std::any::Any;
-
use indexmap::IndexMap;
use shirabe_php_shim::{strnatcasecmp, version_compare};
use crate::package::Link;
use crate::package::PackageInterface;
+use crate::package::PackageInterfaceHandle;
use crate::package::RootPackage;
pub struct PackageSorter;
impl PackageSorter {
pub fn get_most_current_version(
- packages: Vec<Box<dyn PackageInterface>>,
- ) -> Option<Box<dyn PackageInterface>> {
+ packages: Vec<PackageInterfaceHandle>,
+ ) -> Option<PackageInterfaceHandle> {
if packages.is_empty() {
return None;
}
@@ -25,7 +24,7 @@ impl PackageSorter {
if candidate.is_default_branch() {
return Some(candidate);
}
- if version_compare(highest.get_version(), candidate.get_version(), "<") {
+ if version_compare(&highest.get_version(), &candidate.get_version(), "<") {
highest = candidate;
}
}
@@ -34,16 +33,16 @@ impl PackageSorter {
}
pub fn sort_packages_alphabetically(
- mut packages: Vec<Box<dyn PackageInterface>>,
- ) -> Vec<Box<dyn PackageInterface>> {
- packages.sort_by_key(|p| p.get_name().to_string());
+ mut packages: Vec<PackageInterfaceHandle>,
+ ) -> Vec<PackageInterfaceHandle> {
+ packages.sort_by_key(|p| p.get_name());
packages
}
pub fn sort_packages(
- packages: Vec<Box<dyn PackageInterface>>,
+ packages: Vec<PackageInterfaceHandle>,
weights: IndexMap<String, i64>,
- ) -> Vec<Box<dyn PackageInterface>> {
+ ) -> Vec<PackageInterfaceHandle> {
let mut usage_list: IndexMap<String, Vec<String>> = IndexMap::new();
for package in &packages {
@@ -58,7 +57,7 @@ impl PackageSorter {
usage_list
.entry(target)
.or_default()
- .push(package.get_name().to_string());
+ .push(package.get_name());
}
}
@@ -84,7 +83,7 @@ impl PackageSorter {
}
});
- let mut packages: Vec<Option<Box<dyn PackageInterface>>> =
+ let mut packages: Vec<Option<PackageInterfaceHandle>> =
packages.into_iter().map(Some).collect();
weighted_packages
.into_iter()