aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command
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/command
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/command')
-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
18 files changed, 304 insertions, 315 deletions
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,