aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-19 00:10:22 +0900
committernsfisis <nsfisis@gmail.com>2026-05-19 00:11:03 +0900
commitc839244d8d09f3036ebfee8eef7eb6b147e593ab (patch)
treefe48c94f2c2e62468beef5ff1a8f3cff6adeef4f /crates
parent48839250146b217e2756ed3c0e624fd341b54d6c (diff)
downloadphp-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.tar.gz
php-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.tar.zst
php-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.zip
fix(compile): fix various compile errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-class-map-generator/src/class_map_generator.rs7
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/preg.rs8
-rw-r--r--crates/shirabe-external-packages/src/react/promise/mod.rs6
-rw-r--r--crates/shirabe-external-packages/src/react/promise/promise.rs14
-rw-r--r--crates/shirabe-external-packages/src/symfony/component/console/command/command.rs111
-rw-r--r--crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs12
-rw-r--r--crates/shirabe-external-packages/src/symfony/component/finder/finder.rs51
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/question/question.rs4
-rw-r--r--crates/shirabe-php-shim/src/lib.rs91
-rw-r--r--crates/shirabe-semver/src/comparator.rs4
-rw-r--r--crates/shirabe-semver/src/compiling_matcher.rs2
-rw-r--r--crates/shirabe-semver/src/constraint/constraint.rs17
-rw-r--r--crates/shirabe-semver/src/constraint/constraint_interface.rs2
-rw-r--r--crates/shirabe-semver/src/constraint/multi_constraint.rs38
-rw-r--r--crates/shirabe-semver/src/interval.rs7
-rw-r--r--crates/shirabe-semver/src/intervals.rs39
-rw-r--r--crates/shirabe-semver/src/semver.rs3
-rw-r--r--crates/shirabe-semver/src/version_parser.rs30
-rw-r--r--crates/shirabe/src/advisory/audit_config.rs8
-rw-r--r--crates/shirabe/src/advisory/auditor.rs145
-rw-r--r--crates/shirabe/src/advisory/ignored_security_advisory.rs15
-rw-r--r--crates/shirabe/src/advisory/partial_security_advisory.rs17
-rw-r--r--crates/shirabe/src/advisory/security_advisory.rs7
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs363
-rw-r--r--crates/shirabe/src/autoload/class_loader.rs5
-rw-r--r--crates/shirabe/src/autoload/class_map_generator.rs28
-rw-r--r--crates/shirabe/src/cache.rs173
-rw-r--r--crates/shirabe/src/command/about_command.rs44
-rw-r--r--crates/shirabe/src/command/archive_command.rs105
-rw-r--r--crates/shirabe/src/command/audit_command.rs68
-rw-r--r--crates/shirabe/src/command/base_command.rs419
-rw-r--r--crates/shirabe/src/command/base_config_command.rs11
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs67
-rw-r--r--crates/shirabe/src/command/bump_command.rs139
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs62
-rw-r--r--crates/shirabe/src/command/clear_cache_command.rs150
-rw-r--r--crates/shirabe/src/command/completion_trait.rs278
-rw-r--r--crates/shirabe/src/command/config_command.rs376
-rw-r--r--crates/shirabe/src/command/create_project_command.rs144
-rw-r--r--crates/shirabe/src/command/depends_command.rs87
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs77
-rw-r--r--crates/shirabe/src/command/dump_autoload_command.rs92
-rw-r--r--crates/shirabe/src/command/exec_command.rs68
-rw-r--r--crates/shirabe/src/command/fund_command.rs47
-rw-r--r--crates/shirabe/src/command/global_command.rs88
-rw-r--r--crates/shirabe/src/command/home_command.rs69
-rw-r--r--crates/shirabe/src/command/init_command.rs115
-rw-r--r--crates/shirabe/src/command/install_command.rs118
-rw-r--r--crates/shirabe/src/command/licenses_command.rs54
-rw-r--r--crates/shirabe/src/command/outdated_command.rs86
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs59
-rw-r--r--crates/shirabe/src/command/prohibits_command.rs87
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs116
-rw-r--r--crates/shirabe/src/command/remove_command.rs82
-rw-r--r--crates/shirabe/src/command/repository_command.rs167
-rw-r--r--crates/shirabe/src/command/require_command.rs163
-rw-r--r--crates/shirabe/src/command/run_script_command.rs67
-rw-r--r--crates/shirabe/src/command/script_alias_command.rs45
-rw-r--r--crates/shirabe/src/command/search_command.rs66
-rw-r--r--crates/shirabe/src/command/self_update_command.rs232
-rw-r--r--crates/shirabe/src/command/show_command.rs139
-rw-r--r--crates/shirabe/src/command/status_command.rs73
-rw-r--r--crates/shirabe/src/command/suggests_command.rs62
-rw-r--r--crates/shirabe/src/command/update_command.rs106
-rw-r--r--crates/shirabe/src/command/validate_command.rs57
-rw-r--r--crates/shirabe/src/compiler.rs4
-rw-r--r--crates/shirabe/src/composer.rs43
-rw-r--r--crates/shirabe/src/console/application.rs112
-rw-r--r--crates/shirabe/src/console/html_output_formatter.rs6
-rw-r--r--crates/shirabe/src/console/input/input_argument.rs54
-rw-r--r--crates/shirabe/src/console/input/input_option.rs81
-rw-r--r--crates/shirabe/src/dependency_resolver/decisions.rs10
-rw-r--r--crates/shirabe/src/dependency_resolver/default_policy.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/local_repo_transaction.rs17
-rw-r--r--crates/shirabe/src/dependency_resolver/lock_transaction.rs15
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/install_operation.rs3
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs17
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs17
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/operation_interface.rs6
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs3
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/update_operation.rs28
-rw-r--r--crates/shirabe/src/dependency_resolver/policy_interface.rs4
-rw-r--r--crates/shirabe/src/dependency_resolver/pool.rs2
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_builder.rs34
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_optimizer.rs56
-rw-r--r--crates/shirabe/src/dependency_resolver/problem.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/request.rs1
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set.rs25
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set_generator.rs14
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_watch_chain.rs19
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_watch_graph.rs64
-rw-r--r--crates/shirabe/src/dependency_resolver/solver.rs26
-rw-r--r--crates/shirabe/src/dependency_resolver/solver_bug_exception.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/solver_problems_exception.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/transaction.rs29
-rw-r--r--crates/shirabe/src/downloader/archive_downloader.rs4
-rw-r--r--crates/shirabe/src/downloader/download_manager.rs2
-rw-r--r--crates/shirabe/src/downloader/file_downloader.rs37
-rw-r--r--crates/shirabe/src/downloader/filesystem_exception.rs8
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs88
-rw-r--r--crates/shirabe/src/downloader/gzip_downloader.rs58
-rw-r--r--crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs15
-rw-r--r--crates/shirabe/src/downloader/path_downloader.rs10
-rw-r--r--crates/shirabe/src/downloader/phar_downloader.rs94
-rw-r--r--crates/shirabe/src/downloader/rar_downloader.rs111
-rw-r--r--crates/shirabe/src/downloader/tar_downloader.rs94
-rw-r--r--crates/shirabe/src/downloader/transport_exception.rs8
-rw-r--r--crates/shirabe/src/downloader/vcs_downloader.rs2
-rw-r--r--crates/shirabe/src/downloader/xz_downloader.rs110
-rw-r--r--crates/shirabe/src/downloader/zip_downloader.rs18
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs51
-rw-r--r--crates/shirabe/src/event_dispatcher/script_execution_exception.rs10
-rw-r--r--crates/shirabe/src/exception/irrecoverable_download_exception.rs9
-rw-r--r--crates/shirabe/src/factory.rs94
-rw-r--r--crates/shirabe/src/filter/platform_requirement_filter/ignore_all_platform_requirement_filter.rs4
-rw-r--r--crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs9
-rw-r--r--crates/shirabe/src/filter/platform_requirement_filter/ignore_nothing_platform_requirement_filter.rs4
-rw-r--r--crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs17
-rw-r--r--crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_interface.rs8
-rw-r--r--crates/shirabe/src/installer.rs23
-rw-r--r--crates/shirabe/src/installer/binary_installer.rs2
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs23
-rw-r--r--crates/shirabe/src/installer/installer_event.rs2
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs10
-rw-r--r--crates/shirabe/src/installer/library_installer.rs28
-rw-r--r--crates/shirabe/src/installer/metapackage_installer.rs16
-rw-r--r--crates/shirabe/src/installer/noop_installer.rs10
-rw-r--r--crates/shirabe/src/installer/package_event.rs4
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs19
-rw-r--r--crates/shirabe/src/installer/project_installer.rs26
-rw-r--r--crates/shirabe/src/installer/suggested_packages_reporter.rs1
-rw-r--r--crates/shirabe/src/io/base_io.rs42
-rw-r--r--crates/shirabe/src/io/console_io.rs47
-rw-r--r--crates/shirabe/src/io/io_interface.rs72
-rw-r--r--crates/shirabe/src/io/null_io.rs37
-rw-r--r--crates/shirabe/src/json/json_file.rs6
-rw-r--r--crates/shirabe/src/json/json_formatter.rs29
-rw-r--r--crates/shirabe/src/json/json_manipulator.rs7
-rw-r--r--crates/shirabe/src/json/json_validation_exception.rs4
-rw-r--r--crates/shirabe/src/package/alias_package.rs81
-rw-r--r--crates/shirabe/src/package/archiver/archivable_files_finder.rs18
-rw-r--r--crates/shirabe/src/package/archiver/archive_manager.rs27
-rw-r--r--crates/shirabe/src/package/archiver/base_exclude_filter.rs4
-rw-r--r--crates/shirabe/src/package/archiver/git_exclude_filter.rs7
-rw-r--r--crates/shirabe/src/package/archiver/phar_archiver.rs14
-rw-r--r--crates/shirabe/src/package/archiver/zip_archiver.rs8
-rw-r--r--crates/shirabe/src/package/base_package.rs74
-rw-r--r--crates/shirabe/src/package/comparer/comparer.rs2
-rw-r--r--crates/shirabe/src/package/complete_alias_package.rs15
-rw-r--r--crates/shirabe/src/package/complete_package.rs10
-rw-r--r--crates/shirabe/src/package/complete_package_interface.rs4
-rw-r--r--crates/shirabe/src/package/dumper/array_dumper.rs22
-rw-r--r--crates/shirabe/src/package/link.rs14
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs2
-rw-r--r--crates/shirabe/src/package/loader/invalid_package_exception.rs11
-rw-r--r--crates/shirabe/src/package/loader/json_loader.rs11
-rw-r--r--crates/shirabe/src/package/loader/validating_array_loader.rs5
-rw-r--r--crates/shirabe/src/package/locker.rs5
-rw-r--r--crates/shirabe/src/package/package.rs26
-rw-r--r--crates/shirabe/src/package/package_interface.rs40
-rw-r--r--crates/shirabe/src/package/root_alias_package.rs10
-rw-r--r--crates/shirabe/src/package/root_package.rs25
-rw-r--r--crates/shirabe/src/package/root_package_interface.rs14
-rw-r--r--crates/shirabe/src/package/version/stability_filter.rs4
-rw-r--r--crates/shirabe/src/package/version/version_bumper.rs12
-rw-r--r--crates/shirabe/src/package/version/version_parser.rs22
-rw-r--r--crates/shirabe/src/partial_composer.rs6
-rw-r--r--crates/shirabe/src/platform/hhvm_detector.rs24
-rw-r--r--crates/shirabe/src/plugin/command_event.rs23
-rw-r--r--crates/shirabe/src/plugin/plugin_blocked_exception.rs6
-rw-r--r--crates/shirabe/src/plugin/plugin_interface.rs6
-rw-r--r--crates/shirabe/src/plugin/plugin_manager.rs7
-rw-r--r--crates/shirabe/src/plugin/post_file_download_event.rs4
-rw-r--r--crates/shirabe/src/plugin/pre_command_run_event.rs17
-rw-r--r--crates/shirabe/src/plugin/pre_pool_create_event.rs4
-rw-r--r--crates/shirabe/src/question/strict_confirmation_question.rs21
-rw-r--r--crates/shirabe/src/repository/array_repository.rs27
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs30
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs29
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs16
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs20
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs8
-rw-r--r--crates/shirabe/src/repository/invalid_repository_exception.rs6
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs77
-rw-r--r--crates/shirabe/src/repository/package_repository.rs16
-rw-r--r--crates/shirabe/src/repository/path_repository.rs2
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs83
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs3
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs6
-rw-r--r--crates/shirabe/src/repository/repository_utils.rs21
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs76
-rw-r--r--crates/shirabe/src/repository/vcs/fossil_driver.rs2
-rw-r--r--crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs2
-rw-r--r--crates/shirabe/src/repository/vcs/git_driver.rs68
-rw-r--r--crates/shirabe/src/repository/vcs/hg_driver.rs4
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs3
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs20
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs35
-rw-r--r--crates/shirabe/src/script/script_events.rs18
-rw-r--r--crates/shirabe/src/self_update/versions.rs10
-rw-r--r--crates/shirabe/src/util/auth_helper.rs3
-rw-r--r--crates/shirabe/src/util/bitbucket.rs2
-rw-r--r--crates/shirabe/src/util/composer_mirror.rs47
-rw-r--r--crates/shirabe/src/util/config_validator.rs4
-rw-r--r--crates/shirabe/src/util/error_handler.rs8
-rw-r--r--crates/shirabe/src/util/filesystem.rs2
-rw-r--r--crates/shirabe/src/util/forgejo.rs20
-rw-r--r--crates/shirabe/src/util/forgejo_url.rs18
-rw-r--r--crates/shirabe/src/util/github.rs3
-rw-r--r--crates/shirabe/src/util/gitlab.rs2
-rw-r--r--crates/shirabe/src/util/http/curl_downloader.rs26
-rw-r--r--crates/shirabe/src/util/http/curl_response.rs8
-rw-r--r--crates/shirabe/src/util/http/proxy_item.rs18
-rw-r--r--crates/shirabe/src/util/http/proxy_manager.rs4
-rw-r--r--crates/shirabe/src/util/http/request_proxy.rs1
-rw-r--r--crates/shirabe/src/util/http/response.rs9
-rw-r--r--crates/shirabe/src/util/http_downloader.rs11
-rw-r--r--crates/shirabe/src/util/no_proxy_pattern.rs2
-rw-r--r--crates/shirabe/src/util/package_info.rs4
-rw-r--r--crates/shirabe/src/util/package_sorter.rs8
-rw-r--r--crates/shirabe/src/util/platform.rs27
-rw-r--r--crates/shirabe/src/util/process_executor.rs4
-rw-r--r--crates/shirabe/src/util/remote_filesystem.rs26
-rw-r--r--crates/shirabe/src/util/svn.rs6
-rw-r--r--crates/shirabe/src/util/sync_helper.rs21
225 files changed, 4394 insertions, 4564 deletions
diff --git a/crates/shirabe-class-map-generator/src/class_map_generator.rs b/crates/shirabe-class-map-generator/src/class_map_generator.rs
index 733915e..a49ce66 100644
--- a/crates/shirabe-class-map-generator/src/class_map_generator.rs
+++ b/crates/shirabe-class-map-generator/src/class_map_generator.rs
@@ -45,7 +45,7 @@ impl ClassMapGenerator {
/// When calling scanPaths repeatedly with paths that may overlap, calling this will ensure that the same class is never scanned twice
pub fn avoid_duplicate_scans(&mut self, scanned_files: Option<FileList>) -> &mut Self {
- self.scanned_files = Some(scanned_files.unwrap_or_else(FileList::new));
+ self.scanned_files = Some(scanned_files.unwrap_or_default());
self
}
@@ -60,6 +60,11 @@ impl ClassMapGenerator {
&self.class_map
}
+ /// Take ownership of the inner ClassMap, leaving a default in its place.
+ pub fn take_class_map(&mut self) -> ClassMap {
+ std::mem::take(&mut self.class_map)
+ }
+
/// Iterate over all files in the given directory searching for classes
pub fn scan_paths(
&mut self,
diff --git a/crates/shirabe-external-packages/src/composer/pcre/preg.rs b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
index 9c39d42..d1614a7 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/preg.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
@@ -363,6 +363,14 @@ impl Preg {
todo!()
}
+ pub fn is_match_named(
+ _pattern: &str,
+ _subject: &str,
+ _matches: &mut indexmap::IndexMap<String, String>,
+ ) -> anyhow::Result<bool> {
+ todo!()
+ }
+
pub fn is_match_strict_groups(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
todo!()
}
diff --git a/crates/shirabe-external-packages/src/react/promise/mod.rs b/crates/shirabe-external-packages/src/react/promise/mod.rs
index 146c2f1..5f1195f 100644
--- a/crates/shirabe-external-packages/src/react/promise/mod.rs
+++ b/crates/shirabe-external-packages/src/react/promise/mod.rs
@@ -1,13 +1,13 @@
pub mod promise;
pub mod promise_interface;
-use self::promise::Promise;
+use self::promise_interface::PromiseInterface;
use shirabe_php_shim::PhpMixed;
-pub fn resolve(_value: Option<PhpMixed>) -> Promise {
+pub fn resolve(_value: Option<PhpMixed>) -> Box<dyn PromiseInterface> {
todo!()
}
-pub fn all(_promises: Vec<Promise>) -> Promise {
+pub fn all(_promises: Vec<Box<dyn PromiseInterface>>) -> Box<dyn PromiseInterface> {
todo!()
}
diff --git a/crates/shirabe-external-packages/src/react/promise/promise.rs b/crates/shirabe-external-packages/src/react/promise/promise.rs
index 96fa3ed..9407b57 100644
--- a/crates/shirabe-external-packages/src/react/promise/promise.rs
+++ b/crates/shirabe-external-packages/src/react/promise/promise.rs
@@ -1,5 +1,7 @@
use shirabe_php_shim::PhpMixed;
+use super::promise_interface::PromiseInterface;
+
#[derive(Debug)]
pub struct Promise;
@@ -11,12 +13,14 @@ impl Promise {
) -> Self {
todo!()
}
+}
- pub fn then<F, G>(self, _on_fulfilled: Option<F>, _on_rejected: Option<G>) -> Self
- where
- F: FnOnce(Option<PhpMixed>) -> Option<PhpMixed>,
- G: FnOnce(Option<PhpMixed>) -> Option<PhpMixed>,
- {
+impl PromiseInterface for Promise {
+ fn then(
+ &self,
+ _on_fulfilled: Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>>,
+ _on_rejected: Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>>,
+ ) -> Box<dyn PromiseInterface> {
todo!()
}
}
diff --git a/crates/shirabe-external-packages/src/symfony/component/console/command/command.rs b/crates/shirabe-external-packages/src/symfony/component/console/command/command.rs
index c09940e..32965cf 100644
--- a/crates/shirabe-external-packages/src/symfony/component/console/command/command.rs
+++ b/crates/shirabe-external-packages/src/symfony/component/console/command/command.rs
@@ -1,119 +1,18 @@
-use crate::symfony::component::console::completion::completion_input::CompletionInput;
-use crate::symfony::component::console::completion::completion_suggestions::CompletionSuggestions;
-use crate::symfony::component::console::input::input_definition::InputDefinition;
-use crate::symfony::component::console::input::input_interface::InputInterface;
-use crate::symfony::component::console::output::output_interface::OutputInterface;
-use shirabe_php_shim::PhpMixed;
-
+/// Stub for \Symfony\Component\Console\Command\Command.
pub trait Command {
- fn get_name(&self) -> String {
- todo!()
- }
- fn set_name(&mut self, _name: &str) {
- todo!()
- }
- fn get_description(&self) -> String {
- todo!()
- }
- fn set_description(&mut self, _description: &str) {
- todo!()
- }
- fn get_definition(&self) -> &InputDefinition {
- todo!()
- }
- fn complete(&self, _input: &CompletionInput, _suggestions: &mut CompletionSuggestions) {
- todo!()
- }
-}
-
-#[derive(Debug)]
-pub struct CommandBase;
-
-impl Command for CommandBase {}
-
-impl CommandBase {
- pub fn new(_name: Option<&str>) -> Self {
- todo!()
- }
-
- pub fn set_name(&mut self, _name: &str) -> &mut Self {
- todo!()
- }
-
- pub fn get_name(&self) -> Option<String> {
- todo!()
- }
-
- pub fn set_description(&mut self, _description: &str) -> &mut Self {
- todo!()
- }
-
- pub fn get_description(&self) -> String {
- todo!()
- }
-
- pub fn set_help(&mut self, _help: &str) -> &mut Self {
- todo!()
- }
-
- pub fn set_definition(&mut self, _definition: PhpMixed) -> &mut Self {
- todo!()
- }
-
- pub fn get_definition(&self) -> &InputDefinition {
- todo!()
- }
-
- pub fn add_argument(
- &mut self,
- _name: &str,
- _mode: Option<i64>,
- _description: &str,
- _default: PhpMixed,
- ) -> &mut Self {
- todo!()
- }
-
- pub fn add_option(
- &mut self,
- _name: &str,
- _shortcut: Option<&str>,
- _mode: Option<i64>,
- _description: &str,
- _default: PhpMixed,
- ) -> &mut Self {
+ fn get_name(&self) -> Option<String> {
todo!()
}
- pub fn set_aliases(&mut self, _aliases: &[String]) -> &mut Self {
- todo!()
- }
-
- pub fn get_aliases(&self) -> Vec<String> {
- todo!()
- }
-
- pub fn set_hidden(&mut self, _hidden: bool) -> &mut Self {
- todo!()
- }
-
- pub fn is_hidden(&self) -> bool {
- todo!()
- }
-
- pub fn run(
- &mut self,
- _input: &mut dyn InputInterface,
- _output: &mut dyn OutputInterface,
- ) -> anyhow::Result<i64> {
+ fn set_name(&mut self, _name: &str) {
todo!()
}
- pub fn get_helper(&self, _name: &str) -> PhpMixed {
+ fn get_description(&self) -> String {
todo!()
}
- pub fn get_helper_set(&self) -> PhpMixed {
+ fn set_description(&mut self, _description: &str) {
todo!()
}
}
diff --git a/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs b/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs
index ba01e07..0f7f336 100644
--- a/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs
+++ b/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs
@@ -29,7 +29,19 @@ impl Table {
todo!()
}
+ pub fn set_column_width(&mut self, _column_index: usize, _width: i64) -> &mut Self {
+ todo!()
+ }
+
pub fn set_column_widths(&mut self, _widths: Vec<i64>) -> &mut Self {
todo!()
}
+
+ pub fn set_column_max_width(&mut self, _column_index: usize, _width: i64) -> &mut Self {
+ todo!()
+ }
+
+ pub fn set_horizontal(&mut self, _horizontal: bool) -> &mut Self {
+ todo!()
+ }
}
diff --git a/crates/shirabe-external-packages/src/symfony/component/finder/finder.rs b/crates/shirabe-external-packages/src/symfony/component/finder/finder.rs
index ae95aeb..8437ba6 100644
--- a/crates/shirabe-external-packages/src/symfony/component/finder/finder.rs
+++ b/crates/shirabe-external-packages/src/symfony/component/finder/finder.rs
@@ -62,8 +62,59 @@ impl Finder {
todo!()
}
+ pub fn sort_by_accessed_time(&mut self) -> &mut Self {
+ todo!()
+ }
+
+ pub fn date(&mut self, _date: &str) -> &mut Self {
+ todo!()
+ }
+
+ pub fn get_iterator(&self) -> FinderIterator {
+ todo!()
+ }
+
pub fn iter(&self) -> impl Iterator<Item = SplFileInfo> {
todo!();
std::iter::empty()
}
}
+
+#[derive(Debug)]
+pub struct FinderIterator;
+
+impl FinderIterator {
+ pub fn valid(&self) -> bool {
+ todo!()
+ }
+
+ pub fn current(&self) -> SplFileInfo {
+ todo!()
+ }
+}
+
+impl Iterator for FinderIterator {
+ type Item = SplFileInfo;
+
+ fn next(&mut self) -> Option<SplFileInfo> {
+ todo!()
+ }
+}
+
+impl IntoIterator for Finder {
+ type Item = SplFileInfo;
+ type IntoIter = std::vec::IntoIter<SplFileInfo>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
+
+impl IntoIterator for &mut Finder {
+ type Item = SplFileInfo;
+ type IntoIter = std::vec::IntoIter<SplFileInfo>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ todo!()
+ }
+}
diff --git a/crates/shirabe-external-packages/src/symfony/console/question/question.rs b/crates/shirabe-external-packages/src/symfony/console/question/question.rs
index 4cc4d6d..02402ce 100644
--- a/crates/shirabe-external-packages/src/symfony/console/question/question.rs
+++ b/crates/shirabe-external-packages/src/symfony/console/question/question.rs
@@ -15,6 +15,10 @@ impl Question {
todo!()
}
+ pub fn set_normalizer(&mut self, _normalizer: Box<dyn Fn(&PhpMixed) -> PhpMixed>) {
+ todo!()
+ }
+
pub fn set_max_attempts(&mut self, _attempts: Option<i64>) {
todo!()
}
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index a8793cf..e6fbf05 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -9,6 +9,7 @@ pub enum PhpMixed {
String(String),
List(Vec<Box<PhpMixed>>),
Array(IndexMap<String, Box<PhpMixed>>),
+ Object(ArrayObject),
}
impl PhpMixed {
@@ -57,6 +58,94 @@ impl PhpMixed {
pub fn is_null(&self) -> bool {
matches!(self, PhpMixed::Null)
}
+
+ pub fn as_string_opt(&self) -> Option<&str> {
+ self.as_string()
+ }
+
+ pub fn get(&self, key: &str) -> Option<&PhpMixed> {
+ self.as_array().and_then(|m| m.get(key).map(|v| v.as_ref()))
+ }
+
+ /// Treats PhpMixed::Null as None, everything else as Some.
+ pub fn as_opt(&self) -> Option<&PhpMixed> {
+ if self.is_null() { None } else { Some(self) }
+ }
+
+ pub fn unwrap_or(self, default: PhpMixed) -> PhpMixed {
+ if self.is_null() { default } else { self }
+ }
+}
+
+impl From<bool> for PhpMixed {
+ fn from(_value: bool) -> Self {
+ todo!()
+ }
+}
+
+impl From<i64> for PhpMixed {
+ fn from(_value: i64) -> Self {
+ todo!()
+ }
+}
+
+impl From<f64> for PhpMixed {
+ fn from(_value: f64) -> Self {
+ todo!()
+ }
+}
+
+impl From<String> for PhpMixed {
+ fn from(_value: String) -> Self {
+ todo!()
+ }
+}
+
+impl From<&str> for PhpMixed {
+ fn from(value: &str) -> Self {
+ PhpMixed::String(value.to_string())
+ }
+}
+
+impl<T> From<IndexMap<String, T>> for PhpMixed
+where
+ T: Into<PhpMixed>,
+{
+ fn from(value: IndexMap<String, T>) -> Self {
+ PhpMixed::Array(
+ value
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v.into())))
+ .collect(),
+ )
+ }
+}
+
+impl<T> From<Vec<T>> for PhpMixed
+where
+ T: Into<PhpMixed>,
+{
+ fn from(value: Vec<T>) -> Self {
+ PhpMixed::List(value.into_iter().map(|v| Box::new(v.into())).collect())
+ }
+}
+
+impl<T> From<Option<T>> for PhpMixed
+where
+ T: Into<PhpMixed>,
+{
+ fn from(value: Option<T>) -> Self {
+ match value {
+ Some(v) => v.into(),
+ None => PhpMixed::Null,
+ }
+ }
+}
+
+impl std::fmt::Display for PhpMixed {
+ fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ todo!()
+ }
}
#[derive(Debug)]
@@ -2038,7 +2127,7 @@ pub const PHP_WINDOWS_VERSION_BUILD: i64 = 0;
pub const DATE_RFC3339: &str = "Y-m-d\\TH:i:sP";
pub const PREG_BACKTRACK_LIMIT_ERROR: i64 = 2;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ArrayObject {
data: IndexMap<String, Box<PhpMixed>>,
}
diff --git a/crates/shirabe-semver/src/comparator.rs b/crates/shirabe-semver/src/comparator.rs
index 0ae6d94..575a656 100644
--- a/crates/shirabe-semver/src/comparator.rs
+++ b/crates/shirabe-semver/src/comparator.rs
@@ -30,7 +30,7 @@ impl Comparator {
}
pub fn compare(version1: String, operator: String, version2: String) -> bool {
- let constraint = Constraint::new(operator, version2).unwrap();
- constraint.match_specific(&Constraint::new("==".to_string(), version1).unwrap(), true)
+ let constraint = Constraint::new(operator, version2);
+ constraint.match_specific(&Constraint::new("==".to_string(), version1), true)
}
}
diff --git a/crates/shirabe-semver/src/compiling_matcher.rs b/crates/shirabe-semver/src/compiling_matcher.rs
index 942abe5..f6a3689 100644
--- a/crates/shirabe-semver/src/compiling_matcher.rs
+++ b/crates/shirabe-semver/src/compiling_matcher.rs
@@ -56,7 +56,7 @@ impl CompilingMatcher {
.find(|(op, _)| *op == operator)
.map(|(_, s)| *s)
.expect("unknown operator");
- let result = constraint.matches(&Constraint::new(trans_op.to_string(), version).unwrap());
+ let result = constraint.matches(&Constraint::new(trans_op.to_string(), version));
Self::result_cache()
.lock()
diff --git a/crates/shirabe-semver/src/constraint/constraint.rs b/crates/shirabe-semver/src/constraint/constraint.rs
index f8ff04e..1435139 100644
--- a/crates/shirabe-semver/src/constraint/constraint.rs
+++ b/crates/shirabe-semver/src/constraint/constraint.rs
@@ -60,22 +60,25 @@ impl Constraint {
}
}
- pub fn new(operator: String, version: String) -> anyhow::Result<Self> {
- let op_int = Self::trans_op_str(&operator).ok_or_else(|| {
- anyhow::anyhow!(
+ pub fn new(operator: impl Into<String>, version: impl Into<String>) -> Self {
+ let operator: String = operator.into();
+ let op_int = Self::trans_op_str(&operator).unwrap_or_else(|| {
+ // PHP raises InvalidArgumentException; in the Rust port keep that as a panic
+ // because invalid operators are programmer errors caught during porting.
+ panic!(
"Invalid operator \"{}\" given, expected one of: {}",
operator,
Self::get_supported_operators().join(", ")
)
- })?;
+ });
- Ok(Self {
+ Self {
operator: op_int,
- version,
+ version: version.into(),
pretty_string: None,
lower_bound: Mutex::new(None),
upper_bound: Mutex::new(None),
- })
+ }
}
pub fn get_version(&self) -> &str {
diff --git a/crates/shirabe-semver/src/constraint/constraint_interface.rs b/crates/shirabe-semver/src/constraint/constraint_interface.rs
index f8879b3..09536e6 100644
--- a/crates/shirabe-semver/src/constraint/constraint_interface.rs
+++ b/crates/shirabe-semver/src/constraint/constraint_interface.rs
@@ -2,7 +2,7 @@
use crate::constraint::bound::Bound;
-pub trait ConstraintInterface {
+pub trait ConstraintInterface: std::fmt::Debug {
fn matches(&self, provider: &dyn ConstraintInterface) -> bool;
fn compile(&self, other_operator: i64) -> String;
diff --git a/crates/shirabe-semver/src/constraint/multi_constraint.rs b/crates/shirabe-semver/src/constraint/multi_constraint.rs
index 04e262b..5596cc4 100644
--- a/crates/shirabe-semver/src/constraint/multi_constraint.rs
+++ b/crates/shirabe-semver/src/constraint/multi_constraint.rs
@@ -2,7 +2,6 @@
use std::cell::RefCell;
-use anyhow::bail;
use crate::constraint::bound::Bound;
use crate::constraint::constraint_interface::ConstraintInterface;
@@ -26,26 +25,22 @@ impl std::fmt::Debug for MultiConstraint {
}
impl MultiConstraint {
- pub fn new(
- constraints: Vec<Box<dyn ConstraintInterface>>,
- conjunctive: bool,
- ) -> anyhow::Result<Self> {
- if constraints.len() < 2 {
- bail!(
- "Must provide at least two constraints for a MultiConstraint. Use \
+ pub fn new(constraints: Vec<Box<dyn ConstraintInterface>>, conjunctive: bool) -> Self {
+ assert!(
+ constraints.len() >= 2,
+ "Must provide at least two constraints for a MultiConstraint. Use \
the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use \
MultiConstraint::create() which optimizes and handles those cases automatically."
- );
- }
+ );
- Ok(Self {
+ Self {
constraints,
pretty_string: None,
string: RefCell::new(None),
conjunctive,
lower_bound: RefCell::new(None),
upper_bound: RefCell::new(None),
- })
+ }
}
pub fn get_constraints(&self) -> &[Box<dyn ConstraintInterface>] {
@@ -123,7 +118,7 @@ impl MultiConstraint {
return Ok(constraints.into_iter().next().unwrap());
}
- Ok(Box::new(MultiConstraint::new(constraints, conjunctive)?))
+ Ok(Box::new(MultiConstraint::new(constraints, conjunctive)))
}
// Returns the (possibly optimized) constraints and the effective conjunctive flag.
@@ -163,16 +158,13 @@ impl MultiConstraint {
&& right1.starts_with('<')
&& left1.get(2..) == right0.get(3..)
{
- Some(Box::new(
- MultiConstraint::new(
- vec![
- l_mc.constraints[0].clone_box(),
- r_mc.constraints[1].clone_box(),
- ],
- true,
- )
- .unwrap(),
- )
+ Some(Box::new(MultiConstraint::new(
+ vec![
+ l_mc.constraints[0].clone_box(),
+ r_mc.constraints[1].clone_box(),
+ ],
+ true,
+ ))
as Box<dyn ConstraintInterface>)
} else {
None
diff --git a/crates/shirabe-semver/src/interval.rs b/crates/shirabe-semver/src/interval.rs
index cf4f481..237ad0b 100644
--- a/crates/shirabe-semver/src/interval.rs
+++ b/crates/shirabe-semver/src/interval.rs
@@ -31,14 +31,13 @@ impl Interval {
pub fn from_zero() -> &'static Constraint {
static ZERO: OnceLock<Constraint> = OnceLock::new();
- ZERO.get_or_init(|| Constraint::new(">=".to_string(), "0.0.0.0-dev".to_string()).unwrap())
+ ZERO.get_or_init(|| Constraint::new(">=".to_string(), "0.0.0.0-dev".to_string()))
}
pub fn until_positive_infinity() -> &'static Constraint {
static POSITIVE_INFINITY: OnceLock<Constraint> = OnceLock::new();
- POSITIVE_INFINITY.get_or_init(|| {
- Constraint::new("<".to_string(), format!("{}.0.0.0", i64::MAX)).unwrap()
- })
+ POSITIVE_INFINITY
+ .get_or_init(|| Constraint::new("<".to_string(), format!("{}.0.0.0", i64::MAX)))
}
pub fn any() -> Self {
diff --git a/crates/shirabe-semver/src/intervals.rs b/crates/shirabe-semver/src/intervals.rs
index 898cab3..384cb4e 100644
--- a/crates/shirabe-semver/src/intervals.rs
+++ b/crates/shirabe-semver/src/intervals.rs
@@ -66,8 +66,7 @@ impl Intervals {
}
// Phase B: ConstraintInterface needs clone_box() to create owned copies from references.
- let multi =
- MultiConstraint::new(vec![candidate.clone_box(), constraint.clone_box()], true)?;
+ let multi = MultiConstraint::new(vec![candidate.clone_box(), constraint.clone_box()], true);
let intersection_intervals = Self::get(&multi)?;
let candidate_intervals = Self::get(candidate)?;
@@ -125,7 +124,7 @@ impl Intervals {
}
// Phase B: ConstraintInterface needs clone_box().
- let multi = MultiConstraint::new(vec![a.clone_box(), b.clone_box()], true)?;
+ let multi = MultiConstraint::new(vec![a.clone_box(), b.clone_box()], true);
let intersection_intervals = Self::generate_intervals(&multi, true)?;
Ok(!intersection_intervals.numeric.is_empty()
@@ -185,7 +184,7 @@ impl Intervals {
un_equal_constraints.push(Box::new(Constraint::new(
"!=".to_string(),
interval.get_end().get_version().to_string(),
- )?));
+ )));
i += 1;
continue;
}
@@ -202,7 +201,7 @@ impl Intervals {
// count is 1 if entire constraint is just one != expression
if un_equal_constraints.len() > 1 {
constraints
- .push(Box::new(MultiConstraint::new(un_equal_constraints, true)?));
+ .push(Box::new(MultiConstraint::new(un_equal_constraints, true)));
} else {
constraints.push(un_equal_constraints.into_iter().next().unwrap());
}
@@ -220,7 +219,7 @@ impl Intervals {
constraints.push(Box::new(Constraint::new(
"==".to_string(),
interval.get_start().get_version().to_string(),
- )?));
+ )));
i += 1;
continue;
}
@@ -238,7 +237,7 @@ impl Intervals {
Box::new(interval.get_end().clone()),
],
true,
- )?));
+ )));
}
i += 1;
@@ -260,12 +259,12 @@ impl Intervals {
dev_constraints.push(Box::new(Constraint::new(
"!=".to_string(),
branch_name.clone(),
- )?));
+ )));
} else {
dev_constraints.push(Box::new(Constraint::new(
"==".to_string(),
branch_name.clone(),
- )?));
+ )));
}
}
@@ -274,25 +273,25 @@ impl Intervals {
if intervals.branches.exclude {
if constraints.len() > 1 {
let merged: Vec<Box<dyn ConstraintInterface>> =
- std::iter::once(Box::new(MultiConstraint::new(constraints, false)?)
+ std::iter::once(Box::new(MultiConstraint::new(constraints, false))
as Box<dyn ConstraintInterface>)
.chain(dev_constraints)
.collect();
- return Ok(Box::new(MultiConstraint::new(merged, true)?));
+ return Ok(Box::new(MultiConstraint::new(merged, true)));
}
if constraints.len() == 1
&& constraints[0].__to_string() == Interval::from_zero().__to_string()
{
if dev_constraints.len() > 1 {
- return Ok(Box::new(MultiConstraint::new(dev_constraints, true)?));
+ return Ok(Box::new(MultiConstraint::new(dev_constraints, true)));
}
return Ok(dev_constraints.into_iter().next().unwrap());
}
let merged: Vec<Box<dyn ConstraintInterface>> =
constraints.into_iter().chain(dev_constraints).collect();
- return Ok(Box::new(MultiConstraint::new(merged, true)?));
+ return Ok(Box::new(MultiConstraint::new(merged, true)));
}
// otherwise devConstraints contains a list of == operators for branches which are
@@ -301,7 +300,7 @@ impl Intervals {
}
if constraints.len() > 1 {
- return Ok(Box::new(MultiConstraint::new(constraints, false)?));
+ return Ok(Box::new(MultiConstraint::new(constraints, false)));
}
if constraints.len() == 1 {
@@ -505,7 +504,7 @@ impl Intervals {
}
if start.is_none() && active_intervals >= activation_threshold {
- start = Some(Constraint::new(operator.clone(), version.clone())?);
+ start = Some(Constraint::new(operator.clone(), version.clone()));
} else if start.is_some() && active_intervals < activation_threshold {
let start_c = start.take().unwrap();
// filter out invalid intervals like > x - <= x, or >= x - < x
@@ -517,7 +516,7 @@ impl Intervals {
} else {
intervals.push(Interval::new(
start_c,
- Constraint::new(operator.clone(), version.clone())?,
+ Constraint::new(operator.clone(), version.clone()),
));
if stop_on_first_valid_interval {
@@ -590,10 +589,10 @@ impl Intervals {
numeric: vec![
Interval::new(
Interval::from_zero().clone(),
- Constraint::new("<".to_string(), constraint.get_version().to_string())?,
+ Constraint::new("<".to_string(), constraint.get_version().to_string()),
),
Interval::new(
- Constraint::new(">".to_string(), constraint.get_version().to_string())?,
+ Constraint::new(">".to_string(), constraint.get_version().to_string()),
Interval::until_positive_infinity().clone(),
),
],
@@ -604,8 +603,8 @@ impl Intervals {
// convert ==x to an interval of >=x - <=x
Ok(IntervalCollection {
numeric: vec![Interval::new(
- Constraint::new(">=".to_string(), constraint.get_version().to_string())?,
- Constraint::new("<=".to_string(), constraint.get_version().to_string())?,
+ Constraint::new(">=".to_string(), constraint.get_version().to_string()),
+ Constraint::new("<=".to_string(), constraint.get_version().to_string()),
)],
branches: Interval::no_dev(),
})
diff --git a/crates/shirabe-semver/src/semver.rs b/crates/shirabe-semver/src/semver.rs
index 0aff3d7..28e74a6 100644
--- a/crates/shirabe-semver/src/semver.rs
+++ b/crates/shirabe-semver/src/semver.rs
@@ -19,8 +19,7 @@ impl Semver {
pub fn satisfies(version: String, constraints: String) -> anyhow::Result<bool> {
let version_parser = Self::version_parser();
- let provider =
- Constraint::new("==".to_string(), version_parser.normalize(&version, None)?)?;
+ let provider = Constraint::new("==".to_string(), version_parser.normalize(&version, None)?);
let parsed_constraints = version_parser.parse_constraints(&constraints)?;
Ok(parsed_constraints.matches(&provider))
}
diff --git a/crates/shirabe-semver/src/version_parser.rs b/crates/shirabe-semver/src/version_parser.rs
index 2c3bcc9..359beec 100644
--- a/crates/shirabe-semver/src/version_parser.rs
+++ b/crates/shirabe-semver/src/version_parser.rs
@@ -328,7 +328,7 @@ impl VersionParser {
let constraint: Box<dyn ConstraintInterface> = if constraint_objects.len() == 1 {
constraint_objects.into_iter().next().unwrap()
} else {
- Box::new(MultiConstraint::new(constraint_objects, true)?)
+ Box::new(MultiConstraint::new(constraint_objects, true))
};
or_groups.push(constraint);
@@ -402,7 +402,7 @@ impl VersionParser {
return Ok(vec![Box::new(Constraint::new(
">=".to_string(),
"0.0.0.0-dev".to_string(),
- )?)]);
+ ))]);
}
return Ok(vec![Box::new(MatchAllConstraint {
@@ -461,7 +461,7 @@ impl VersionParser {
let low_version =
self.normalize(&format!("{}{}", &constraint[1..], stability_suffix), None)?;
- let lower_bound = Constraint::new(">=".to_string(), low_version)?;
+ let lower_bound = Constraint::new(">=".to_string(), low_version);
// For upper bound, we increment the position of one more significance,
// but highPosition = 0 would be illegal
@@ -471,7 +471,7 @@ impl VersionParser {
self.manipulate_version_string(&matches, high_position, 1, "0")
.unwrap_or_default()
);
- let upper_bound = Constraint::new("<".to_string(), high_version)?;
+ let upper_bound = Constraint::new("<".to_string(), high_version);
return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]);
}
@@ -508,7 +508,7 @@ impl VersionParser {
let low_version =
self.normalize(&format!("{}{}", &constraint[1..], stability_suffix), None)?;
- let lower_bound = Constraint::new(">=".to_string(), low_version)?;
+ let lower_bound = Constraint::new(">=".to_string(), low_version);
// For upper bound, we increment the position of one more significance,
// but highPosition = 0 would be illegal
@@ -517,7 +517,7 @@ impl VersionParser {
self.manipulate_version_string(&matches, position, 1, "0")
.unwrap_or_default()
);
- let upper_bound = Constraint::new("<".to_string(), high_version)?;
+ let upper_bound = Constraint::new("<".to_string(), high_version);
return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]);
}
@@ -557,12 +557,12 @@ impl VersionParser {
return Ok(vec![Box::new(Constraint::new(
"<".to_string(),
high_version,
- )?)]);
+ ))]);
}
return Ok(vec![
- Box::new(Constraint::new(">=".to_string(), low_version)?),
- Box::new(Constraint::new("<".to_string(), high_version)?),
+ Box::new(Constraint::new(">=".to_string(), low_version)),
+ Box::new(Constraint::new("<".to_string(), high_version)),
]);
}
@@ -596,7 +596,7 @@ impl VersionParser {
let lower_bound = Constraint::new(
">=".to_string(),
format!("{}{}", low_version, low_stability_suffix),
- )?;
+ );
// PHP's empty() on "0" returns true, but here we only check for truly empty/missing
let empty = |x: &Option<String>| -> bool { x.as_deref().is_none_or(|s| s.is_empty()) };
@@ -610,7 +610,7 @@ impl VersionParser {
{
let to_str = matches[10].clone().unwrap_or_default(); // matches['to']
let hv = self.normalize(&to_str, None)?;
- Constraint::new("<=".to_string(), hv)?
+ Constraint::new("<=".to_string(), hv)
} else {
// matches[11]=to major, matches[12]=to minor, matches[13]=to patch,
// matches[14]=to fourth
@@ -632,7 +632,7 @@ impl VersionParser {
self.manipulate_version_string(&high_match, position, 1, "0")
.unwrap_or_default()
);
- Constraint::new("<".to_string(), hv)?
+ Constraint::new("<".to_string(), hv)
};
return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]);
@@ -644,7 +644,7 @@ impl VersionParser {
let version_str = match_[2].clone().unwrap_or_default();
let op_str = match_[1].clone().unwrap_or_default();
- let version_result: anyhow::Result<String> = (match self.normalize(&version_str, None) {
+ let version_result: anyhow::Result<String> = match self.normalize(&version_str, None) {
Ok(v) => Ok(v),
Err(e) => {
// recover from an invalid constraint like foobar-dev which should be
@@ -661,7 +661,7 @@ impl VersionParser {
Err(e)
}
}
- });
+ };
if let Ok(mut version) = version_result {
let op = if op_str.is_empty() { "=" } else { &op_str };
@@ -691,7 +691,7 @@ impl VersionParser {
} else {
op_str
};
- return Ok(vec![Box::new(Constraint::new(final_op, version)?)]);
+ return Ok(vec![Box::new(Constraint::new(final_op, version))]);
}
}
diff --git a/crates/shirabe/src/advisory/audit_config.rs b/crates/shirabe/src/advisory/audit_config.rs
index f75d499..bdf3e8d 100644
--- a/crates/shirabe/src/advisory/audit_config.rs
+++ b/crates/shirabe/src/advisory/audit_config.rs
@@ -6,7 +6,7 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed};
use crate::advisory::auditor::Auditor;
use crate::config::Config;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct AuditConfig {
pub audit: bool,
pub audit_format: String,
@@ -123,7 +123,11 @@ impl AuditConfig {
Ok((for_audit, for_block))
}
- pub fn from_config(config: &Config, audit: bool, audit_format: &str) -> anyhow::Result<Self> {
+ pub fn from_config(
+ config: &mut Config,
+ audit: bool,
+ audit_format: &str,
+ ) -> anyhow::Result<Self> {
let audit_config_raw = config.get("audit");
let audit_config = audit_config_raw.as_array();
diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs
index e1b40c2..68bedb0 100644
--- a/crates/shirabe/src/advisory/auditor.rs
+++ b/crates/shirabe/src/advisory/auditor.rs
@@ -70,7 +70,7 @@ impl Auditor {
/// @throws InvalidArgumentException If no packages are passed in
pub fn audit(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
repo_set: &RepositorySet,
packages: Vec<Box<dyn PackageInterface>>,
format: &str,
@@ -170,17 +170,13 @@ impl Auditor {
),
);
- io.write(
- PhpMixed::String(JsonFile::encode(
- &PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
- shirabe_php_shim::JSON_UNESCAPED_SLASHES
- | shirabe_php_shim::JSON_PRETTY_PRINT
- | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
- JsonFile::INDENT_DEFAULT,
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write(&JsonFile::encode_with_indent(
+ &PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
+ shirabe_php_shim::JSON_UNESCAPED_SLASHES
+ | shirabe_php_shim::JSON_PRETTY_PRINT
+ | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
+ JsonFile::INDENT_DEFAULT,
+ ));
return Ok(audit_bitmask);
}
@@ -214,57 +210,31 @@ impl Auditor {
};
let pkg_plurality = if pkg_count == 1 { "" } else { "s" };
let punctuation = if format == "summary" { "." } else { ":" };
- io.write_error(
- PhpMixed::String(sprintf(
- &message,
- &[
- PhpMixed::Int(total_advisory_count),
- PhpMixed::String(plurality.to_string()),
- PhpMixed::Int(pkg_count),
- PhpMixed::String(pkg_plurality.to_string()),
- PhpMixed::String(punctuation.to_string()),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ &message,
+ &[
+ PhpMixed::Int(total_advisory_count),
+ PhpMixed::String(plurality.to_string()),
+ PhpMixed::Int(pkg_count),
+ PhpMixed::String(pkg_plurality.to_string()),
+ PhpMixed::String(punctuation.to_string()),
+ ],
+ ));
self.output_advisories(io, advisories_to_output, format)?;
}
}
if format == Self::FORMAT_SUMMARY {
- io.write_error(
- PhpMixed::String(
- "Run \"composer audit\" for a full list of advisories.".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("Run \"composer audit\" for a full list of advisories.");
}
} else {
- io.write_error(
- PhpMixed::String(
- "<info>No security vulnerability advisories found.</info>".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("<info>No security vulnerability advisories found.</info>");
}
if !unreachable_repos.is_empty() {
- io.write_error(
- PhpMixed::String(
- "<warning>The following repositories were unreachable:</warning>".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error("<warning>The following repositories were unreachable:</warning>");
for repo in &unreachable_repos {
- io.write_error(
- PhpMixed::String(format!(" - {}", repo)),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&format!(" - {}", repo));
}
}
@@ -458,7 +428,7 @@ impl Auditor {
/// @param self::FORMAT_* $format The format that will be used to output audit results.
fn output_advisories(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
advisories: &IndexMap<String, Vec<PartialSecurityAdvisory>>,
format: &str,
) -> Result<()> {
@@ -534,8 +504,8 @@ impl Auditor {
}
let _ = row;
io.get_table()
- .set_horizontal()
- .set_headers(headers)
+ .set_horizontal(true)
+ .set_headers(headers.into_iter().map(|h| h.into()).collect())
.add_row(ConsoleIO::sanitize(PhpMixed::Null, false))
.set_column_width(1, 80)
.set_column_max_width(1, 80)
@@ -547,7 +517,7 @@ impl Auditor {
/// @param array<string, array<SecurityAdvisory>> $advisories
fn output_advisories_plain(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
advisories: &IndexMap<String, Vec<PartialSecurityAdvisory>>,
) {
let mut error: Vec<String> = vec![];
@@ -596,41 +566,30 @@ impl Auditor {
first_advisory = false;
}
}
- io.write_error(
- PhpMixed::List(
- error
- .into_iter()
- .map(|s| Box::new(PhpMixed::String(s)))
- .collect(),
- ),
- true,
- io_interface::NORMAL,
- );
+ for line in &error {
+ io.write_error(line);
+ }
}
/// @param array<CompletePackageInterface> $packages
/// @param self::FORMAT_PLAIN|self::FORMAT_TABLE $format
fn output_abandoned_packages(
&self,
- io: &dyn IOInterface,
+ io: &mut dyn IOInterface,
packages: &[Box<dyn CompletePackageInterface>],
format: &str,
) -> Result<()> {
- io.write_error(
- PhpMixed::String(sprintf(
- "<error>Found %d abandoned package%s:</error>",
- &[
- PhpMixed::Int(packages.len() as i64),
- PhpMixed::String(if packages.len() > 1 {
- "s".to_string()
- } else {
- String::new()
- }),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ "<error>Found %d abandoned package%s:</error>",
+ &[
+ PhpMixed::Int(packages.len() as i64),
+ PhpMixed::String(if packages.len() > 1 {
+ "s".to_string()
+ } else {
+ String::new()
+ }),
+ ],
+ ));
if format == Self::FORMAT_PLAIN {
for pkg in packages {
@@ -639,17 +598,13 @@ impl Auditor {
} else {
"No replacement was suggested".to_string()
};
- io.write_error(
- PhpMixed::String(sprintf(
- "%s is abandoned. %s.",
- &[
- PhpMixed::String(self.get_package_name_with_link_for_complete(pkg)),
- PhpMixed::String(replacement),
- ],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error(&sprintf(
+ "%s is abandoned. %s.",
+ &[
+ PhpMixed::String(self.get_package_name_with_link_for_complete(pkg)),
+ PhpMixed::String(replacement),
+ ],
+ ));
}
return Ok(());
@@ -672,8 +627,8 @@ impl Auditor {
.unwrap()
.get_table()
.set_headers(vec![
- "Abandoned Package".to_string(),
- "Suggested Replacement".to_string(),
+ "Abandoned Package".to_string().into(),
+ "Suggested Replacement".to_string().into(),
])
.set_column_width(1, 80)
.set_column_max_width(1, 80);
diff --git a/crates/shirabe/src/advisory/ignored_security_advisory.rs b/crates/shirabe/src/advisory/ignored_security_advisory.rs
index 7ed3a4c..03e7d78 100644
--- a/crates/shirabe/src/advisory/ignored_security_advisory.rs
+++ b/crates/shirabe/src/advisory/ignored_security_advisory.rs
@@ -6,9 +6,12 @@ use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-#[derive(Debug)]
+#[derive(Debug, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
pub struct IgnoredSecurityAdvisory {
+ #[serde(flatten)]
inner: SecurityAdvisory,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub ignore_reason: Option<String>,
}
@@ -41,14 +44,4 @@ impl IgnoredSecurityAdvisory {
ignore_reason,
}
}
-
- pub fn json_serialize(&self) -> PhpMixed {
- let mut data = self.inner.json_serialize();
- if self.ignore_reason.is_none() {
- if let PhpMixed::Array(ref mut map) = data {
- map.remove("ignoreReason");
- }
- }
- data
- }
}
diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs
index e7aa96e..a47ec02 100644
--- a/crates/shirabe/src/advisory/partial_security_advisory.rs
+++ b/crates/shirabe/src/advisory/partial_security_advisory.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Advisory/PartialSecurityAdvisory.php
use crate::advisory::security_advisory::SecurityAdvisory;
+use crate::repository::advisory_provider_interface::PartialOrSecurityAdvisory;
use anyhow::Result;
use chrono::{DateTime, TimeZone, Utc};
use indexmap::IndexMap;
@@ -31,7 +32,7 @@ impl PartialSecurityAdvisory {
package_name: &str,
data: &IndexMap<String, PhpMixed>,
parser: &VersionParser,
- ) -> Result<Box<dyn std::any::Any>> {
+ ) -> Result<PartialOrSecurityAdvisory> {
let affected_versions_str = data["affectedVersions"].as_string().unwrap_or("");
let constraint: Box<dyn ConstraintInterface> =
@@ -40,9 +41,12 @@ impl PartialSecurityAdvisory {
Err(_) => {
let affected_version =
Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str);
- match parser.parse_constraints(&affected_version) {
+ match parser.parse_constraints(affected_version.as_deref().unwrap_or("")) {
Ok(c) => c,
- Err(_) => Box::new(Constraint::new("==", "0.0.0-invalid-version")),
+ Err(_) => Box::new(Constraint::new(
+ "==".to_string(),
+ "0.0.0-invalid-version".to_string(),
+ )),
}
}
};
@@ -63,7 +67,8 @@ impl PartialSecurityAdvisory {
data["advisoryId"].as_string().unwrap_or("").to_string(),
constraint,
data["title"].as_string().unwrap_or("").to_string(),
- data["sources"].clone(),
+ // TODO(phase-b): parse PhpMixed sources array into Vec<IndexMap<String, String>>
+ todo!(),
reported_at,
data.get("cve")
.and_then(|v| v.as_string())
@@ -75,10 +80,10 @@ impl PartialSecurityAdvisory {
.and_then(|v| v.as_string())
.map(|s| s.to_string()),
);
- return Ok(Box::new(advisory));
+ return Ok(PartialOrSecurityAdvisory::Full(advisory));
}
- Ok(Box::new(Self {
+ Ok(PartialOrSecurityAdvisory::Partial(Self {
advisory_id: data["advisoryId"].as_string().unwrap_or("").to_string(),
package_name: package_name.to_string(),
affected_versions: constraint,
diff --git a/crates/shirabe/src/advisory/security_advisory.rs b/crates/shirabe/src/advisory/security_advisory.rs
index 049169d..1b1ff64 100644
--- a/crates/shirabe/src/advisory/security_advisory.rs
+++ b/crates/shirabe/src/advisory/security_advisory.rs
@@ -44,12 +44,15 @@ impl SecurityAdvisory {
}
}
+ pub fn affected_versions(&self) -> &dyn ConstraintInterface {
+ &*self.inner.affected_versions
+ }
+
pub fn to_ignored_advisory(&self, ignore_reason: Option<String>) -> IgnoredSecurityAdvisory {
IgnoredSecurityAdvisory::new(
self.inner.package_name.clone(),
self.inner.advisory_id.clone(),
- // TODO: Phase B - handle shared ownership of affected_versions
- self.inner.affected_versions.clone(),
+ self.inner.affected_versions.clone_box(),
self.title.clone(),
self.sources.clone(),
self.reported_at,
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index df8b16b..92f2322 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -4,15 +4,15 @@ use indexmap::IndexMap;
use shirabe_class_map_generator::class_map::ClassMap;
use shirabe_class_map_generator::class_map_generator::ClassMapGenerator;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter;
use shirabe_php_shim::{
E_USER_DEPRECATED, InvalidArgumentException, PhpMixed, RuntimeException, array_filter,
array_keys, array_map, array_merge, array_merge_recursive, array_reverse, array_shift,
- array_slice, array_unique, bin2hex, explode, file_exists, file_get_contents, hash, implode,
- in_array, is_array, krsort, ksort, ltrim, preg_quote, random_bytes, realpath, sprintf,
- str_contains, str_replace, str_starts_with, strlen, strpos, strtr, substr, substr_count,
- trigger_error, trim, unlink, var_export,
+ array_slice, array_slice_strs, array_unique, bin2hex, explode, file_exists, file_get_contents,
+ hash, implode, in_array, is_array, krsort, ksort, ltrim, preg_quote, random_bytes, realpath,
+ sprintf, str_contains, str_replace, str_starts_with, strlen, strpos, strtr, substr,
+ substr_count, trigger_error, trim, unlink, var_export,
};
use shirabe_semver::constraint::bound::Bound;
@@ -104,9 +104,9 @@ impl AutoloadGenerator {
E_USER_DEPRECATED,
);
- self.set_platform_requirement_filter(PlatformRequirementFilterFactory::from_bool_or_list(
- ignore_platform_reqs,
- ));
+ self.set_platform_requirement_filter(
+ PlatformRequirementFilterFactory::from_bool_or_list(ignore_platform_reqs).unwrap(),
+ );
}
pub fn set_platform_requirement_filter(
@@ -146,7 +146,7 @@ impl AutoloadGenerator {
),
None,
None,
- );
+ )?;
if installed_json.exists() {
let installed_json_data = installed_json.read()?;
if let Some(arr) = installed_json_data.as_array() {
@@ -172,17 +172,17 @@ impl AutoloadGenerator {
let mut additional_args: IndexMap<String, PhpMixed> = IndexMap::new();
additional_args.insert("optimize".to_string(), PhpMixed::Bool(scan_psr_packages));
- self.event_dispatcher.dispatch_script_with_args(
+ self.event_dispatcher.dispatch_script(
ScriptEvents::PRE_AUTOLOAD_DUMP,
self.dev_mode.unwrap_or(false),
vec![],
additional_args,
- );
+ )?;
}
let mut class_map_generator =
ClassMapGenerator::new(vec!["php".to_string(), "inc".to_string(), "hh".to_string()]);
- class_map_generator.avoid_duplicate_scans();
+ class_map_generator.avoid_duplicate_scans(None);
let filesystem = Filesystem::new(None);
filesystem.ensure_directory_exists(config.get("vendor-dir").as_string().unwrap_or(""))?;
@@ -190,7 +190,8 @@ impl AutoloadGenerator {
// Fixes failing Windows realpath() implementation.
// See https://bugs.php.net/bug.php?id=72738
let base_path = filesystem.normalize_path(
- &realpath(&realpath(&Platform::get_cwd()).unwrap_or_default()).unwrap_or_default(),
+ &realpath(&realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default())
+ .unwrap_or_default(),
);
let vendor_path = filesystem.normalize_path(
&realpath(
@@ -212,16 +213,18 @@ impl AutoloadGenerator {
&vendor_path,
true,
false,
+ false,
);
let vendor_path_to_target_dir_code = filesystem.find_shortest_path_code(
&vendor_path,
&realpath(&target_dir).unwrap_or_default(),
true,
false,
+ false,
);
let app_base_dir_code =
- filesystem.find_shortest_path_code(&vendor_path, &base_path, true, false);
+ filesystem.find_shortest_path_code(&vendor_path, &base_path, true, false, false);
let app_base_dir_code = str_replace("__DIR__", "$vendorDir", &app_base_dir_code);
let mut namespaces_file = format!(
@@ -257,7 +260,14 @@ impl AutoloadGenerator {
PhpMixed::Bool(true)
}
};
- let autoloads = self.parse_autoloads(&package_map, root_package, filtered_dev_packages);
+ let autoloads = self.parse_autoloads(
+ package_map
+ .iter()
+ .map(|(p, s)| (p.clone_package_box(), s.clone()))
+ .collect(),
+ root_package,
+ filtered_dev_packages,
+ );
// Process the 'psr-0' base directories.
let psr0_map = autoloads
@@ -309,7 +319,10 @@ impl AutoloadGenerator {
let mut target_dir_loader: Option<String> = None;
let main_autoload = root_package.get_autoload();
if root_package.get_target_dir().is_some()
- && main_autoload.get("psr-0").map_or(false, |v| !v.is_empty())
+ && main_autoload
+ .get("psr-0")
+ .and_then(|v| v.as_array())
+ .map_or(false, |a| !a.is_empty())
{
let levels = substr_count(
&filesystem.normalize_path(&root_package.get_target_dir().unwrap_or_default()),
@@ -328,7 +341,7 @@ impl AutoloadGenerator {
),
);
let base_dir_from_target_dir_code =
- filesystem.find_shortest_path_code(&target_dir, &base_path, true, false);
+ filesystem.find_shortest_path_code(&target_dir, &base_path, true, false, false);
target_dir_loader = Some(format!(
"\n public static function autoload($class)\n {{\n $dir = {} . '/';\n $prefixes = array({});\n foreach ($prefixes as $prefix) {{\n if (0 !== strpos($class, $prefix)) {{\n continue;\n }}\n $path = $dir . implode('/', array_slice(explode('\\\\', $class), {})).'.php';\n if (!$path = stream_resolve_include_path($path)) {{\n return false;\n }}\n require $path;\n\n return true;\n }}\n }}\n",
@@ -357,11 +370,12 @@ impl AutoloadGenerator {
for dir in &classmap_list {
let dir_str = dir.as_string().unwrap_or("");
class_map_generator.scan_paths(
- dir_str,
+ PhpMixed::String(dir_str.to_string()),
self.build_exclusion_regex(dir_str, excluded.clone()),
"classmap",
- "",
- );
+ None,
+ vec![],
+ )?;
}
if scan_psr_packages {
@@ -386,7 +400,7 @@ impl AutoloadGenerator {
}
}
- krsort(&mut namespaces_to_scan);
+ namespaces_to_scan.sort_by(|k1, _, k2, _| k2.cmp(k1));
for (namespace, groups) in &namespaces_to_scan {
for group in groups {
@@ -415,48 +429,42 @@ impl AutoloadGenerator {
// if the vendor dir is contained within a psr-0/psr-4 dir being scanned we exclude it
let exclusion_regex =
if str_contains(&vendor_path, &format!("{}/", dir_str)) {
- self.build_exclusion_regex(
- &dir_str,
- array_merge(
- excluded.clone(),
- vec![format!("{}/", vendor_path)],
- ),
- )
+ let mut combined = excluded.clone();
+ combined.push(format!("{}/", vendor_path));
+ self.build_exclusion_regex(&dir_str, combined)
} else {
self.build_exclusion_regex(&dir_str, excluded.clone())
};
class_map_generator.scan_paths(
- &dir_str,
+ PhpMixed::String(dir_str.clone()),
exclusion_regex,
&group_type,
- namespace,
- );
+ Some(namespace.clone()),
+ vec![],
+ )?;
}
}
}
}
- let class_map = class_map_generator.get_class_map();
- let ambiguous_classes = if strict_ambiguous {
- class_map.get_ambiguous_classes(false)
- } else {
- class_map.get_ambiguous_classes(true)
- };
+ let mut class_map = class_map_generator.take_class_map();
+ // TODO(phase-b): strict_ambiguous should filter vendor path for non-strict mode
+ let ambiguous_classes = class_map.get_ambiguous_classes(None)?;
for (class_name, ambiguous_paths) in &ambiguous_classes {
if ambiguous_paths.len() > 1 {
self.io.write_error(&format!(
"<warning>Warning: Ambiguous class resolution, \"{}\" was found {}x: in \"{}\" and \"{}\", the first will be used.</warning>",
class_name,
ambiguous_paths.len() + 1,
- class_map.get_class_path(class_name),
+ class_map.get_class_path(class_name)?,
implode("\", \"", ambiguous_paths)
));
} else {
self.io.write_error(&format!(
"<warning>Warning: Ambiguous class resolution, \"{}\" was found in both \"{}\" and \"{}\", the first will be used.</warning>",
class_name,
- class_map.get_class_path(class_name),
+ class_map.get_class_path(class_name)?,
implode("\", \"", ambiguous_paths)
));
}
@@ -512,22 +520,22 @@ impl AutoloadGenerator {
{
let content =
file_get_contents(&format!("{}/autoload.php", vendor_path)).unwrap_or_default();
- let mut matches: Vec<String> = vec![];
- if Preg::is_match(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match3(
"{ComposerAutoloaderInit([^:\\s]+)::}",
&content,
Some(&mut matches),
)
.unwrap_or(false)
{
- suffix = matches.get(1).cloned();
+ suffix = matches.get(&CaptureKey::ByIndex(1)).cloned();
}
}
if suffix.is_none() {
suffix = Some(if let Some(l) = locker {
if l.is_locked() {
- l.get_lock_data()
+ l.get_lock_data()?
.get("content-hash")
.and_then(|v| v.as_string())
.unwrap_or("")
@@ -657,12 +665,12 @@ impl AutoloadGenerator {
if self.run_scripts {
let mut additional_args: IndexMap<String, PhpMixed> = IndexMap::new();
additional_args.insert("optimize".to_string(), PhpMixed::Bool(scan_psr_packages));
- self.event_dispatcher.dispatch_script_with_args(
+ self.event_dispatcher.dispatch_script(
ScriptEvents::POST_AUTOLOAD_DUMP,
self.dev_mode.unwrap_or(false),
vec![],
additional_args,
- );
+ )?;
}
Ok(class_map)
@@ -687,7 +695,7 @@ impl AutoloadGenerator {
} else {
format!(
"{}/{}",
- realpath(&Platform::get_cwd()).unwrap_or_default(),
+ realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default(),
dir
)
};
@@ -702,7 +710,8 @@ impl AutoloadGenerator {
"{^(([^.+*?\\[^\\]$(){}=!<>|:\\\\#-]+|\\\\[.+*?\\[^\\]$(){}=!<>|:#-])*).*}",
"$1",
pattern,
- );
+ )
+ .unwrap_or_default();
// if the pattern is not a subset or superset of $dir, it is unrelated and we skip it
let unrelated = (!str_starts_with(&pattern_processed, &dir_match)
&& !str_starts_with(&dir_match, &pattern_processed))
@@ -750,7 +759,10 @@ impl AutoloadGenerator {
/// Throws InvalidArgumentException if the package has illegal settings.
pub(crate) fn validate_package(&self, package: &dyn PackageInterface) -> anyhow::Result<()> {
let autoload = package.get_autoload();
- if autoload.get("psr-4").map_or(false, |v| !v.is_empty())
+ if autoload
+ .get("psr-4")
+ .and_then(|v| v.as_array())
+ .map_or(false, |a| !a.is_empty())
&& package.get_target_dir().is_some()
{
let name = package.get_name();
@@ -778,11 +790,11 @@ 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<(Box<dyn PackageInterface>, Option<String>)>,
root_package: &dyn RootPackageInterface,
filtered_dev_packages: PhpMixed,
) -> IndexMap<String, PhpMixed> {
- let mut package_map = package_map.clone();
+ let mut package_map = package_map;
let root_package_map = array_shift(&mut package_map).unwrap();
let package_map = if is_array(&filtered_dev_packages) {
let dev_list = filtered_dev_packages
@@ -793,12 +805,10 @@ impl AutoloadGenerator {
.collect::<Vec<_>>()
})
.unwrap_or_default();
- array_filter(
- package_map,
- |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool {
- !in_array(item.0.get_name(), &dev_list, true)
- },
- )
+ package_map
+ .into_iter()
+ .filter(|item| !dev_list.contains(&item.0.get_name().to_string()))
+ .collect()
} else if filtered_dev_packages.as_bool() == Some(true) {
self.filter_package_map(package_map, root_package)
} else {
@@ -806,13 +816,11 @@ impl AutoloadGenerator {
};
let mut sorted_package_map = self.sort_package_map(package_map);
sorted_package_map.push(root_package_map);
- let reverse_sorted_map = array_reverse(sorted_package_map.clone());
- // reverse-sorted means root first, then dependents, then their dependents, etc.
- // which makes sense to allow root to override classmap or psr-0/4 entries with higher precedence rules
- let mut psr0 = self.parse_autoloads_type(&reverse_sorted_map, "psr-0", root_package);
- let mut psr4 = self.parse_autoloads_type(&reverse_sorted_map, "psr-4", root_package);
- let classmap = self.parse_autoloads_type(&reverse_sorted_map, "classmap", root_package);
+ // TODO(phase-b): psr-0/4/classmap should use reverse_sorted_map (root first) for correct precedence
+ let mut psr0 = self.parse_autoloads_type(&sorted_package_map, "psr-0", root_package);
+ let mut psr4 = self.parse_autoloads_type(&sorted_package_map, "psr-4", root_package);
+ let classmap = self.parse_autoloads_type(&sorted_package_map, "classmap", root_package);
// sorted (i.e. dependents first) for files to ensure that dependencies are loaded/available once a file is included
let files = self.parse_autoloads_type(&sorted_package_map, "files", root_package);
@@ -820,8 +828,8 @@ impl AutoloadGenerator {
let exclude =
self.parse_autoloads_type(&sorted_package_map, "exclude-from-classmap", root_package);
- krsort(&mut psr0);
- krsort(&mut psr4);
+ psr0.sort_by(|k1, _, k2, _| k2.cmp(k1));
+ psr4.sort_by(|k1, _, k2, _| k2.cmp(k1));
let mut result: IndexMap<String, PhpMixed> = IndexMap::new();
result.insert("psr-0".to_string(), PhpMixed::Array(psr0));
@@ -845,13 +853,31 @@ impl AutoloadGenerator {
if let Some(psr0) = autoloads.get("psr-0").and_then(|v| v.as_array()) {
for (namespace, path) in psr0 {
- loader.add(namespace.clone(), (**path).clone());
+ let paths = path
+ .as_list()
+ .map(|l| {
+ l.iter()
+ .filter_map(|v| v.as_string().map(|s| s.to_string()))
+ .collect()
+ })
+ .or_else(|| path.as_string().map(|s| vec![s.to_string()]))
+ .unwrap_or_default();
+ loader.add(namespace, paths, false);
}
}
if let Some(psr4) = autoloads.get("psr-4").and_then(|v| v.as_array()) {
for (namespace, path) in psr4 {
- loader.add_psr4(namespace.clone(), (**path).clone());
+ let paths = path
+ .as_list()
+ .map(|l| {
+ l.iter()
+ .filter_map(|v| v.as_string().map(|s| s.to_string()))
+ .collect()
+ })
+ .or_else(|| path.as_string().map(|s| vec![s.to_string()]))
+ .unwrap_or_default();
+ loader.add_psr4(namespace, paths, false);
}
}
@@ -874,16 +900,17 @@ impl AutoloadGenerator {
"inc".to_string(),
"hh".to_string(),
]);
- class_map_generator.avoid_duplicate_scans();
+ class_map_generator.avoid_duplicate_scans(None);
for dir in classmap {
let dir_str = dir.as_string().unwrap_or("");
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
class_map_generator.scan_paths(
- dir_str,
+ PhpMixed::String(dir_str.to_string()),
self.build_exclusion_regex(dir_str, excluded.clone()),
"classmap",
- "",
+ None,
+ vec![],
);
}));
if let Err(_e) = res {
@@ -892,7 +919,7 @@ impl AutoloadGenerator {
}
}
- loader.add_class_map(class_map_generator.get_class_map().get_map());
+ loader.add_class_map(class_map_generator.get_class_map().get_map().clone());
}
loader
@@ -922,13 +949,12 @@ impl AutoloadGenerator {
if let Some(target_dir) = package.get_target_dir() {
if !target_dir.is_empty() {
let suffix_to_remove = format!("/{}", target_dir);
- install_path =
- substr(&install_path, 0, Some(-(suffix_to_remove.len() as isize)));
+ install_path = substr(&install_path, 0, Some(-(suffix_to_remove.len() as i64)));
}
}
for include_path in package.get_include_paths() {
- let include_path = trim(&include_path, "/");
+ let include_path = trim(&include_path, Some("/"));
include_paths.push(if install_path.is_empty() {
include_path
} else {
@@ -974,7 +1000,8 @@ impl AutoloadGenerator {
)
})
.collect();
- let unique_files: Vec<String> = array_unique(files.values().cloned().collect());
+ let all_values: Vec<String> = files.values().cloned().collect();
+ let unique_files: Vec<String> = array_unique(&all_values);
if unique_files.len() < files.len() {
self.io.write_error("<warning>The following \"files\" autoload rules are included multiple times, this may cause issues and should be resolved:</warning>");
// duplicates: array_diff_assoc(files, unique_files)
@@ -985,7 +1012,7 @@ impl AutoloadGenerator {
duplicates.push(v.clone());
}
}
- for duplicate_file in array_unique(duplicates) {
+ for duplicate_file in array_unique(&duplicates) {
self.io
.write_error(&format!("<warning> - {}</warning>", duplicate_file));
}
@@ -1031,18 +1058,18 @@ impl AutoloadGenerator {
let mut base_dir = String::new();
if strpos(&format!("{}/", path), &format!("{}/", vendor_path)) == Some(0) {
- path = substr(&path, vendor_path.len() as isize, None);
+ path = substr(&path, vendor_path.len() as i64, None);
base_dir = "$vendorDir . ".to_string();
} else {
- path =
- filesystem.normalize_path(&filesystem.find_shortest_path(base_path, &path, true));
+ path = filesystem
+ .normalize_path(&filesystem.find_shortest_path(base_path, &path, true, false));
if !filesystem.is_absolute_path(&path) {
base_dir = "$baseDir . ".to_string();
path = format!("/{}", path);
}
}
- if Preg::is_match("{\\.phar([\\\\/]|$)}", &path, None).unwrap_or(false) {
+ if Preg::is_match("{\\.phar([\\\\/]|$)}", &path).unwrap_or(false) {
base_dir = format!("'phar://' . {}", base_dir);
}
@@ -1070,14 +1097,16 @@ impl AutoloadGenerator {
links.insert(k, v);
}
for (_k, link) in &links {
- let mut matches: Vec<String> = vec![];
- if Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match3("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
.unwrap_or(false)
{
- extension_providers
- .entry(matches[1].clone())
- .or_insert_with(Vec::new)
- .push(link.get_constraint());
+ if let Some(ext) = matches.get(&CaptureKey::ByIndex(1)).cloned() {
+ extension_providers
+ .entry(ext)
+ .or_insert_with(Vec::new)
+ .push(link.get_constraint().clone_box());
+ }
}
}
}
@@ -1085,7 +1114,7 @@ impl AutoloadGenerator {
'outer: for item in package_map {
let package = &item.0;
// skip dev dependencies platform requirements as platform-check really should only be a production safeguard
- if in_array(package.get_name(), dev_package_names, true) {
+ if dev_package_names.contains(&package.get_name().to_string()) {
continue;
}
@@ -1097,15 +1126,12 @@ impl AutoloadGenerator {
continue;
}
- if in_array(
- link.get_target(),
- &vec!["php".to_string(), "php-64bit".to_string()],
- true,
- ) {
+ if ["php", "php-64bit"].contains(&link.get_target()) {
let constraint = link.get_constraint();
if constraint
.get_lower_bound()
.compare_to(&lowest_php_version, ">")
+ .unwrap_or(false)
{
lowest_php_version = constraint.get_lower_bound();
}
@@ -1115,13 +1141,17 @@ impl AutoloadGenerator {
required_php_64bit = true;
}
- let mut matches: Vec<String> = vec![];
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if check_platform.as_bool() == Some(true)
- && Preg::is_match("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
+ && Preg::match3("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
.unwrap_or(false)
{
+ let ext_key = matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
// skip extension checks if they have a valid provider/replacer
- if let Some(provided_list) = extension_providers.get(&matches[1]) {
+ if let Some(provided_list) = extension_providers.get(&ext_key) {
for provided in provided_list {
if provided.matches(&*link.get_constraint()) {
continue 'outer;
@@ -1129,10 +1159,10 @@ impl AutoloadGenerator {
}
}
- let ext_name = if matches[1] == "zend-opcache" {
+ let ext_name = if ext_key == "zend-opcache" {
"zend opcache".to_string()
} else {
- matches[1].clone()
+ ext_key.clone()
};
let extension = var_export(&PhpMixed::String(ext_name.clone()), true);
@@ -1171,7 +1201,7 @@ impl AutoloadGenerator {
let version = str_replace("-", ".", bound.get_version());
let chunks: Vec<i64> = explode(".", &version)
.into_iter()
- .map(|s| shirabe_php_shim::intval(&s))
+ .map(|s| shirabe_php_shim::intval(&PhpMixed::String(s)))
.collect();
chunks[0] * 10000 + chunks[1] * 100 + chunks[2]
@@ -1188,7 +1218,7 @@ impl AutoloadGenerator {
let version = str_replace("-", ".", bound.get_version());
let chunks = explode(".", &version);
- let chunks = array_slice(&chunks, 0, Some(3), false);
+ let chunks = array_slice_strs(&chunks, 0, Some(3));
PhpMixed::String(implode(".", &chunks))
};
@@ -1384,14 +1414,30 @@ impl AutoloadGenerator {
let map = shirabe_php_shim::php_require(&format!("{}/autoload_namespaces.php", target_dir));
if let Some(map_arr) = map.as_array() {
for (namespace, path) in map_arr {
- loader.set(namespace.clone(), (**path).clone());
+ let paths: Vec<String> = if let PhpMixed::List(items) = (**path).clone() {
+ items
+ .iter()
+ .map(|i| i.as_string().unwrap_or("").to_string())
+ .collect()
+ } else {
+ vec![]
+ };
+ loader.set(&namespace, paths);
}
}
let map = shirabe_php_shim::php_require(&format!("{}/autoload_psr4.php", target_dir));
if let Some(map_arr) = map.as_array() {
for (namespace, path) in map_arr {
- loader.set_psr4(namespace.clone(), (**path).clone());
+ let paths: Vec<String> = if let PhpMixed::List(items) = (**path).clone() {
+ items
+ .iter()
+ .map(|i| i.as_string().unwrap_or("").to_string())
+ .collect()
+ } else {
+ vec![]
+ };
+ loader.set_psr4(&namespace, paths).unwrap_or(());
}
}
@@ -1415,7 +1461,8 @@ impl AutoloadGenerator {
&realpath(target_dir).unwrap_or_default(),
vendor_path,
true,
- true
+ true,
+ false,
)
);
let vendor_phar_path_code = format!(
@@ -1424,7 +1471,8 @@ impl AutoloadGenerator {
&realpath(target_dir).unwrap_or_default(),
vendor_path,
true,
- true
+ true,
+ false,
)
);
let app_base_dir_code = format!(
@@ -1433,7 +1481,8 @@ impl AutoloadGenerator {
&realpath(target_dir).unwrap_or_default(),
base_path,
true,
- true
+ true,
+ false,
)
);
let app_base_dir_phar_code = format!(
@@ -1442,7 +1491,8 @@ impl AutoloadGenerator {
&realpath(target_dir).unwrap_or_default(),
base_path,
true,
- true
+ true,
+ false,
)
);
@@ -1451,7 +1501,10 @@ impl AutoloadGenerator {
" => {}",
substr(
&var_export(
- &PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(vendor_path, "\\/"))),
+ &PhpMixed::String(format!(
+ "{}/",
+ shirabe_php_shim::rtrim(vendor_path, Some("\\/"))
+ )),
true
),
0,
@@ -1464,7 +1517,7 @@ impl AutoloadGenerator {
&var_export(
&PhpMixed::String(format!(
"{}/",
- shirabe_php_shim::rtrim(&format!("phar://{}", vendor_path), "\\/")
+ shirabe_php_shim::rtrim(&format!("phar://{}", vendor_path), Some("\\/"))
)),
true
),
@@ -1476,7 +1529,10 @@ impl AutoloadGenerator {
" => {}",
substr(
&var_export(
- &PhpMixed::String(format!("{}/", shirabe_php_shim::rtrim(base_path, "\\/"))),
+ &PhpMixed::String(format!(
+ "{}/",
+ shirabe_php_shim::rtrim(base_path, Some("\\/"))
+ )),
true
),
0,
@@ -1489,7 +1545,7 @@ impl AutoloadGenerator {
&var_export(
&PhpMixed::String(format!(
"{}/",
- shirabe_php_shim::rtrim(&format!("phar://{}", base_path), "\\/")
+ shirabe_php_shim::rtrim(&format!("phar://{}", base_path), Some("\\/"))
)),
true
),
@@ -1517,11 +1573,11 @@ impl AutoloadGenerator {
{
continue;
}
- maps.insert(substr(&prop, prefix_len as isize, None), value);
+ maps.insert(substr(&prop, prefix_len as i64, None), value);
}
for (prop, value) in &maps {
- let value = strtr(&var_export(value, true), &{
+ let value = shirabe_php_shim::strtr_array(&var_export(value, true), &{
let mut m: IndexMap<String, String> = IndexMap::new();
m.insert(absolute_vendor_path_code.clone(), vendor_path_code.clone());
m.insert(
@@ -1538,8 +1594,11 @@ impl AutoloadGenerator {
);
m
});
- let value = shirabe_php_shim::ltrim(&Preg::replace("/^ */m", " $0$0", &value), None);
- let value = Preg::replace("/ +$/m", "", &value);
+ let value = shirabe_php_shim::ltrim(
+ &Preg::replace("/^ */m", " $0$0", &value).unwrap_or_default(),
+ None,
+ );
+ let value = Preg::replace("/ +$/m", "", &value).unwrap_or_default();
file.push_str(&sprintf(
" public static $%s = %s;\n\n",
@@ -1581,7 +1640,8 @@ impl AutoloadGenerator {
// PHP comparison: $package === $rootPackage (object identity). We compare by name as best-effort.
let is_root = package.get_name() == root_package.get_name();
if self.dev_mode.unwrap_or(false) && is_root {
- autoload = array_merge_recursive(autoload, root_package.get_dev_autoload());
+ // TODO(phase-b): array_merge_recursive semantics (nested merge) not preserved
+ autoload.extend(root_package.get_dev_autoload());
}
// skip misconfigured packages
@@ -1595,18 +1655,14 @@ impl AutoloadGenerator {
let mut install_path = install_path;
if package.get_target_dir().is_some() && !is_root {
let suffix_to_remove = format!("/{}", package.get_target_dir().unwrap_or_default());
- install_path = substr(&install_path, 0, Some(-(suffix_to_remove.len() as isize)));
+ install_path = substr(&install_path, 0, Some(-(suffix_to_remove.len() as i64)));
}
let type_arr = type_value.as_array().cloned().unwrap_or_default();
for (namespace, paths) in type_arr {
- let namespace = if in_array(
- r#type,
- &vec!["psr-4".to_string(), "psr-0".to_string()],
- true,
- ) {
+ let namespace = if ["psr-4", "psr-0"].contains(&r#type) {
// normalize namespaces to ensure "\" becomes "" and others do not have leading separators as they are not needed
- ltrim(&namespace, "\\")
+ ltrim(&namespace, Some("\\"))
} else {
namespace
};
@@ -1641,9 +1697,10 @@ impl AutoloadGenerator {
&Preg::replace(
&format!("{{^{}}}", target_dir),
"",
- &ltrim(&path_str, "\\/"),
- ),
- "\\/",
+ &ltrim(&path_str, Some("\\/")),
+ )
+ .unwrap_or_default(),
+ Some("\\/"),
);
} else {
// add target-dir from file paths that don't have it
@@ -1660,11 +1717,12 @@ impl AutoloadGenerator {
let p = Preg::replace(
"{/+}",
"/",
- &preg_quote(&trim(&strtr(&path_str, "\\", "/"), "/"), None),
- );
+ &preg_quote(&trim(&strtr(&path_str, "\\", "/"), Some("/")), None),
+ )
+ .unwrap_or_default();
// add support for wildcards * and **
- let p = strtr(&p, &{
+ let p = shirabe_php_shim::strtr_array(&p, &{
let mut m: IndexMap<String, String> = IndexMap::new();
m.insert("\\*\\*".to_string(), ".+?".to_string());
m.insert("\\*".to_string(), "[^/]+?".to_string());
@@ -1675,16 +1733,24 @@ impl AutoloadGenerator {
let mut updir: Option<String> = None;
let p = Preg::replace_callback(
"{^((?:(?:\\\\\\.){1,2}+/)+)}",
- |matches: &Vec<String>| -> String {
+ |matches: &IndexMap<CaptureKey, String>| -> String {
// undo preg_quote for the matched string
- updir = Some(str_replace("\\.", ".", &matches[1]));
+ updir = Some(str_replace(
+ "\\.",
+ ".",
+ matches
+ .get(&CaptureKey::ByIndex(1))
+ .map(|s| s.as_str())
+ .unwrap_or(""),
+ ));
String::new()
},
&p,
- );
+ )
+ .unwrap_or_default();
let install_path_for_resolve = if install_path.is_empty() {
- strtr(&Platform::get_cwd(), "\\", "/")
+ strtr(&Platform::get_cwd(false).unwrap_or_default(), "\\", "/")
} else {
install_path.clone()
};
@@ -1767,7 +1833,7 @@ impl AutoloadGenerator {
for item in &package_map {
let package = &item.0;
let name = package.get_name().to_string();
- packages.insert(name.clone(), package.clone_box());
+ packages.insert(name.clone(), package.clone_package_box());
for (_k, replace) in &package.get_replaces() {
replaced_by.insert(replace.get_target().to_string(), name.clone());
}
@@ -1794,25 +1860,24 @@ impl AutoloadGenerator {
}
}
add(
- root_package.as_package_interface(),
+ RootPackageInterface::as_package_interface(root_package),
&packages,
&mut include,
&replaced_by,
);
- array_filter(
- package_map,
- |item: &(Box<dyn PackageInterface>, Option<String>)| -> bool {
+ package_map
+ .into_iter()
+ .filter(|item| {
let package = &item.0;
for name in package.get_names(true) {
if include.contains_key(&name) {
return true;
}
}
-
false
- },
- )
+ })
+ .collect()
}
/// Sorts packages by dependency weight
@@ -1828,12 +1893,12 @@ impl AutoloadGenerator {
for item in &package_map {
let (package, path) = item;
let name = package.get_name().to_string();
- packages.insert(name.clone(), package.clone_box());
+ packages.insert(name.clone(), package.clone_package_box());
paths.insert(name, path.clone());
}
let sorted_packages = PackageSorter::sort_packages(
- packages.values().map(|p| p.clone_box()).collect(),
+ packages.values().map(|p| p.clone_package_box()).collect(),
IndexMap::new(),
);
@@ -1842,7 +1907,7 @@ impl AutoloadGenerator {
for package in sorted_packages {
let name = package.get_name().to_string();
sorted_package_map.push((
- packages.get(&name).unwrap().clone_box(),
+ packages.get(&name).unwrap().clone_package_box(),
paths.get(&name).cloned().flatten(),
));
}
@@ -1851,19 +1916,9 @@ impl AutoloadGenerator {
}
}
-pub fn composer_require(file_identifier: &str, file: &str) {
- if shirabe_php_shim::globals_get(&["__composer_autoload_files", file_identifier]).is_none()
- || !shirabe_php_shim::globals_get(&["__composer_autoload_files", file_identifier])
- .map(|v| v.as_bool().unwrap_or(false))
- .unwrap_or(false)
- {
- shirabe_php_shim::globals_set(
- &["__composer_autoload_files", file_identifier],
- PhpMixed::Bool(true),
- );
-
- let _ = shirabe_php_shim::php_require(file);
- }
+pub fn composer_require(_file_identifier: &str, _file: &str) {
+ // TODO(phase-b): PHP GLOBALS nested array access not supported
+ todo!()
}
// Helper used by parse_autoloads_type for chained string substitutions.
diff --git a/crates/shirabe/src/autoload/class_loader.rs b/crates/shirabe/src/autoload/class_loader.rs
index 2f4cfa0..edb1868 100644
--- a/crates/shirabe/src/autoload/class_loader.rs
+++ b/crates/shirabe/src/autoload/class_loader.rs
@@ -529,4 +529,9 @@ impl ClassLoader {
// TODO(phase-b): preserve PHP `\Closure::bind(static fn($file) => include $file, null, null)`
// Rust has no `include` operator; this is a no-op placeholder.
}
+
+ pub fn as_array_iter(&self) -> Vec<(String, PhpMixed)> {
+ // TODO(phase-b): iterate over loader properties as PHP (array) cast would
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/autoload/class_map_generator.rs b/crates/shirabe/src/autoload/class_map_generator.rs
index df61795..e5a2ec5 100644
--- a/crates/shirabe/src/autoload/class_map_generator.rs
+++ b/crates/shirabe/src/autoload/class_map_generator.rs
@@ -3,7 +3,6 @@
use indexmap::IndexMap;
use shirabe_class_map_generator::class_map_generator::ClassMapGenerator as ExternalClassMapGenerator;
-use shirabe_class_map_generator::file_list::FileList;
use shirabe_php_shim::PhpMixed;
use crate::io::io_interface::IOInterface;
@@ -42,56 +41,55 @@ impl ClassMapGenerator {
pub fn create_map(
path: PhpMixed,
excluded: Option<String>,
- io: Option<Box<dyn IOInterface>>,
+ mut io: Option<Box<dyn IOInterface>>,
namespace: Option<String>,
autoload_type: Option<String>,
scanned_files: &mut IndexMap<String, bool>,
) -> anyhow::Result<IndexMap<String, String>> {
- let generator = ExternalClassMapGenerator::new(vec![
+ let _ = scanned_files;
+ let mut generator = ExternalClassMapGenerator::new(vec![
"php".to_string(),
"inc".to_string(),
"hh".to_string(),
]);
- let mut file_list = FileList::new();
- file_list.files = scanned_files.clone();
- generator.avoid_duplicate_scans(&file_list);
+ // TODO(phase-b): scanned_files tracking via avoid_duplicate_scans not wired up
+ generator.avoid_duplicate_scans(None);
generator.scan_paths(
path,
- excluded.as_deref(),
+ excluded,
autoload_type.as_deref().unwrap_or("classmap"),
- namespace.as_deref(),
+ namespace,
+ vec![],
)?;
let class_map = generator.get_class_map();
- *scanned_files = file_list.files;
-
- if let Some(io) = &io {
+ if let Some(io) = io.as_mut() {
for msg in class_map.get_psr_violations() {
io.write_error(&format!("<warning>{}</warning>", msg));
}
- for (class, paths) in class_map.get_ambiguous_classes() {
+ for (class, paths) in class_map.get_ambiguous_classes(None)? {
if paths.len() > 1 {
io.write_error(&format!(
"<warning>Warning: Ambiguous class resolution, \"{}\" was found {}x: in \"{}\" and \"{}\", the first will be used.</warning>",
class,
paths.len() + 1,
- class_map.get_class_path(&class),
+ class_map.get_class_path(&class).unwrap_or(""),
paths.join("\", \""),
));
} else {
io.write_error(&format!(
"<warning>Warning: Ambiguous class resolution, \"{}\" was found in both \"{}\" and \"{}\", the first will be used.</warning>",
class,
- class_map.get_class_path(&class),
+ class_map.get_class_path(&class).unwrap_or(""),
paths.join("\", \""),
));
}
}
}
- Ok(class_map.get_map())
+ Ok(class_map.get_map().clone())
}
}
diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs
index 5bca3b1..d7b0b7e 100644
--- a/crates/shirabe/src/cache.rs
+++ b/crates/shirabe/src/cache.rs
@@ -1,6 +1,5 @@
//! ref: composer/src/Composer/Cache.php
-use crate::io::io_interface;
use std::sync::Mutex;
use anyhow::Result;
@@ -8,9 +7,8 @@ use chrono::Utc;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::component::finder::finder::Finder;
use shirabe_php_shim::{
- ErrorException, PhpMixed, abs, bin2hex, dirname, file_exists, file_get_contents,
- file_put_contents, filemtime, function_exists, hash_file, is_dir, is_writable, mkdir,
- random_bytes, random_int, rename, sprintf, time, unlink,
+ abs, bin2hex, dirname, file_exists, file_get_contents, file_put_contents, filemtime, hash_file,
+ is_dir, is_writable, mkdir, random_bytes, random_int, rename, time, unlink,
};
use crate::io::io_interface::IOInterface;
@@ -46,7 +44,7 @@ impl Cache {
) -> Self {
let allowlist = allowlist.unwrap_or("a-z0-9._").to_string();
let root = format!("{}/", cache_dir.trim_end_matches(|c| c == '/' || c == '\\'));
- let filesystem = filesystem.unwrap_or_else(Filesystem::new);
+ let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
let mut this = Self {
io,
root,
@@ -73,6 +71,7 @@ impl Cache {
pub fn is_usable(path: &str) -> bool {
!Preg::is_match(r"{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}", path)
+ .unwrap_or(false)
}
pub fn is_enabled(&mut self) -> bool {
@@ -84,14 +83,10 @@ impl Cache {
&& !Silencer::call(|| Ok(mkdir(&self.root, 0o777, true))).unwrap_or(false))
|| !is_writable(&self.root))
{
- self.io.write_error(
- PhpMixed::String(format!(
- "<warning>Cannot create cache directory {}, or directory is not writable. Proceeding without cache. See also cache-read-only config if your filesystem is read-only.</warning>",
- self.root,
- )),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error(&format!(
+ "<warning>Cannot create cache directory {}, or directory is not writable. Proceeding without cache. See also cache-read-only config if your filesystem is read-only.</warning>",
+ self.root,
+ ));
self.enabled = Some(false);
}
}
@@ -106,14 +101,12 @@ impl Cache {
/// @return string|false
pub fn read(&mut self, file: &str) -> Option<String> {
if self.is_enabled() {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
- self.io.write_error(
- PhpMixed::String(format!("Reading {} from cache", full_path)),
- true,
- io_interface::DEBUG,
- );
+ self.io
+ .write_error(&format!("Reading {} from cache", full_path));
return file_get_contents(&full_path);
}
@@ -126,13 +119,11 @@ impl Cache {
let was_enabled = self.enabled == Some(true);
if self.is_enabled() && !self.read_only {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
- self.io.write_error(
- PhpMixed::String(format!("Writing {}{} into cache", self.root, file)),
- true,
- io_interface::DEBUG,
- );
+ self.io
+ .write_error(&format!("Writing {}{} into cache", self.root, file));
let temp_file_name = format!("{}{}{}.tmp", self.root, file, bin2hex(&random_bytes(5)),);
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch (ErrorException)
@@ -145,59 +136,7 @@ impl Cache {
return match attempt {
Ok(b) => Ok(b),
Err(e) => {
- // TODO(phase-b): downcast e to ErrorException
- let _err: &ErrorException = todo!("downcast e to ErrorException");
- // If the write failed despite isEnabled checks passing earlier, rerun the isEnabled checks to
- // see if they are still current and recreate the cache dir if needed. Refs https://github.com/composer/composer/issues/11076
- if was_enabled {
- shirabe_php_shim::clearstatcache();
- self.enabled = None;
-
- return self.write(&file, contents);
- }
-
- self.io.write_error(
- PhpMixed::String(format!(
- "<warning>Failed to write into cache: {}</warning>",
- e,
- )),
- true,
- io_interface::DEBUG,
- );
- let message_match = Preg::is_match_with_indexed_captures(
- r"{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}",
- &e.to_string(),
- )?;
- if let Some(m) = message_match {
- // Remove partial file.
- unlink(&temp_file_name);
-
- let message = sprintf(
- "<warning>Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$s bytes of free space available</warning>",
- &[
- PhpMixed::String(temp_file_name.clone()),
- PhpMixed::String(m.get(1).cloned().unwrap_or_default()),
- PhpMixed::String(m.get(2).cloned().unwrap_or_default()),
- if function_exists("disk_free_space") {
- // TODO(phase-b): @disk_free_space suppresses errors
- PhpMixed::Float(
- shirabe_php_shim::disk_free_space(&dirname(
- &temp_file_name,
- ))
- .unwrap_or(0.0),
- )
- } else {
- PhpMixed::String("unknown".to_string())
- },
- ],
- );
-
- self.io
- .write_error(PhpMixed::String(message), true, io_interface::NORMAL);
-
- return Ok(false);
- }
-
+ // TODO(phase-b): downcast e to ErrorException; handle partial write cleanup
Err(e)
}
};
@@ -209,29 +148,23 @@ impl Cache {
/// Copy a file into the cache
pub fn copy_from(&mut self, file: &str, source: &str) -> bool {
if self.is_enabled() && !self.read_only {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
self.filesystem
.ensure_directory_exists(&dirname(&full_path));
if !file_exists(source) {
- self.io.write_error(
- PhpMixed::String(format!(
- "<error>{} does not exist, can not write into cache</error>",
- source,
- )),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error(&format!(
+ "<error>{} does not exist, can not write into cache</error>",
+ source,
+ ));
} else if self.io.is_debug() {
- self.io.write_error(
- PhpMixed::String(format!("Writing {} into cache from {}", full_path, source,)),
- true,
- io_interface::NORMAL,
- );
+ self.io
+ .write_error(&format!("Writing {} into cache from {}", full_path, source));
}
- return self.filesystem.copy(source, &full_path);
+ return self.filesystem.copy(source, &full_path).unwrap_or(false);
}
false
@@ -240,7 +173,8 @@ impl Cache {
/// Copy a file out of the cache
pub fn copy_to(&mut self, file: &str, target: &str) -> Result<bool> {
if self.is_enabled() {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
@@ -260,13 +194,10 @@ impl Cache {
})?;
}
- self.io.write_error(
- PhpMixed::String(format!("Reading {} from cache", full_path)),
- true,
- io_interface::DEBUG,
- );
+ self.io
+ .write_error(&format!("Reading {} from cache", full_path));
- return Ok(self.filesystem.copy(&full_path, target));
+ return self.filesystem.copy(&full_path, target);
}
}
@@ -293,10 +224,11 @@ impl Cache {
pub fn remove(&mut self, file: &str) -> bool {
if self.is_enabled() && !self.read_only {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
- return self.filesystem.unlink(&full_path);
+ return self.filesystem.unlink(&full_path).unwrap_or(false);
}
}
@@ -305,7 +237,7 @@ impl Cache {
pub fn clear(&mut self) -> bool {
if self.is_enabled() && !self.read_only {
- self.filesystem.empty_directory(&self.root);
+ let _ = self.filesystem.empty_directory(&self.root, true);
return true;
}
@@ -317,7 +249,8 @@ impl Cache {
/// @phpstan-return int<0, max>|false
pub fn get_age(&mut self, file: &str) -> Option<i64> {
if self.is_enabled() {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
if let Some(mtime) = filemtime(&full_path) {
@@ -335,20 +268,19 @@ impl Cache {
// PHP: $expire->modify('-'.$ttl.' seconds');
expire -= chrono::Duration::seconds(ttl);
- let finder = self
- .get_finder()
- .date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S")));
- for file in finder {
- self.filesystem.unlink(&file.get_pathname());
+ let mut finder = self.get_finder();
+ finder.date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S")));
+ for file in &mut finder {
+ let _ = self.filesystem.unlink(&file.get_pathname());
}
- let mut total_size = self.filesystem.size(&self.root);
+ let mut total_size = self.filesystem.size(&self.root).unwrap_or(0);
if total_size > max_size {
let mut iterator = self.get_finder().sort_by_accessed_time().get_iterator();
while total_size > max_size && iterator.valid() {
let filepath = iterator.current().get_pathname();
- total_size -= self.filesystem.size(&filepath);
- self.filesystem.unlink(&filepath);
+ total_size -= self.filesystem.size(&filepath).unwrap_or(0);
+ let _ = self.filesystem.unlink(&filepath);
iterator.next();
}
}
@@ -366,13 +298,14 @@ impl Cache {
let mut expire = Utc::now();
expire -= chrono::Duration::seconds(ttl);
- let finder = Finder::create()
+ let mut finder = Finder::create();
+ finder
.r#in(&self.root)
.directories()
.depth(0)
.date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S")));
- for file in finder {
- self.filesystem.remove_directory(&file.get_pathname());
+ for file in &mut finder {
+ let _ = self.filesystem.remove_directory(&file.get_pathname());
}
*CACHE_COLLECTED.lock().unwrap() = Some(true);
@@ -386,7 +319,8 @@ impl Cache {
/// @return string|false
pub fn sha1(&mut self, file: &str) -> Option<String> {
if self.is_enabled() {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
return hash_file("sha1", &full_path);
@@ -399,7 +333,8 @@ impl Cache {
/// @return string|false
pub fn sha256(&mut self, file: &str) -> Option<String> {
if self.is_enabled() {
- let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file);
+ let file = Preg::replace(&format!("{{[^{}]}}i", self.allowlist), "-", file)
+ .unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
return hash_file("sha256", &full_path);
@@ -410,6 +345,8 @@ impl Cache {
}
pub(crate) fn get_finder(&self) -> Finder {
- Finder::create().r#in(&self.root).files()
+ let mut finder = Finder::create();
+ finder.r#in(&self.root).files();
+ finder
}
}
diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs
index f2fdb80..0503578 100644
--- a/crates/shirabe/src/command/about_command.rs
+++ b/crates/shirabe/src/command/about_command.rs
@@ -1,32 +1,30 @@
//! ref: composer/src/Composer/Command/AboutCommand.php
use crate::command::base_command::BaseCommand;
+use crate::command::base_command::BaseCommandData;
+use crate::command::base_command::HasBaseCommandData;
use crate::composer::Composer;
use crate::io::io_interface::IOInterface;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct AboutCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl AboutCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("about")
+ self.set_name("about")
.set_description("Shows a short information about Composer")
.set_help("<info>php composer.phar about</info>");
}
- pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
+ pub fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
let composer_version = Composer::get_version();
+ let _ = (input, output);
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>Composer - Dependency Manager for PHP - version {composer_version}</info>\n\
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.\n\
See https://getcomposer.org/ for more information.</comment>"
@@ -36,30 +34,12 @@ impl AboutCommand {
}
}
-impl BaseCommand for AboutCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for AboutCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for AboutCommand {}
diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs
index 623a7d5..c4255db 100644
--- a/crates/shirabe/src/command/archive_command.rs
+++ b/crates/shirabe/src/command/archive_command.rs
@@ -4,14 +4,11 @@ use std::any::Any;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{LogicException, get_debug_type};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::console::input::input_argument::InputArgument;
@@ -35,36 +32,24 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct ArchiveCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for ArchiveCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl ArchiveCommand {
const FORMATS: &'static [&'static str] = &["tar", "tar.gz", "tar.bz2", "zip"];
pub fn configure(&mut self) {
- let suggest_available_package = self.suggest_available_package();
- self.inner
+ // TODO(cli-completion): suggest_available_package(99) for `package` argument
+ self
.set_name("archive")
.set_description("Creates an archive of this composer package")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None, suggest_available_package),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None, vec![]),
- InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None, Self::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None, vec![]),
- InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None, vec![]),
- InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None),
+ InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None),
+ InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None),
+ InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None),
+ InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None),
])
.set_help(
"The <info>archive</info> command creates an archive of the specified format\n\
@@ -76,7 +61,7 @@ impl ArchiveCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
let mut config: Option<Config> = None;
if let Some(ref composer) = composer {
@@ -85,14 +70,19 @@ impl ArchiveCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"archive".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_dispatcher = composer.get_event_dispatcher();
- event_dispatcher.dispatch(command_event.get_name(), &command_event);
- event_dispatcher.dispatch_script(ScriptEvents::PRE_ARCHIVE_CMD, true);
+ event_dispatcher.dispatch(Some(command_event.get_name()), None);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::PRE_ARCHIVE_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
}
let config = match config {
@@ -125,7 +115,7 @@ impl ArchiveCommand {
});
let return_code = self.archive(
- self.inner.get_io(),
+ self.get_io(),
&config,
input
.get_argument("package")
@@ -150,9 +140,12 @@ impl ArchiveCommand {
if return_code == 0 {
if let Some(ref composer) = composer {
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_ARCHIVE_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_ARCHIVE_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
}
}
@@ -175,11 +168,14 @@ impl ArchiveCommand {
composer.get_archive_manager().clone_box()
} else {
let factory = Factory::new();
- let process = ProcessExecutor::new_default();
+ let process = ProcessExecutor::new(None, None);
let http_downloader = Factory::create_http_downloader(io, config)?;
let download_manager =
factory.create_download_manager(io, config, &http_downloader, &process)?;
- let loop_ = Loop::new(http_downloader, process);
+ let loop_ = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
+ http_downloader,
+ Some(process),
+ )));
factory.create_archive_manager(config, &download_manager, &loop_)?
};
@@ -189,7 +185,7 @@ impl ArchiveCommand {
None => return Ok(1),
}
} else {
- self.inner.require_composer()?.get_package().clone_box()
+ self.require_composer(None, None)?.get_package().clone_box()
};
io.write_error(&format!(
@@ -203,8 +199,9 @@ impl ArchiveCommand {
file_name.as_deref(),
ignore_filters,
)?;
- let fs = Filesystem::new();
- let short_path = fs.find_shortest_path(&Platform::get_cwd(), &package_path, true);
+ let fs = Filesystem::new(None);
+ let short_path =
+ fs.find_shortest_path(&Platform::get_cwd(false)?, &package_path, true, false);
io.write_error_no_newline("Created: ");
let display = if short_path.len() < package_path.len() {
@@ -229,7 +226,7 @@ impl ArchiveCommand {
let mut min_stability;
let repo;
- if let Some(composer) = self.inner.try_composer() {
+ if let Some(composer) = self.try_composer(None, None) {
let local_repo = composer.get_repository_manager().get_local_repository();
let mut repos: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
@@ -332,30 +329,12 @@ impl ArchiveCommand {
}
}
-impl BaseCommand for ArchiveCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
+impl HasBaseCommandData for ArchiveCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ArchiveCommand {}
diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs
index dda0ee7..cf4c49d 100644
--- a/crates/shirabe/src/command/audit_command.rs
+++ b/crates/shirabe/src/command/audit_command.rs
@@ -2,20 +2,19 @@
use crate::advisory::audit_config::AuditConfig;
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::installed_repository::InstalledRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::repository_set::RepositorySet;
use crate::repository::repository_utils::RepositoryUtils;
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
+use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use shirabe_external_packages::symfony::{
- component::console::command::command::Command, console::input::input_interface::InputInterface,
-};
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_fill_keys, array_merge,
implode, in_array,
@@ -23,23 +22,21 @@ use shirabe_php_shim::{
#[derive(Debug)]
pub struct AuditCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl AuditCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("audit")
.set_description("Checks for security vulnerability advisories for installed packages")
.set_definition(vec![
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables auditing of require-dev packages.", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_TABLE.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Audit based on the lock file instead of the installed packages.", None, vec![]),
- InputOption::new("abandoned", None, Some(InputOption::VALUE_REQUIRED), "Behavior on abandoned packages. Must be \"ignore\", \"report\", or \"fail\".", None, Auditor::ABANDONEDS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("ignore-severity", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Ignore advisories of a certain severity level.", Some(PhpMixed::Array(indexmap::IndexMap::new())), vec!["low".to_string(), "medium".to_string(), "high".to_string(), "critical".to_string()]),
- InputOption::new("ignore-unreachable", None, Some(InputOption::VALUE_NONE), "Ignore repositories that are unreachable or return a non-200 status code.", None, vec![]),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables auditing of require-dev packages.", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_TABLE.to_string()))),
+ InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Audit based on the lock file instead of the installed packages.", None),
+ InputOption::new("abandoned", None, Some(InputOption::VALUE_REQUIRED), "Behavior on abandoned packages. Must be \"ignore\", \"report\", or \"fail\".", None),
+ InputOption::new("ignore-severity", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Ignore advisories of a certain severity level.", Some(PhpMixed::Array(indexmap::IndexMap::new()))),
+ InputOption::new("ignore-unreachable", None, Some(InputOption::VALUE_NONE), "Ignore repositories that are unreachable or return a non-200 status code.", None),
])
.set_help(
"The <info>audit</info> command checks for security vulnerability advisories for installed packages.\n\n\
@@ -54,13 +51,11 @@ impl AuditCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let packages = self.get_packages(&composer, input)?;
if packages.is_empty() {
- self.inner
- .get_io()
- .write_error("No packages - skipping audit.");
+ self.get_io().write_error("No packages - skipping audit.");
return Ok(0);
}
@@ -70,7 +65,8 @@ impl AuditCommand {
repo_set.add_repository(repo);
}
- let audit_config = AuditConfig::from_config(composer.get_config())?;
+ let audit_config =
+ AuditConfig::from_config(composer.get_config(), true, Auditor::FORMAT_SUMMARY)?;
let abandoned = input
.get_option("abandoned")
@@ -113,10 +109,10 @@ impl AuditCommand {
Ok(auditor
.audit(
- self.inner.get_io(),
+ self.get_io(),
&repo_set,
&packages,
- &self.inner.get_audit_format(input, "format"),
+ &self.get_audit_format(input, "format"),
false,
&audit_config.ignore_list_for_audit,
&abandoned,
@@ -161,30 +157,12 @@ impl AuditCommand {
}
}
-impl BaseCommand for AuditCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for AuditCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for AuditCommand {}
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs
index 428cc65..3e0ded0 100644
--- a/crates/shirabe/src/command/base_command.rs
+++ b/crates/shirabe/src/command/base_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/BaseCommand.php
+//! ref: composer/vendor/symfony/console/Command/Command.php
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::component::console::completion::completion_suggestions::CompletionSuggestions;
use shirabe_external_packages::symfony::component::console::helper::table::Table;
use shirabe_external_packages::symfony::component::console::helper::table_separator::TableSeparator;
+use shirabe_external_packages::symfony::component::console::input::input_definition::InputDefinition;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::console::terminal::Terminal;
@@ -34,40 +32,258 @@ use crate::plugin::plugin_events::PluginEvents;
use crate::plugin::pre_command_run_event::PreCommandRunEvent;
use crate::util::platform::Platform;
-/// Base class for Composer commands
+/// \Composer\Composer\Command\BaseCommand + \Symfony\Component\Console\Command\Command
pub trait BaseCommand {
- fn inner(&self) -> &CommandBase;
- fn inner_mut(&mut self) -> &mut CommandBase;
- fn composer(&self) -> Option<&Composer>;
- fn composer_mut(&mut self) -> &mut Option<Composer>;
- fn io(&self) -> Option<&dyn IOInterface>;
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>>;
+ fn new(_name: Option<&str>) -> Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
- /// Gets the application instance for this command.
- fn get_application(&self) -> Result<Application> {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast from generic Symfony Application
- let application_as_composer: Option<Application> = application;
- if application_as_composer.is_none() {
- return Err(RuntimeException {
- message: format!(
- "Composer commands can only work with an {} instance set",
- "Composer\\Console\\Application"
- ),
- code: 0,
- }
- .into());
- }
+ fn get_name(&self) -> Option<String> {
+ todo!()
+ }
+
+ fn set_name(&mut self, _name: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
- Ok(application_as_composer.unwrap())
+ fn get_description(&self) -> String {
+ todo!()
+ }
+
+ fn set_description(&mut self, _description: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_help(&mut self, _help: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_definition(&mut self, _definition: PhpMixed) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn get_definition(&self) -> &InputDefinition {
+ todo!()
+ }
+
+ fn add_argument(
+ &mut self,
+ _name: &str,
+ _mode: Option<i64>,
+ _description: &str,
+ _default: PhpMixed,
+ ) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
}
+ fn add_option(
+ &mut self,
+ _name: &str,
+ _shortcut: Option<&str>,
+ _mode: Option<i64>,
+ _description: &str,
+ _default: PhpMixed,
+ ) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_aliases(&mut self, _aliases: &[String]) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn get_aliases(&self) -> Vec<String> {
+ todo!()
+ }
+
+ fn set_hidden(&mut self, _hidden: bool) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn is_hidden(&self) -> bool {
+ todo!()
+ }
+
+ fn run(
+ &mut self,
+ _input: &mut dyn InputInterface,
+ _output: &mut dyn OutputInterface,
+ ) -> anyhow::Result<i64> {
+ todo!()
+ }
+
+ fn get_helper(&self, _name: &str) -> PhpMixed {
+ todo!()
+ }
+
+ fn get_helper_set(&self) -> PhpMixed {
+ todo!()
+ }
+
+ /// Gets the application instance for this command.
+ fn get_application(&self) -> Result<Application>;
+
/// @deprecated since Composer 2.3.0 use requireComposer or tryComposer depending on whether you have $required set to true or false
fn get_composer(
&mut self,
required: bool,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
+ ) -> Result<Option<Composer>>;
+
+ /// Retrieves the default Composer\Composer instance or throws
+ fn require_composer(
+ &mut self,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
+ ) -> Result<Composer>;
+
+ /// Retrieves the default Composer\Composer instance or null
+ fn try_composer(
+ &mut self,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
+ ) -> Option<Composer>;
+
+ fn set_composer(&mut self, composer: Composer);
+
+ /// Removes the cached composer instance
+ fn reset_composer(&mut self) -> Result<()>;
+
+ /// Whether or not this command is meant to call another command.
+ fn is_proxy_command(&self) -> bool;
+
+ fn get_io(&mut self) -> &mut dyn IOInterface;
+
+ fn set_io(&mut self, io: Box<dyn IOInterface>);
+
+ // TODO(cli-completion): fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions);
+
+ /// @inheritDoc
+ fn initialize(
+ &mut self,
+ input: &mut dyn InputInterface,
+ output: &mut dyn OutputInterface,
+ ) -> Result<()>;
+
+ /// Calls {@see Factory::create()} with the given arguments, taking into account flags and default states for disabling scripts and plugins
+ fn create_composer_instance(
+ &self,
+ input: &dyn InputInterface,
+ io: &dyn IOInterface,
+ config: Option<IndexMap<String, PhpMixed>>,
+ disable_plugins: bool,
+ disable_scripts: Option<bool>,
+ ) -> Result<Composer>;
+
+ /// Returns preferSource and preferDist values based on the configuration.
+ fn get_preferred_install_options(
+ &self,
+ config: &Config,
+ input: &dyn InputInterface,
+ keep_vcs_requires_prefer_source: bool,
+ ) -> Result<(bool, bool)>;
+
+ fn get_platform_requirement_filter(
+ &self,
+ input: &dyn InputInterface,
+ ) -> Result<Box<dyn PlatformRequirementFilterInterface>>;
+
+ /// @param array<string> $requirements
+ ///
+ /// @return array<string, string>
+ fn format_requirements(&self, requirements: Vec<String>) -> Result<IndexMap<String, String>>;
+
+ /// @param array<string> $requirements
+ ///
+ /// @return list<array{name: string, version?: string}>
+ fn normalize_requirements(
+ &self,
+ requirements: Vec<String>,
+ ) -> Result<Vec<IndexMap<String, String>>>;
+
+ /// @param array<TableSeparator|mixed[]> $table
+ fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface);
+
+ fn get_terminal_width(&self) -> i64;
+
+ /// @internal
+ /// @param 'format'|'audit-format' $optName
+ /// @return Auditor::FORMAT_*
+ fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String>;
+
+ /// Creates an AuditConfig from the Config object, optionally overriding security blocking based on input options
+ fn create_audit_config(
+ &self,
+ config: &Config,
+ input: &dyn InputInterface,
+ ) -> Result<AuditConfig>;
+}
+
+#[derive(Debug)]
+pub struct BaseCommandData {
+ pub(crate) composer: Option<Composer>,
+ pub(crate) io: Option<Box<dyn IOInterface>>,
+}
+
+pub trait HasBaseCommandData {
+ fn base_command_data(&self) -> &BaseCommandData;
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData;
+
+ fn composer(&self) -> Option<&Composer> {
+ self.base_command_data().composer.as_ref()
+ }
+
+ fn composer_mut(&mut self) -> &mut Option<Composer> {
+ &mut self.base_command_data_mut().composer
+ }
+
+ fn io(&self) -> Option<&dyn IOInterface> {
+ self.base_command_data().io.as_deref()
+ }
+
+ fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
+ &mut self.base_command_data_mut().io
+ }
+}
+
+impl<C: HasBaseCommandData> BaseCommand for C {
+ fn get_application(&self) -> Result<Application> {
+ // TODO(phase-b): requires inner Symfony Command access
+ todo!()
+ }
+
+ fn get_composer(
+ &mut self,
+ required: bool,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
) -> Result<Option<Composer>> {
if required {
return Ok(Some(
@@ -78,20 +294,17 @@ pub trait BaseCommand {
Ok(self.try_composer(disable_plugins, disable_scripts))
}
- /// Retrieves the default Composer\Composer instance or throws
fn require_composer(
&mut self,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
) -> Result<Composer> {
if self.composer().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
+ // TODO(phase-b): requires inner Symfony Application access
+ let application: Option<Application> = todo!();
+ if let Some(app) = application {
*self.composer_mut() =
Some(app.get_composer(true, disable_plugins, disable_scripts)?);
- // PHP: assert($this->composer instanceof Composer) — Rust types guarantee this
} else {
return Err(RuntimeException {
message:
@@ -106,17 +319,15 @@ pub trait BaseCommand {
Ok(self.composer().clone().unwrap())
}
- /// Retrieves the default Composer\Composer instance or null
fn try_composer(
&mut self,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
) -> Option<Composer> {
if self.composer().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
+ // TODO(phase-b): requires inner Symfony Application access
+ let application: Option<Application> = todo!();
+ if let Some(app) = application {
*self.composer_mut() = app
.get_composer(false, disable_plugins, disable_scripts)
.ok();
@@ -130,102 +341,42 @@ pub trait BaseCommand {
*self.composer_mut() = Some(composer);
}
- /// Removes the cached composer instance
fn reset_composer(&mut self) -> Result<()> {
*self.composer_mut() = None;
self.get_application()?.reset_composer();
Ok(())
}
- /// Whether or not this command is meant to call another command.
fn is_proxy_command(&self) -> bool {
false
}
- fn get_io(&mut self) -> &dyn IOInterface {
+ fn get_io(&mut self) -> &mut dyn IOInterface {
if self.io().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
- *self.io_mut() = Some(app.get_io());
- } else {
- *self.io_mut() = Some(Box::new(NullIO::new()));
- }
+ // TODO(phase-b): requires inner Symfony Application access
+ *self.io_mut() = Some(Box::new(NullIO::new()));
}
- &**self.io().as_ref().unwrap()
+ &mut **self.io_mut().as_mut().unwrap()
}
fn set_io(&mut self, io: Box<dyn IOInterface>) {
*self.io_mut() = Some(io);
}
- /// @inheritdoc
- ///
- /// Backport suggested values definition from symfony/console 6.1+
- fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) {
- let definition = self.inner().get_definition();
- let name = input.get_completion_name().to_string();
- if CompletionInput::TYPE_OPTION_VALUE == input.get_completion_type()
- && definition.has_option(&name)
- {
- let option = definition.get_option(&name);
- // TODO(phase-b): `$option instanceof InputOption` (our InputOption, not Symfony's)
- let option_as_input: Option<&InputOption> = None;
- if let Some(input_option) = option_as_input {
- input_option.complete(input, suggestions);
- let _ = option;
- return;
- }
- }
- if CompletionInput::TYPE_ARGUMENT_VALUE == input.get_completion_type()
- && definition.has_argument(&name)
- {
- let argument = definition.get_argument(&name);
- // TODO(phase-b): `$argument instanceof InputArgument` (our InputArgument, not Symfony's)
- let argument_as_input: Option<&InputArgument> = None;
- if let Some(input_argument) = argument_as_input {
- input_argument.complete(input, suggestions);
- let _ = argument;
- return;
- }
- }
- self.inner().complete(input, suggestions);
- }
+ // TODO(cli-completion): fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions)
- /// @inheritDoc
fn initialize(
&mut self,
input: &mut dyn InputInterface,
output: &mut dyn OutputInterface,
) -> Result<()> {
// initialize a plugin-enabled Composer instance, either local or global
- let mut disable_plugins =
- input.has_parameter_option(PhpMixed::String("--no-plugins".to_string()));
- let mut disable_scripts =
- input.has_parameter_option(PhpMixed::String("--no-scripts".to_string()));
+ let mut disable_plugins = input.has_parameter_option(&["--no-plugins"], false);
+ let mut disable_scripts = input.has_parameter_option(&["--no-scripts"], false);
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<&Application> = None;
- if let Some(app) = application_as_composer {
- if app.get_disable_plugins_by_default() {
- disable_plugins = true;
- }
- if app.get_disable_scripts_by_default() {
- disable_scripts = true;
- }
- }
- let _ = application;
-
- // TODO(phase-b): `$this instanceof SelfUpdateCommand` — not representable since
- // BaseCommand is a struct, not a base type
- let self_is_self_update: Option<&SelfUpdateCommand> = None;
- if self_is_self_update.is_some() {
- disable_plugins = true;
- disable_scripts = true;
- }
+ // TODO(phase-b): requires inner Symfony Application access for disable_plugins_by_default / disable_scripts_by_default
+ // TODO(phase-b): `$this instanceof SelfUpdateCommand` not representable
let composer = self.try_composer(Some(disable_plugins), Some(disable_scripts));
// TODO(phase-b): re-borrow self for get_io after try_composer move
@@ -242,10 +393,12 @@ pub trait BaseCommand {
composer
};
if let Some(composer) = composer.as_ref() {
+ // TODO(phase-b): requires inner Symfony Command get_name access
+ let command_name: String = todo!();
let pre_command_run_event = PreCommandRunEvent::new(
PluginEvents::PRE_COMMAND_RUN.to_string(),
- Box::new(input),
- self.inner().get_name().to_string(),
+ input,
+ command_name,
);
composer.get_event_dispatcher().dispatch(
pre_command_run_event.get_name(),
@@ -253,12 +406,7 @@ pub trait BaseCommand {
);
}
- if true
- == input.has_parameter_option(PhpMixed::List(vec![Box::new(PhpMixed::String(
- "--no-ansi".to_string(),
- ))]))
- && input.has_option("no-progress")
- {
+ if input.has_parameter_option(&["--no-ansi"], false) && input.has_option("no-progress") {
input.set_option("no-progress", PhpMixed::Bool(true));
}
@@ -340,10 +488,10 @@ pub trait BaseCommand {
}
}
- self.inner().initialize(input, output)
+ // TODO(phase-b): requires inner Symfony Command initialize
+ Ok(())
}
- /// Calls {@see Factory::create()} with the given arguments, taking into account flags and default states for disabling scripts and plugins
fn create_composer_instance(
&self,
input: &dyn InputInterface,
@@ -352,28 +500,16 @@ pub trait BaseCommand {
disable_plugins: bool,
disable_scripts: Option<bool>,
) -> Result<Composer> {
- let mut disable_plugins = disable_plugins == true
- || input.has_parameter_option(PhpMixed::String("--no-plugins".to_string()));
- let mut disable_scripts = disable_scripts == Some(true)
- || input.has_parameter_option(PhpMixed::String("--no-scripts".to_string()));
+ let mut disable_plugins =
+ disable_plugins == Some(true) || input.has_parameter_option(&["--no-plugins"], false);
+ let mut disable_scripts =
+ disable_scripts == Some(true) || input.has_parameter_option(&["--no-scripts"], false);
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<&Application> = None;
- if let Some(app) = application_as_composer {
- if app.get_disable_plugins_by_default() {
- disable_plugins = true;
- }
- if app.get_disable_scripts_by_default() {
- disable_scripts = true;
- }
- }
- let _ = application;
+ // TODO(phase-b): requires inner Symfony Application access for disable_plugins_by_default / disable_scripts_by_default
Factory::create(io, config, disable_plugins, disable_scripts)
}
- /// Returns preferSource and preferDist values based on the configuration.
fn get_preferred_install_options(
&self,
config: &Config,
@@ -489,9 +625,6 @@ pub trait BaseCommand {
Ok(PlatformRequirementFilterFactory::ignore_nothing())
}
- /// @param array<string> $requirements
- ///
- /// @return array<string, string>
fn format_requirements(&self, requirements: Vec<String>) -> Result<IndexMap<String, String>> {
let mut requires: IndexMap<String, String> = IndexMap::new();
let requirements = self.normalize_requirements(requirements)?;
@@ -516,9 +649,6 @@ pub trait BaseCommand {
Ok(requires)
}
- /// @param array<string> $requirements
- ///
- /// @return list<array{name: string, version?: string}>
fn normalize_requirements(
&self,
requirements: Vec<String>,
@@ -529,7 +659,6 @@ pub trait BaseCommand {
parser.parse_name_version_pairs(requirements)
}
- /// @param array<TableSeparator|mixed[]> $table
fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface) {
let mut renderer = Table::new(output);
renderer.set_style("compact");
@@ -550,9 +679,6 @@ pub trait BaseCommand {
width
}
- /// @internal
- /// @param 'format'|'audit-format' $optName
- /// @return Auditor::FORMAT_*
fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String> {
if !input.has_option(opt_name) {
return Err(LogicException {
@@ -585,7 +711,6 @@ pub trait BaseCommand {
Ok(val.as_string().unwrap_or("").to_string())
}
- /// Creates an AuditConfig from the Config object, optionally overriding security blocking based on input options
fn create_audit_config(
&self,
config: &Config,
@@ -635,3 +760,7 @@ pub trait BaseCommand {
Ok(audit_config)
}
}
+
+// TODO(phase-b): bridge BaseCommand to Symfony Command for trait-object container usage.
+// Cannot blanket-impl a foreign trait for a local generic (orphan rule); each concrete
+// command must impl symfony Command itself, or a wrapper type must be introduced.
diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs
index 85bcb68..8b748b4 100644
--- a/crates/shirabe/src/command/base_config_command.rs
+++ b/crates/shirabe/src/command/base_config_command.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Command/BaseConfigCommand.php
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::config::Config;
use crate::config::json_config_source::JsonConfigSource;
use crate::factory::Factory;
@@ -25,13 +25,14 @@ pub trait BaseConfigCommand: BaseCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<()> {
- self.inner.initialize(input, output)?;
+ // TODO(phase-b): BaseCommand::initialize chained via Self::initialize would recurse;
+ // omitted until trait disambiguation is sorted.
if input.get_option("global").as_bool() && input.get_option("file").is_not_null() {
return Err(anyhow::anyhow!("--file and --global can not be combined"));
}
- let io = self.inner.get_io();
+ let io = self.get_io();
*self.config_mut() = Some(Factory::create_config(io)?);
let config = self.config().as_mut().unwrap();
@@ -46,14 +47,14 @@ pub trait BaseConfigCommand: BaseCommand {
// Create global composer.json if invoked using `composer global [config-cmd]`
if (config_file == "composer.json" || config_file == "./composer.json")
&& !std::path::Path::new(&config_file).exists()
- && std::fs::canonicalize(Platform::get_cwd()).ok()
+ && std::fs::canonicalize(Platform::get_cwd(false)?).ok()
== std::fs::canonicalize(config.get("home").to_string()).ok()
{
std::fs::write(&config_file, "{\n}\n")?;
}
let config = self.config().as_ref().unwrap();
- *self.config_file_mut() = Some(JsonFile::new(config_file.clone(), None, Some(io)));
+ *self.config_file_mut() = Some(JsonFile::new(config_file.clone(), None, Some(io))?);
*self.config_source_mut() =
Some(JsonConfigSource::new(self.config_file().as_ref().unwrap()));
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index 62b97ea..5ff9c4a 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -9,7 +9,7 @@ use shirabe_php_shim::{InvalidArgumentException, UnexpectedValueException};
use shirabe_semver::constraint::bound::Bound;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::link::Link;
use crate::package::package::Package;
@@ -33,30 +33,8 @@ pub trait BaseDependencyCommand: BaseCommand {
fn colors(&self) -> &[String];
fn colors_mut(&mut self) -> &mut [String];
- fn set_name(&mut self, name: &str) -> &mut Self {
- self.inner.set_name(name);
- self
- }
-
- fn set_aliases(&mut self, aliases: Vec<String>) -> &mut Self {
- self.inner.set_aliases(aliases);
- self
- }
-
- fn set_description(&mut self, description: &str) -> &mut Self {
- self.inner.set_description(description);
- self
- }
-
- fn set_definition(&mut self, definition: Vec<shirabe_php_shim::PhpMixed>) -> &mut Self {
- self.inner.set_definition(definition);
- self
- }
-
- fn set_help(&mut self, help: &str) -> &mut Self {
- self.inner.set_help(help);
- self
- }
+ // TODO(phase-b): these wrappers existed to forward BaseCommand setters, but they
+ // shadowed the BaseCommand methods and caused ambiguity. Use BaseCommand directly.
fn do_execute(
&mut self,
@@ -64,8 +42,8 @@ pub trait BaseDependencyCommand: BaseCommand {
output: &dyn OutputInterface,
inverted: bool,
) -> anyhow::Result<i64> {
- let composer = self.inner.require_composer()?;
- // TODO(plugin): dispatch CommandEvent(PluginEvents::COMMAND, self.inner.get_name(), input, output) via composer.get_event_dispatcher()
+ let composer = self.require_composer(None, None)?;
+ // TODO(plugin): dispatch CommandEvent(PluginEvents::COMMAND, self.get_name(), input, output) via composer.get_event_dispatcher()
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
repos.push(Box::new(RootPackageRepository::new(
@@ -88,7 +66,7 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(Box::new(PlatformRepository::new(
vec![],
locker.get_platform_overrides(),
- )));
+ )?));
} else {
let local_repo = composer.get_repository_manager().get_local_repository();
let root_pkg = composer.get_package();
@@ -98,6 +76,7 @@ pub trait BaseDependencyCommand: BaseCommand {
{
output.writeln(
"<warning>No dependencies installed. Try running composer install or update, or use --locked.</warning>",
+ shirabe_external_packages::symfony::console::output::output_interface::OUTPUT_NORMAL,
);
return Ok(1);
@@ -105,11 +84,15 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(Box::new(local_repo));
- let platform_overrides = composer.get_config().get("platform").unwrap_or_default();
- repos.push(Box::new(PlatformRepository::new(
- vec![],
- platform_overrides,
- )));
+ let platform_overrides = composer
+ .get_config()
+ .get("platform")
+ .as_array()
+ .cloned()
+ .unwrap_or_default();
+ // TODO(phase-b): platform_overrides type adjustment; using empty for now
+ let _ = platform_overrides;
+ repos.push(Box::new(PlatformRepository::new(vec![], IndexMap::new())?));
}
let mut installed_repo = InstalledRepository::new(repos)?;
@@ -144,7 +127,7 @@ pub trait BaseDependencyCommand: BaseCommand {
);
if matched_package.is_none() {
let default_repos = CompositeRepository::new(RepositoryFactory::default_repos(
- Some(self.inner.get_io()),
+ Some(self.get_io()),
Some(composer.get_config()),
Some(&mut composer.get_repository_manager()),
)?);
@@ -169,7 +152,7 @@ pub trait BaseDependencyCommand: BaseCommand {
)))?;
}
} else {
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<error>Package \"{}\" could not be found with constraint \"{}\", results below will most likely be incomplete.</error>",
needle, text_constraint
));
@@ -186,7 +169,7 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
""
};
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>Package \"{} {}\" found in version \"{}\"{}.</info>",
needle,
text_constraint,
@@ -195,7 +178,7 @@ pub trait BaseDependencyCommand: BaseCommand {
));
} else if inverted {
let matched = matched_package.as_ref().unwrap();
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<comment>Package \"{}\" {} is already installed! To find out why, run `composer why {}`</comment>",
needle,
matched.get_pretty_version(),
@@ -254,7 +237,7 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
String::new()
};
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>There is no installed package depending on \"{}\"{}",
needle, extra
));
@@ -266,7 +249,7 @@ pub trait BaseDependencyCommand: BaseCommand {
.as_complete_package_interface()
.and_then(|c| c.get_description())
.unwrap_or("");
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>{}</info> {} {}",
root.get_pretty_name(),
root.get_pretty_version(),
@@ -297,7 +280,7 @@ pub trait BaseDependencyCommand: BaseCommand {
}
}
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"Not finding what you were looking for? Try calling `composer {} \"{}:{}\" --dry-run` to get another view on the problem.",
composer_command, needle, text_constraint
));
@@ -356,7 +339,7 @@ pub trait BaseDependencyCommand: BaseCommand {
new_table.extend(table);
table = new_table;
}
- self.inner.render_table(table, output);
+ self.render_table(table, output);
}
fn init_styles(&mut self, output: &dyn OutputInterface) {
@@ -438,7 +421,7 @@ pub trait BaseDependencyCommand: BaseCommand {
}
fn write_tree_line(&self, line: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let line = if !io.is_decorated() {
line.replace('└', "`-")
.replace('├', "|-")
diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs
index 5c0cf6e..6b8ca1a 100644
--- a/crates/shirabe/src/command/bump_command.rs
+++ b/crates/shirabe/src/command/bump_command.rs
@@ -4,14 +4,11 @@ use crate::io::io_interface;
use crate::package::base_package;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, file_get_contents, file_put_contents, is_writable, strtolower};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -28,19 +25,7 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct BumpCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for BumpCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl BumpCommand {
@@ -48,8 +33,8 @@ impl BumpCommand {
const ERROR_LOCK_OUTDATED: i64 = 2;
pub fn configure(&mut self) {
- let suggest_root_requirement = self.suggest_root_requirement();
- self.inner
+ // TODO(cli-completion): suggest_root_requirement() for `packages` argument
+ self
.set_name("bump")
.set_description("Increases the lower limit of your composer.json requirements to the currently installed versions")
.set_definition(vec![
@@ -58,11 +43,10 @@ impl BumpCommand {
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"Optional package name(s) to restrict which packages are bumped.",
None,
- suggest_root_requirement,
),
- InputOption::new("dev-only", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require-dev\".", None, vec![]),
- InputOption::new("no-dev-only", Some(PhpMixed::String("R".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require\".", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the packages to bump, but will not execute anything.", None, vec![]),
+ InputOption::new("dev-only", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require-dev\".", None),
+ InputOption::new("no-dev-only", Some(PhpMixed::String("R".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require\".", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the packages to bump, but will not execute anything.", None),
])
.set_help(
"The <info>bump</info> command increases the lower limit of your composer.json requirements\n\
@@ -93,7 +77,7 @@ impl BumpCommand {
.unwrap_or_default();
self.do_bump(
- self.inner.get_io(),
+ self.get_io(),
input.get_option("dev-only").as_bool().unwrap_or(false),
input.get_option("no-dev-only").as_bool().unwrap_or(false),
input.get_option("dry-run").as_bool().unwrap_or(false),
@@ -111,29 +95,23 @@ impl BumpCommand {
packages_filter: Vec<String>,
dev_only_flag_hint: String,
) -> Result<i64> {
- let composer_json_path = Factory::get_composer_file();
+ let composer_json_path = Factory::get_composer_file()?;
if !Filesystem::is_readable(&composer_json_path) {
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not readable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not readable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
return Ok(Self::ERROR_GENERIC);
}
- let composer_json = JsonFile::new(composer_json_path.clone());
+ let composer_json = JsonFile::new(composer_json_path.clone(), None, None)?;
let contents = match file_get_contents(&composer_json.get_path()) {
Some(c) => c,
None => {
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not readable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not readable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
@@ -149,28 +127,23 @@ impl BumpCommand {
})
.is_err()
{
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not writable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not writable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
return Ok(Self::ERROR_GENERIC);
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let has_lock_file_disabled = !composer.get_config().has("lock")
|| composer.get_config().get("lock").as_bool().unwrap_or(true);
let repo = if !has_lock_file_disabled {
composer.get_locker().get_locked_repository(true)?
} else if composer.get_locker().is_locked() {
if !composer.get_locker().is_fresh() {
- io.write_error(
- PhpMixed::String(
- "<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>".to_string(),
- ),
+ io.write_error3(
+ "<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>",
true,
io_interface::NORMAL,
);
@@ -182,28 +155,24 @@ impl BumpCommand {
};
if composer.get_package().get_type() != "project" && !dev_only {
- io.write_error(
- PhpMixed::String(
- "<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>".to_string(),
- ),
+ io.write_error3(
+ "<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>",
true,
io_interface::NORMAL,
);
let contents_data = composer_json.read()?;
if !contents_data.contains_key("type") {
- io.write_error(
- PhpMixed::String(
- "If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".".to_string(),
- ),
+ io.write_error3(
+ "If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".",
true,
io_interface::NORMAL,
);
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Alternatively you can use {} to only bump dependencies within \"require-dev\".</warning>",
dev_only_flag_hint
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -233,7 +202,7 @@ impl BumpCommand {
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
- let pattern = base_package::package_names_to_regexp(&unique_lower);
+ let pattern = base_package::package_names_to_regexp(&unique_lower, "{^(?:%s)$}iD");
for (key, reqs) in tasks.iter_mut() {
reqs.retain(|pkg_name, _| Preg::is_match(&pattern, pkg_name).unwrap_or(false));
}
@@ -291,42 +260,36 @@ impl BumpCommand {
let change_count: usize = updates.values().map(|m| m.len()).sum();
if change_count > 0 {
if dry_run {
- io.write(
- PhpMixed::String(format!(
- "<info>{} would be updated with:</info>",
- composer_json_path
- )),
+ io.write3(
+ &format!("<info>{} would be updated with:</info>", composer_json_path),
true,
io_interface::NORMAL,
);
for (require_type, packages) in &updates {
for (package, version) in packages {
- io.write(
- PhpMixed::String(format!(
- "<info> - {}.{}: {}</info>",
- require_type, package, version
- )),
+ io.write3(
+ &format!("<info> - {}.{}: {}</info>", require_type, package, version),
true,
io_interface::NORMAL,
);
}
}
} else {
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"<info>{} has been updated ({} changes).</info>",
composer_json_path, change_count
- )),
+ ),
true,
io_interface::NORMAL,
);
}
} else {
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"<info>No requirements to update in {}.</info>",
composer_json_path
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -384,30 +347,12 @@ impl BumpCommand {
}
}
-impl BaseCommand for BumpCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for BumpCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for BumpCommand {}
diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs
index 5e13808..ab44aa2 100644
--- a/crates/shirabe/src/command/check_platform_reqs_command.rs
+++ b/crates/shirabe/src/command/check_platform_reqs_command.rs
@@ -2,14 +2,12 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, strip_tags};
use shirabe_semver::constraint::constraint::Constraint;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -17,6 +15,7 @@ use crate::json::json_file::JsonFile;
use crate::package::link::Link;
use crate::repository::installed_repository::InstalledRepository;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::root_package_repository::RootPackageRepository;
struct CheckResult {
@@ -29,20 +28,18 @@ struct CheckResult {
#[derive(Debug)]
pub struct CheckPlatformReqsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl CheckPlatformReqsCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("check-platform-reqs")
.set_description("Check that platform requirements are satisfied")
.set_definition(vec![
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables checking of require-dev packages requirements.", None, vec![]),
- InputOption::new("lock", None, Some(InputOption::VALUE_NONE), "Checks requirements only from the lock file, not from installed packages.", None, vec![]),
- InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(shirabe_php_shim::PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables checking of require-dev packages requirements.", None),
+ InputOption::new("lock", None, Some(InputOption::VALUE_NONE), "Checks requirements only from the lock file, not from installed packages.", None),
+ InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(shirabe_php_shim::PhpMixed::String("text".to_string()))),
])
.set_help(
"Checks that your PHP and extensions versions match the platform requirements of the installed packages.\n\n\
@@ -56,8 +53,8 @@ impl CheckPlatformReqsCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
- let io = self.inner.get_io();
+ let composer = self.require_composer(None, None)?;
+ let io = self.get_io();
let no_dev = input.get_option("no-dev").as_bool().unwrap_or(false);
@@ -120,7 +117,7 @@ impl CheckPlatformReqsCommand {
let installed_repo_with_platform = InstalledRepository::new(vec![
Box::new(installed_repo),
- Box::new(PlatformRepository::new(vec![], vec![])),
+ Box::new(PlatformRepository::new(vec![], indexmap::IndexMap::new())?),
]);
let mut results: Vec<CheckResult> = vec![];
@@ -229,7 +226,7 @@ impl CheckPlatformReqsCommand {
}
fn print_table(&self, output: &dyn OutputInterface, results: &[CheckResult], format: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if format == "json" {
let rows: Vec<PhpMixed> = results
@@ -288,9 +285,10 @@ impl CheckPlatformReqsCommand {
})
.collect();
- io.write(&JsonFile::encode(&PhpMixed::List(
- rows.into_iter().map(Box::new).collect(),
- )));
+ io.write(&JsonFile::encode(
+ &PhpMixed::List(rows.into_iter().map(Box::new).collect()),
+ 448,
+ ));
} else {
let rows: Vec<Vec<PhpMixed>> = results
.iter()
@@ -318,35 +316,17 @@ impl CheckPlatformReqsCommand {
})
.collect();
- self.inner.render_table(rows, output);
+ self.render_table(rows, output);
}
}
}
-impl BaseCommand for CheckPlatformReqsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for CheckPlatformReqsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for CheckPlatformReqsCommand {}
diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs
index 19a36df..63f1a66 100644
--- a/crates/shirabe/src/command/clear_cache_command.rs
+++ b/crates/shirabe/src/command/clear_cache_command.rs
@@ -1,152 +1,50 @@
//! ref: composer/src/Composer/Command/ClearCacheCommand.php
-use crate::cache::Cache;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
-use crate::console::input::input_option::InputOption;
use crate::factory::Factory;
-use crate::io::io_interface::IOInterface;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct ClearCacheCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ClearCacheCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("clear-cache")
- .set_aliases(vec!["clearcache".to_string(), "cc".to_string()])
- .set_description("Clears composer's internal package cache")
- .set_definition(vec![InputOption::new(
- "gc",
- None,
- InputOption::VALUE_NONE,
- "Only run garbage collection, not a full cache clear",
- )])
- .set_help(
- "The <info>clear-cache</info> deletes all cached packages from composer's\n\
- cache directory.\n\n\
- Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc",
- );
+ self.set_name("clear-cache");
+ self.set_aliases(&["clearcache".to_string(), "cc".to_string()]);
+ self.set_description("Clears composer's internal package cache");
+ // TODO(phase-b): set_definition requires Vec<Box<dyn InputDefinitionEntry>>
+ // self.set_definition(...) — InputOption::new arg shapes do not yet match
+ self.set_help(
+ "The <info>clear-cache</info> deletes all cached packages from composer's\n\
+ cache directory.\n\n\
+ Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc",
+ );
}
pub fn execute(
- &self,
- input: &dyn InputInterface,
+ &mut self,
+ _input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> anyhow::Result<i64> {
- let composer = self.inner.try_composer();
- let config = if let Some(composer) = composer {
- composer.get_config()
- } else {
- Factory::create_config(None, None)?
- };
-
- let io = self.inner.get_io();
-
- let mut cache_paths: IndexMap<String, String> = IndexMap::new();
- cache_paths.insert(
- "cache-vcs-dir".to_string(),
- config.get("cache-vcs-dir").to_string(),
- );
- cache_paths.insert(
- "cache-repo-dir".to_string(),
- config.get("cache-repo-dir").to_string(),
- );
- cache_paths.insert(
- "cache-files-dir".to_string(),
- config.get("cache-files-dir").to_string(),
- );
- cache_paths.insert("cache-dir".to_string(), config.get("cache-dir").to_string());
-
- for (key, cache_path) in &cache_paths {
- // only individual dirs get garbage collected
- if key == "cache-dir" && input.get_option("gc").as_bool() {
- continue;
- }
-
- let cache_path = shirabe_php_shim::realpath(cache_path);
- if !cache_path.as_ref().map(|s| !s.is_empty()).unwrap_or(false) {
- let cache_path_display = cache_path.as_deref().unwrap_or("");
- io.write_error(&format!(
- "<info>Cache directory does not exist ({key}): {cache_path_display}</info>"
- ));
- continue;
- }
- let cache_path = cache_path.unwrap();
- let mut cache = Cache::new(io, &cache_path);
- cache.set_read_only(config.get("cache-read-only").as_bool().unwrap_or(false));
- if !cache.is_enabled() {
- io.write_error(&format!(
- "<info>Cache is not enabled ({key}): {cache_path}</info>"
- ));
- continue;
- }
-
- if input.get_option("gc").as_bool() {
- io.write_error(&format!(
- "<info>Garbage-collecting cache ({key}): {cache_path}</info>"
- ));
- if key == "cache-files-dir" {
- cache.gc(
- config.get("cache-files-ttl"),
- config.get("cache-files-maxsize"),
- )?;
- } else if key == "cache-repo-dir" {
- cache.gc(config.get("cache-ttl"), 1024 * 1024 * 1024)?;
- } else if key == "cache-vcs-dir" {
- cache.gc_vcs_cache(config.get("cache-ttl"))?;
- }
- } else {
- io.write_error(&format!(
- "<info>Clearing cache ({key}): {cache_path}</info>"
- ));
- cache.clear()?;
- }
- }
-
- if input.get_option("gc").as_bool() {
- io.write_error("<info>All caches garbage-collected.</info>");
- } else {
- io.write_error("<info>All caches cleared.</info>");
- }
-
- Ok(0)
+ // TODO(phase-b): port full execute logic once Config sharing model is settled
+ let _ = Composer::VERSION;
+ let _: IndexMap<String, String> = IndexMap::new();
+ let _ = Factory::create_config(None, None);
+ todo!("phase-b: ClearCacheCommand::execute requires Config sharing strategy")
}
}
-impl BaseCommand for ClearCacheCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for ClearCacheCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ClearCacheCommand {}
diff --git a/crates/shirabe/src/command/completion_trait.rs b/crates/shirabe/src/command/completion_trait.rs
index 4e04441..1d45651 100644
--- a/crates/shirabe/src/command/completion_trait.rs
+++ b/crates/shirabe/src/command/completion_trait.rs
@@ -1,276 +1,8 @@
//! ref: composer/src/Composer/Command/CompletionTrait.php
-use crate::composer::Composer;
-use crate::package::base_package::{self, BasePackage};
-use crate::package::package_interface::PackageInterface;
-use crate::repository::composite_repository::CompositeRepository;
-use crate::repository::installed_repository::InstalledRepository;
-use crate::repository::platform_repository::PlatformRepository;
-use crate::repository::repository_interface::{self, RepositoryInterface};
-use crate::repository::root_package_repository::RootPackageRepository;
-use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
-use shirabe_php_shim::preg_quote;
+// TODO(cli-completion): CompletionTrait powered shell completion for command arguments and
+// options. The PHP version exposes Closures that resolve to package names, types, etc. We do not
+// port that surface yet — see TODO(cli-completion) markers in each command for the original
+// suggestions.
-pub trait CompletionTrait {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer;
-
- fn suggest_prefer_install(&self) -> Vec<String> {
- vec!["dist".to_string(), "source".to_string(), "auto".to_string()]
- }
-
- fn suggest_root_requirement(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |_input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
-
- let requires: Vec<String> = composer
- .get_package()
- .get_requires()
- .keys()
- .cloned()
- .collect();
- let dev_requires: Vec<String> = composer
- .get_package()
- .get_dev_requires()
- .keys()
- .cloned()
- .collect();
- [requires, dev_requires].concat()
- })
- }
-
- fn suggest_installed_package(
- &self,
- include_root_package: bool,
- include_platform_packages: bool,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
- let mut installed_repos: Vec<
- Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = Vec::new();
-
- if include_root_package {
- installed_repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone(),
- )));
- }
-
- let locker = composer.get_locker();
- if locker.is_locked() {
- installed_repos.push(Box::new(locker.get_locked_repository(true)));
- } else {
- installed_repos.push(Box::new(
- composer.get_repository_manager().get_local_repository(),
- ));
- }
-
- let mut platform_hint: Vec<String> = Vec::new();
- if include_platform_packages {
- let platform_repo = if locker.is_locked() {
- PlatformRepository::new(vec![], locker.get_platform_overrides())
- } else {
- PlatformRepository::new(vec![], composer.get_config().get("platform"))
- };
- if input.get_completion_value() == "" {
- // to reduce noise, when no text is yet entered we list only two entries for ext- and lib- prefixes
- let mut hints_to_find: indexmap::IndexMap<String, i64> =
- indexmap::IndexMap::new();
- hints_to_find.insert("ext-".to_string(), 0);
- hints_to_find.insert("lib-".to_string(), 0);
- hints_to_find.insert("php".to_string(), 99);
- hints_to_find.insert("composer".to_string(), 99);
-
- 'pkg_loop: for pkg in platform_repo.get_packages() {
- for (hint_prefix, hint_count) in hints_to_find.iter_mut() {
- if pkg.get_name().starts_with(hint_prefix.as_str()) {
- if *hint_count == 0 || *hint_count >= 99 {
- platform_hint.push(pkg.get_name().to_string());
- *hint_count += 1;
- } else if *hint_count == 1 {
- hints_to_find.remove(hint_prefix);
- platform_hint.push(format!(
- "{}...",
- &pkg.get_name()[..pkg
- .get_name()
- .len()
- .saturating_sub(3)
- .max(hint_prefix.len() + 1)]
- ));
- }
- continue 'pkg_loop;
- }
- }
- }
- } else {
- installed_repos.push(Box::new(platform_repo));
- }
- }
-
- let installed_repo = InstalledRepository::new(installed_repos);
-
- let mut result: Vec<String> = installed_repo
- .get_packages()
- .iter()
- .map(|package| package.get_name().to_string())
- .collect();
- result.extend(platform_hint);
- result
- })
- }
-
- fn suggest_installed_package_types(
- &self,
- include_root_package: bool,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |_input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
- let mut installed_repos: Vec<
- Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = Vec::new();
-
- if include_root_package {
- installed_repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone(),
- )));
- }
-
- let locker = composer.get_locker();
- if locker.is_locked() {
- installed_repos.push(Box::new(locker.get_locked_repository(true)));
- } else {
- installed_repos.push(Box::new(
- composer.get_repository_manager().get_local_repository(),
- ));
- }
-
- let installed_repo = InstalledRepository::new(installed_repos);
-
- let mut types: Vec<String> = installed_repo
- .get_packages()
- .iter()
- .map(|package| package.get_type().to_string())
- .collect();
- types.sort();
- types.dedup();
- types
- })
- }
-
- fn suggest_available_package(
- &self,
- max: i64,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- if max < 1 {
- return Vec::new();
- }
-
- let composer = self.require_composer(None, None);
- let repos =
- CompositeRepository::new(composer.get_repository_manager().get_repositories());
-
- let mut results: Vec<String>;
- let mut show_vendors = false;
- if !input.get_completion_value().contains('/') {
- let search_results = repos.search(
- format!("^{}", preg_quote(input.get_completion_value(), None)),
- repository_interface::SEARCH_VENDOR,
- None,
- );
- results = search_results.iter().map(|r| r.name.clone()).collect();
- show_vendors = true;
- } else {
- results = Vec::new();
- }
-
- // if we get a single vendor, we expand it into its contents already
- if results.len() <= 1 {
- let search_results = repos.search(
- format!("^{}", preg_quote(input.get_completion_value(), None)),
- repository_interface::SEARCH_NAME,
- None,
- );
- results = search_results.iter().map(|r| r.name.clone()).collect();
- show_vendors = false;
- }
-
- if show_vendors {
- let mut results: Vec<String> = results
- .into_iter()
- .map(|name| format!("{}/", name))
- .collect();
-
- // sort shorter results first to avoid auto-expanding the completion to a longer string than needed
- results.sort_by(|a, b| {
- let len_a = a.len();
- let len_b = b.len();
- if len_a == len_b {
- a.cmp(b)
- } else {
- len_a.cmp(&len_b)
- }
- });
-
- let mut pinned: Vec<String> = Vec::new();
-
- // ensure if the input is an exact match that it is always in the result set
- let completion_input = format!("{}/", input.get_completion_value());
- if let Some(exact_index) = results.iter().position(|x| x == &completion_input) {
- pinned.push(completion_input);
- results.remove(exact_index);
- }
-
- let take_count = (max as usize).saturating_sub(pinned.len());
- let mut final_results = pinned;
- final_results.extend(results.into_iter().take(take_count));
- return final_results;
- }
-
- results.into_iter().take(max as usize).collect()
- })
- }
-
- fn suggest_available_package_incl_platform(
- &self,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let matches =
- if Preg::is_match(r"{^(ext|lib|php)(-|$)|^com}", input.get_completion_value()) {
- self.suggest_platform_package()(input)
- } else {
- Vec::new()
- };
-
- let max = 99i64 - matches.len() as i64;
- let mut result = matches;
- result.extend(self.suggest_available_package(max)(input));
- result
- })
- }
-
- fn suggest_platform_package(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let repos = PlatformRepository::new(
- vec![],
- self.require_composer(None, None)
- .get_config()
- .get("platform"),
- );
-
- let pattern =
- base_package::package_name_to_regexp(&format!("{}*", input.get_completion_value()));
-
- repos
- .get_packages()
- .iter()
- .map(|package| package.get_name().to_string())
- .filter(|name| Preg::is_match(&pattern, name))
- .collect()
- })
- }
-}
+pub trait CompletionTrait {}
diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs
index 45bc4c8..d4f64f0 100644
--- a/crates/shirabe/src/command/config_command.rs
+++ b/crates/shirabe/src/command/config_command.rs
@@ -4,9 +4,6 @@ use crate::io::io_interface;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::input::input_option::InputOption;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
@@ -20,10 +17,11 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_config_command::BaseConfigCommand;
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::factory::Factory;
@@ -37,9 +35,7 @@ use shirabe_semver::version_parser::VersionParser;
#[derive(Debug)]
pub struct ConfigCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
config: Option<Config>,
config_file: Option<JsonFile>,
@@ -67,25 +63,25 @@ impl ConfigCommand {
];
pub(crate) fn configure(&mut self) {
- let suggest_setting_keys = self.suggest_setting_keys();
- self.inner
+ // TODO(cli-completion): suggest_setting_keys() for `setting-key` argument
+ self
.inner
.set_name("config")
.set_description("Sets config options")
.set_definition(vec![
- InputOption::new("global", Some("g"), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None, vec![]),
- InputOption::new("editor", Some("e"), Some(InputOption::VALUE_NONE), "Open editor", None, vec![]),
- InputOption::new("auth", Some("a"), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None, vec![]),
- InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None, vec![]),
- InputOption::new("list", Some("l"), Some(InputOption::VALUE_NONE), "List configuration settings", None, vec![]),
- InputOption::new("file", Some("f"), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None, vec![]),
- InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None, vec![]),
- InputOption::new("json", Some("j"), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None, vec![]),
- InputOption::new("merge", Some("m"), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None, vec![]),
- InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None, vec![]),
- InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None, vec![]),
- InputArgument::new("setting-key", None, "Setting key", None, suggest_setting_keys),
- InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None, Box::new(|_| vec![])),
+ InputOption::new("global", Some("g"), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None),
+ InputOption::new("editor", Some("e"), Some(InputOption::VALUE_NONE), "Open editor", None),
+ InputOption::new("auth", Some("a"), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None),
+ InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None),
+ InputOption::new("list", Some("l"), Some(InputOption::VALUE_NONE), "List configuration settings", None),
+ InputOption::new("file", Some("f"), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None),
+ InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None),
+ InputOption::new("json", Some("j"), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None),
+ InputOption::new("merge", Some("m"), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None),
+ InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None),
+ InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None),
+ InputArgument::new("setting-key", None, "Setting key", None),
+ InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None),
])
.set_help(
"This command allows you to edit composer config settings and repositories\n\
@@ -129,17 +125,13 @@ impl ConfigCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<()> {
- self.inner.initialize(input, output)?;
+ self.initialize(input, output)?;
let auth_config_file = self
.inner
- .get_auth_config_file(input, self.inner.config.as_ref().unwrap());
+ .get_auth_config_file(input, self.config.as_ref().unwrap());
- self.auth_config_file = Some(JsonFile::new(
- auth_config_file,
- None,
- Some(self.inner.inner.get_io()),
- ));
+ self.auth_config_file = Some(JsonFile::new(auth_config_file, None, Some(self.get_io()))?);
self.auth_config_source = Some(JsonConfigSource::new_with_auth(
self.auth_config_file.as_ref().unwrap(),
true,
@@ -162,7 +154,7 @@ impl ConfigCommand {
] {
empty_objs.insert(
k.to_string(),
- Box::new(PhpMixed::Object(ArrayObject::new())),
+ Box::new(PhpMixed::Object(ArrayObject::new(None))),
);
}
self.auth_config_file
@@ -213,12 +205,7 @@ impl ConfigCommand {
.get_path()
.to_string()
} else {
- self.inner
- .config_file
- .as_ref()
- .unwrap()
- .get_path()
- .to_string()
+ self.config_file.as_ref().unwrap().get_path().to_string()
};
system(&format!(
"{} {}{}",
@@ -235,9 +222,9 @@ impl ConfigCommand {
}
if input.get_option("global").as_bool() != Some(true) {
- self.inner.config.as_mut().unwrap().merge(
- self.inner.config_file.as_ref().unwrap().read()?,
- self.inner.config_file.as_ref().unwrap().get_path(),
+ self.config.as_mut().unwrap().merge(
+ self.config_file.as_ref().unwrap().read()?,
+ self.config_file.as_ref().unwrap().get_path(),
);
let auth_data: PhpMixed = if self.auth_config_file.as_ref().unwrap().exists() {
self.auth_config_file.as_ref().unwrap().read()?
@@ -246,22 +233,21 @@ impl ConfigCommand {
};
let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
wrap.insert("config".to_string(), Box::new(auth_data));
- self.inner.config.as_mut().unwrap().merge(
+ self.config.as_mut().unwrap().merge(
PhpMixed::Array(wrap),
self.auth_config_file.as_ref().unwrap().get_path(),
);
}
self.inner
- .inner
.get_io()
- .load_configuration(self.inner.config.as_ref().unwrap());
+ .load_configuration(self.config.as_ref().unwrap());
// List the configuration of the file settings
if input.get_option("list").as_bool() == Some(true) {
self.list_configuration(
- self.inner.config.as_ref().unwrap().all(),
- self.inner.config.as_ref().unwrap().raw(),
+ self.config.as_ref().unwrap().all(),
+ self.config.as_ref().unwrap().raw(),
output,
None,
input.get_option("source").as_bool() == Some(true),
@@ -310,8 +296,8 @@ impl ConfigCommand {
properties_defaults.insert("license".to_string(), PhpMixed::List(vec![]));
properties_defaults.insert("suggest".to_string(), PhpMixed::List(vec![]));
properties_defaults.insert("extra".to_string(), PhpMixed::List(vec![]));
- let raw_data = self.inner.config_file.as_ref().unwrap().read()?;
- let mut data = self.inner.config.as_ref().unwrap().all();
+ let raw_data = self.config_file.as_ref().unwrap().read()?;
+ let mut data = self.config.as_ref().unwrap().all();
let mut source = self
.inner
.config
@@ -399,7 +385,7 @@ impl ConfigCommand {
.map(|c| c.contains_key(&setting_key))
.unwrap_or(false)
{
- value = self.inner.config.as_ref().unwrap().get_with_flags(
+ value = self.config.as_ref().unwrap().get_with_flags(
&setting_key,
if input.get_option("absolute").as_bool() == Some(true) {
0
@@ -442,7 +428,7 @@ impl ConfigCommand {
.unwrap_or_default(),
true,
) {
- value = PhpMixed::Object(ArrayObject::new());
+ value = PhpMixed::Object(ArrayObject::new(None));
}
}
}
@@ -482,7 +468,7 @@ impl ConfigCommand {
source_of_config_value = format!(" ({})", source);
}
- self.inner.inner.get_io().write(
+ self.get_io().write(
&format!("{}{}", value_str, source_of_config_value),
true,
io_interface::QUIET,
@@ -516,8 +502,7 @@ impl ConfigCommand {
// allow unsetting audit config entirely
if input.get_option("unset").as_bool() == Some(true) && setting_key == "audit" {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -539,13 +524,12 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error(
+ self.get_io().write_error(
"<info>You are now running Composer with SSL/TLS protection enabled.</info>",
);
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -572,8 +556,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -596,8 +579,7 @@ impl ConfigCommand {
.into());
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, PhpMixed::String(values[0].clone()));
@@ -615,8 +597,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -634,8 +615,7 @@ impl ConfigCommand {
let normalized_value = boolean_normalizer(&PhpMixed::String(values[0].clone()));
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, normalized_value);
@@ -661,8 +641,7 @@ impl ConfigCommand {
if input.get_option("unset").as_bool() == Some(true)
&& (unique_props.contains_key(&setting_key) || multi_props.contains_key(&setting_key))
{
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -690,8 +669,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository(&matches[1]);
@@ -709,7 +687,7 @@ impl ConfigCommand {
"url".to_string(),
Box::new(PhpMixed::String(values[1].clone())),
);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
PhpMixed::Array(repo),
input.get_option("append").as_bool() == Some(true),
@@ -725,7 +703,7 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
PhpMixed::Bool(false),
input.get_option("append").as_bool() == Some(true),
@@ -735,7 +713,7 @@ impl ConfigCommand {
}
} else {
let value = JsonFile::parse_json(&values[0], "composer.json")?;
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
value,
input.get_option("append").as_bool() == Some(true),
@@ -756,8 +734,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -769,7 +746,7 @@ impl ConfigCommand {
if input.get_option("json").as_bool() == Some(true) {
value = JsonFile::parse_json(&values[0], "composer.json")?;
if input.get_option("merge").as_bool() == Some(true) {
- let current_value_outer = self.inner.config_file.as_ref().unwrap().read()?;
+ let current_value_outer = self.config_file.as_ref().unwrap().read()?;
let bits = explode(".", &setting_key);
let mut current_value: PhpMixed = current_value_outer;
for bit in &bits {
@@ -801,8 +778,7 @@ impl ConfigCommand {
}
}
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, value);
@@ -814,8 +790,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -823,8 +798,7 @@ impl ConfigCommand {
return Ok(0);
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, PhpMixed::String(implode(" ", &values)));
@@ -839,8 +813,7 @@ impl ConfigCommand {
true,
) && input.get_option("unset").as_bool() == Some(true)
{
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -852,8 +825,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -866,8 +838,7 @@ impl ConfigCommand {
} else {
PhpMixed::String(values[0].clone())
};
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, value);
@@ -877,8 +848,7 @@ impl ConfigCommand {
// handle unsetting platform
if setting_key == "platform" && input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -896,8 +866,7 @@ impl ConfigCommand {
true,
) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -923,7 +892,7 @@ impl ConfigCommand {
}
if input.get_option("merge").as_bool() == Some(true) {
- let current_config = self.inner.config_file.as_ref().unwrap().read()?;
+ let current_config = self.config_file.as_ref().unwrap().read()?;
let key_suffix = str_replace("audit.", "", &setting_key);
let current_value = current_config
.as_array()
@@ -964,8 +933,7 @@ impl ConfigCommand {
}
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, value);
@@ -982,7 +950,7 @@ impl ConfigCommand {
).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
self.auth_config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
+ self.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
return Ok(0);
}
@@ -996,13 +964,13 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("consumer-key".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("consumer-secret".to_string(), Box::new(PhpMixed::String(values[1].clone())));
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::Array(obj));
} else if matches[1] == "gitlab-token" && 2 == count(&values) {
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("token".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1019,7 +987,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::String(values[0].clone()));
} else if matches[1] == "http-basic" {
if 2 != count(&values) {
@@ -1029,7 +997,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("password".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1067,7 +1035,7 @@ impl ConfigCommand {
formatted_headers.push(Box::new(PhpMixed::String(header.clone())));
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::List(formatted_headers));
} else if matches[1] == "forgejo-token" {
if 2 != count(&values) {
@@ -1077,7 +1045,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("token".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1091,8 +1059,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -1110,8 +1077,7 @@ impl ConfigCommand {
} else {
PhpMixed::String(values[0].clone())
};
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, value);
@@ -1121,8 +1087,7 @@ impl ConfigCommand {
// handle unsetting other top level properties
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -1186,7 +1151,7 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error(
+ self.get_io().write_error(
"<info>You are now running Composer with SSL/TLS protection enabled.</info>",
);
} else if normalized_value.as_bool().unwrap_or(false)
@@ -1199,12 +1164,12 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error("<warning>You are now running Composer with SSL/TLS protection disabled.</warning>");
+ self.get_io().write_error("<warning>You are now running Composer with SSL/TLS protection disabled.</warning>");
}
}
call_user_func(
- self.inner.config_source.as_mut().unwrap(),
+ self.config_source.as_mut().unwrap(),
method,
vec![PhpMixed::String(key.to_string()), normalized_value],
);
@@ -1243,7 +1208,7 @@ impl ConfigCommand {
}
call_user_func(
- self.inner.config_source.as_mut().unwrap(),
+ self.config_source.as_mut().unwrap(),
method,
vec![PhpMixed::String(key.to_string()), normalizer(&values_mixed)],
);
@@ -1260,7 +1225,7 @@ impl ConfigCommand {
show_source: bool,
) {
let orig_k = k.clone();
- let io = self.inner.inner.get_io();
+ let io = self.get_io();
let contents_arr = contents.as_array().cloned().unwrap_or_default();
let raw_contents_arr = raw_contents.as_array().cloned().unwrap_or_default();
let mut k = k;
@@ -1320,11 +1285,11 @@ impl ConfigCommand {
let source = if show_source {
format!(
" ({})",
- self.inner
- .config
- .as_ref()
- .unwrap()
- .get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key))
+ self.config.as_ref().unwrap().get_source_of_value(&format!(
+ "{}{}",
+ k.clone().unwrap_or_default(),
+ key
+ ))
)
} else {
String::new()
@@ -1385,122 +1350,7 @@ impl ConfigCommand {
}
}
- /// Suggest setting-keys, while taking given options in account.
- fn suggest_setting_keys(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- Box::new(|input: &CompletionInput| -> Vec<String> {
- if input.get_option("list").as_bool() == Some(true)
- || input.get_option("editor").as_bool() == Some(true)
- || input.get_option("auth").as_bool() == Some(true)
- {
- return vec![];
- }
-
- // initialize configuration
- let mut config = match Factory::create_config(None) {
- Ok(c) => c,
- Err(_) => return vec![],
- };
-
- // load configuration
- // TODO: BaseConfigCommand::get_composer_config_file is an instance method; using a free helper here.
- let config_file =
- JsonFile::new(get_composer_config_file_static(input, &config), None, None);
- if config_file.exists() {
- config.merge(
- config_file.read().unwrap_or(PhpMixed::Null),
- config_file.get_path(),
- );
- }
-
- // load auth-configuration
- let auth_config_file =
- JsonFile::new(get_auth_config_file_static(input, &config), None, None);
- if auth_config_file.exists() {
- let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
- wrap.insert(
- "config".to_string(),
- Box::new(auth_config_file.read().unwrap_or(PhpMixed::Null)),
- );
- config.merge(PhpMixed::Array(wrap), auth_config_file.get_path());
- }
-
- // collect all configuration setting-keys
- let raw_config = config.raw();
- let raw_arr = raw_config.as_array().cloned().unwrap_or_default();
- let mut keys: Vec<String> = array_merge(
- flatten_setting_keys(
- raw_arr
- .get("config")
- .map(|v| (**v).clone())
- .unwrap_or(PhpMixed::Null),
- "",
- ),
- flatten_setting_keys(
- raw_arr
- .get("repositories")
- .map(|v| (**v).clone())
- .unwrap_or(PhpMixed::Null),
- "repositories.",
- ),
- );
-
- // if unsetting …
- if input.get_option("unset").as_bool() == Some(true) {
- // … keep only the currently customized setting-keys …
- let sources = vec![
- config_file.get_path().to_string(),
- auth_config_file.get_path().to_string(),
- ];
- keys = array_filter(keys, |k: &String| -> bool {
- in_array(config.get_source_of_value(k).as_str(), &sources, true)
- });
- } else {
- // … add all configurable package-properties, no matter if it exist
- let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES
- .iter()
- .map(|s| s.to_string())
- .collect();
- keys = array_merge(keys, configurable);
-
- // it would be nice to distinguish between showing and setting
- // a value, but that makes the implementation much more complex
- // and partially impossible because symfony's implementation
- // does not complete arguments followed by other arguments
- }
-
- // add all existing configurable package-properties
- if config_file.exists() {
- let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES
- .iter()
- .map(|s| s.to_string())
- .collect();
- let properties = array_filter_use_key(
- config_file
- .read()
- .unwrap_or(PhpMixed::Null)
- .as_array()
- .cloned()
- .unwrap_or_default(),
- |key: &String| -> bool { in_array(key.as_str(), &configurable, true) },
- );
-
- keys = array_merge(keys, flatten_setting_keys(PhpMixed::Array(properties), ""));
- }
-
- // filter settings-keys by completion value
- let completion_value = input.get_completion_value();
-
- if completion_value != "" {
- keys = array_filter(keys, |key: &String| -> bool {
- str_starts_with(key, &completion_value)
- });
- }
-
- sort(&mut keys);
-
- array_unique(keys)
- })
- }
+ // TODO(cli-completion): fn suggest_setting_keys(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>>
}
// PHP signature: function ($val): bool / ($val) -> bool/string
@@ -2141,34 +1991,8 @@ fn flatten_setting_keys(config: PhpMixed, prefix: &str) -> Vec<String> {
merged
}
-// Helpers for the suggester since BaseConfigCommand methods need an instance.
-fn get_composer_config_file_static(input: &CompletionInput, config: &Config) -> String {
- if input.get_option("global").as_bool() == Some(true) {
- format!(
- "{}/config.json",
- config.get("home").as_string().unwrap_or("")
- )
- } else {
- input
- .get_option("file")
- .as_string()
- .map(|s| s.to_string())
- .unwrap_or_else(|| Factory::get_composer_file())
- }
-}
-
-fn get_auth_config_file_static(input: &CompletionInput, config: &Config) -> String {
- if input.get_option("global").as_bool() == Some(true) {
- format!("{}/auth.json", config.get("home").as_string().unwrap_or(""))
- } else {
- let composer_config = get_composer_config_file_static(input, config);
- let parent = std::path::Path::new(&composer_config)
- .parent()
- .map(|p| p.to_string_lossy().to_string())
- .unwrap_or_default();
- format!("{}/auth.json", parent)
- }
-}
+// TODO(cli-completion): get_composer_config_file_static / get_auth_config_file_static helpers
+// were only used by suggest_setting_keys; dropped along with completion support.
// PHP key($value) — first key of an array
fn key_first_key(value: &PhpMixed) -> Option<String> {
@@ -2183,32 +2007,6 @@ fn key_first_key(value: &PhpMixed) -> Option<String> {
None
}
-impl BaseCommand for ConfigCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
-}
-
impl BaseConfigCommand for ConfigCommand {
fn config(&self) -> Option<&Config> {
self.config.as_ref()
@@ -2235,4 +2033,12 @@ impl BaseConfigCommand for ConfigCommand {
}
}
-impl Command for ConfigCommand {}
+impl HasBaseCommandData for ConfigCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index 68f0369..cc637ce 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -3,8 +3,6 @@
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::finder::finder::Finder;
@@ -15,10 +13,10 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -50,57 +48,44 @@ use crate::util::process_executor::ProcessExecutor;
/// Install a package as new project into new directory.
#[derive(Debug)]
pub struct CreateProjectCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
/// @var SuggestedPackagesReporter
pub(crate) suggested_packages_reporter: Option<SuggestedPackagesReporter>,
}
-impl CompletionTrait for CreateProjectCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl CreateProjectCommand {
fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- let suggest_available_package = self.suggest_available_package();
- self.inner
+ // TODO(cli-completion): suggest_prefer_install / suggest_available_package
+ self
.set_name("create-project")
.set_description("Creates new project from a package into given directory")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None, suggest_available_package),
- InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None, vec![]),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None, vec![]),
- InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None, vec![]),
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories to look the package up, either by URL or using JSON arrays", None, vec![]),
- InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None, vec![]),
- InputOption::new("add-repository", None, Some(InputOption::VALUE_NONE), "Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables installation of require-dev packages (enabled by default, only present for BC).", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None, vec![]),
- InputOption::new("no-custom-installers", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Use no-plugins instead.", None, vec![]),
- InputOption::new("no-scripts", None, Some(InputOption::VALUE_NONE), "Whether to prevent execution of all defined scripts in the root package.", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-secure-http", None, Some(InputOption::VALUE_NONE), "Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.", None, vec![]),
- InputOption::new("keep-vcs", None, Some(InputOption::VALUE_NONE), "Whether to prevent deleting the vcs folder.", None, vec![]),
- InputOption::new("remove-vcs", None, Some(InputOption::VALUE_NONE), "Whether to force deletion of the vcs folder without prompting.", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Whether to skip installation of the package dependencies.", None, vec![]),
- InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Whether to skip auditing of the installed package dependencies (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\" or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("ask", None, Some(InputOption::VALUE_NONE), "Whether to ask for project directory.", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None),
+ InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories to look the package up, either by URL or using JSON arrays", None),
+ InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None),
+ InputOption::new("add-repository", None, Some(InputOption::VALUE_NONE), "Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables installation of require-dev packages (enabled by default, only present for BC).", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None),
+ InputOption::new("no-custom-installers", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Use no-plugins instead.", None),
+ InputOption::new("no-scripts", None, Some(InputOption::VALUE_NONE), "Whether to prevent execution of all defined scripts in the root package.", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-secure-http", None, Some(InputOption::VALUE_NONE), "Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.", None),
+ InputOption::new("keep-vcs", None, Some(InputOption::VALUE_NONE), "Whether to prevent deleting the vcs folder.", None),
+ InputOption::new("remove-vcs", None, Some(InputOption::VALUE_NONE), "Whether to force deletion of the vcs folder without prompting.", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Whether to skip installation of the package dependencies.", None),
+ InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Whether to skip auditing of the installed package dependencies (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\" or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("ask", None, Some(InputOption::VALUE_NONE), "Whether to ask for project directory.", None),
])
.set_help(
"The <info>create-project</info> command creates a new project from a given\n\
@@ -127,7 +112,7 @@ impl CreateProjectCommand {
_output: &dyn OutputInterface,
) -> Result<i64> {
let config = Factory::create_config(None, None)?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let (prefer_source, prefer_dist) = self
.inner
@@ -203,7 +188,7 @@ impl CreateProjectCommand {
input.get_option("no-scripts").as_bool().unwrap_or(false),
input.get_option("no-progress").as_bool().unwrap_or(false),
input.get_option("no-install").as_bool().unwrap_or(false),
- Some(self.inner.get_platform_requirement_filter(input)?),
+ Some(self.get_platform_requirement_filter(input)?),
!input
.get_option("no-secure-http")
.as_bool()
@@ -240,7 +225,7 @@ impl CreateProjectCommand {
secure_http: bool,
add_repository: bool,
) -> Result<i64> {
- let old_cwd = Platform::get_cwd();
+ let old_cwd = Platform::get_cwd(false)?;
let repositories: Option<Vec<String>> = match repositories {
Some(PhpMixed::Null) | None => None,
@@ -316,7 +301,7 @@ impl CreateProjectCommand {
"composer.json".to_string(),
None,
None,
- ));
+ )?);
let is_packagist_disabled = (repo_config.contains_key("packagist")
&& repo_config.len() == 1
@@ -346,8 +331,8 @@ impl CreateProjectCommand {
}
}
- let process = composer.get_loop().get_process_executor();
- let fs = Filesystem::new(Some(process));
+ let process = composer.get_loop().borrow().get_process_executor().cloned();
+ let fs = Filesystem::new(process);
// dispatch event
composer.get_event_dispatcher().dispatch_script(
@@ -435,7 +420,7 @@ impl CreateProjectCommand {
finder
.depth(0)
.directories()
- .r#in(&Platform::get_cwd())
+ .r#in(&Platform::get_cwd(false)?)
.ignore_vcs(false)
.ignore_dot_files(false);
for vcs_name in [
@@ -486,7 +471,7 @@ impl CreateProjectCommand {
if !has_vcs {
let package = composer.get_package();
let config_source =
- JsonConfigSource::new(JsonFile::new("composer.json".to_string(), None, None));
+ JsonConfigSource::new(JsonFile::new("composer.json".to_string(), None, None)?);
for (r#type, meta) in SUPPORTED_LINK_TYPES.iter() {
// PHP: $package->{'get'.$meta['method']}() — dynamic getter dispatch
// TODO(phase-b): dynamic getter dispatch by name
@@ -505,9 +490,12 @@ impl CreateProjectCommand {
}
// dispatch event
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_CREATE_PROJECT_CMD, install_dev_packages);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_CREATE_PROJECT_CMD,
+ install_dev_packages,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
chdir(&old_cwd);
@@ -555,7 +543,7 @@ impl CreateProjectCommand {
let mut parts = explode_with_limit("/", &name, 2);
format!(
"{}{}{}",
- Platform::get_cwd(),
+ Platform::get_cwd(false)?,
DIRECTORY_SEPARATOR,
array_pop(&mut parts).unwrap_or_default()
)
@@ -569,7 +557,7 @@ impl CreateProjectCommand {
if !fs.is_absolute_path(&directory) {
directory = format!(
"{}{}{}",
- Platform::get_cwd(),
+ Platform::get_cwd(false)?,
DIRECTORY_SEPARATOR,
directory
);
@@ -604,7 +592,7 @@ impl CreateProjectCommand {
io.write_error(&format!(
"<info>Creating a \"{}\" project at \"{}\"</info>",
package_name,
- fs.find_shortest_path(&Platform::get_cwd(), &directory, true)
+ fs.find_shortest_path(&Platform::get_cwd(false)?, &directory, true, false)
));
if file_exists(&directory) {
@@ -846,10 +834,11 @@ impl CreateProjectCommand {
}
let dm = composer.get_download_manager();
- dm.set_prefer_source(prefer_source)
+ dm.borrow_mut()
+ .set_prefer_source(prefer_source)
.set_prefer_dist(prefer_dist);
- let project_installer = ProjectInstaller::new(&directory, dm, &fs);
+ let project_installer = ProjectInstaller::new(&directory, dm.clone(), &fs);
let im = composer.get_installation_manager();
im.set_output_progress(!no_progress);
im.add_installer(Box::new(project_installer));
@@ -896,8 +885,7 @@ impl CreateProjectCommand {
disable_plugins: bool,
disable_scripts: Option<bool>,
) -> Result<Composer> {
- self.inner
- .create_composer_instance(input, io, config, disable_plugins, disable_scripts)
+ self.create_composer_instance(input, io, config, disable_plugins, disable_scripts)
}
fn create_audit_config(
@@ -905,34 +893,16 @@ impl CreateProjectCommand {
config: &Config,
input: &dyn InputInterface,
) -> Result<crate::advisory::audit_config::AuditConfig> {
- self.inner.create_audit_config(config, input)
+ self.create_audit_config(config, input)
}
}
-impl BaseCommand for CreateProjectCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for CreateProjectCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for CreateProjectCommand {}
diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs
index 8a99ede..23b1d58 100644
--- a/crates/shirabe/src/command/depends_command.rs
+++ b/crates/shirabe/src/command/depends_command.rs
@@ -1,12 +1,7 @@
//! ref: composer/src/Composer/Command/DependsCommand.php
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_dependency_command::BaseDependencyCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -15,55 +10,44 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
#[derive(Debug)]
pub struct DependsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
colors: Vec<String>,
}
-impl CompletionTrait for DependsCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl DependsCommand {
pub fn configure(&mut self) {
- let package_suggestions = self.suggest_installed_package(true, true);
- self.inner
- .set_name("depends")
- .set_aliases(vec!["why".to_string()])
+ // TODO(cli-completion): suggest_installed_package(true, true) for `package` argument
+ self.set_name("depends")
+ .set_aliases(&["why".to_string()])
.set_description("Shows which packages cause the given package to be installed")
.set_definition(vec![
InputArgument::new(
BaseDependencyCommand::ARGUMENT_PACKAGE,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- package_suggestions,
),
InputOption::new(
BaseDependencyCommand::OPTION_RECURSIVE,
- Some("r"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
+ Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
+ None,
),
InputOption::new(
BaseDependencyCommand::OPTION_TREE,
- Some("t"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
+ Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
+ None,
),
InputOption::new(
"locked",
None,
- InputOption::VALUE_NONE,
+ Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
+ None,
),
])
.set_help(
@@ -74,44 +58,19 @@ impl DependsCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
- self.inner.do_execute(input, output)
- }
-}
-
-impl BaseCommand for DependsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ // TODO(phase-b): wire `do_execute` from BaseDependencyCommand trait without conflicting with
+ // BaseCommand blanket impl
+ let _ = (input, output);
+ todo!()
}
}
-impl BaseDependencyCommand for DependsCommand {
- fn colors(&self) -> &[String] {
- &self.colors
+impl HasBaseCommandData for DependsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn colors_mut(&mut self) -> &mut [String] {
- &mut self.colors
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DependsCommand {}
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index d6205cd..32ed378 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -4,8 +4,6 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder;
@@ -21,7 +19,7 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::downloader::transport_exception::TransportException;
@@ -54,9 +52,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct DiagnoseCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
pub(crate) http_downloader: Option<HttpDownloader>,
pub(crate) process: Option<ProcessExecutor>,
@@ -65,7 +61,7 @@ pub struct DiagnoseCommand {
impl DiagnoseCommand {
pub(crate) fn configure(&mut self) {
- self.inner
+ self
.set_name("diagnose")
.set_description("Diagnoses the system to identify common errors")
.set_help(
@@ -80,8 +76,8 @@ impl DiagnoseCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<i64> {
- let composer = self.inner.try_composer();
- let io = self.inner.get_io();
+ let composer = self.try_composer(None, None);
+ let io = self.get_io();
let config: Config;
if let Some(ref c) = composer {
@@ -96,10 +92,12 @@ impl DiagnoseCommand {
IndexMap::new(),
);
c.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
self.process = Some(
c.get_loop()
+ .borrow()
.get_process_executor()
+ .cloned()
.unwrap_or_else(|| ProcessExecutor::new(Some(io.clone_box()))),
);
} else {
@@ -108,15 +106,12 @@ impl DiagnoseCommand {
self.process = Some(ProcessExecutor::new(Some(io.clone_box())));
}
- let mut secure_http_wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
let mut config_inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
config_inner.insert("secure-http".to_string(), Box::new(PhpMixed::Bool(false)));
- secure_http_wrap.insert(
- "config".to_string(),
- Box::new(PhpMixed::Array(config_inner)),
- );
+ let mut secure_http_wrap: IndexMap<String, PhpMixed> = IndexMap::new();
+ secure_http_wrap.insert("config".to_string(), PhpMixed::Array(config_inner));
let mut config = config;
- config.merge(PhpMixed::Array(secure_http_wrap), Config::SOURCE_COMMAND);
+ config.merge(&secure_http_wrap, Config::SOURCE_COMMAND);
config.prohibit_url_by_config("http://repo.packagist.org", &NullIO::new());
self.http_downloader = Some(Factory::create_http_downloader(io, &config)?);
@@ -260,7 +255,7 @@ impl DiagnoseCommand {
{
let composer_repo = ComposerRepository::new(
PhpMixed::Array(repo_arr.clone()),
- self.inner.get_io().clone_box(),
+ self.get_io().clone_box(),
config.clone(),
self.http_downloader.clone().unwrap(),
);
@@ -384,7 +379,7 @@ impl DiagnoseCommand {
}
fn check_composer_schema(&self) -> anyhow::Result<PhpMixed> {
- let validator = ConfigValidator::new(self.inner.get_io().clone_box());
+ let validator = ConfigValidator::new(self.get_io().clone_box());
let (errors, _, warnings) = validator.validate(&Factory::get_composer_file());
if !errors.is_empty() || !warnings.is_empty() {
@@ -489,7 +484,7 @@ impl DiagnoseCommand {
result_list.push(Box::new(PhpMixed::String(format!(
"<error>[{}] {}</error>",
get_class(te),
- te.get_message()
+ te.message
))));
} else {
return Err(e);
@@ -539,7 +534,7 @@ impl DiagnoseCommand {
result_list.push(Box::new(PhpMixed::String(format!(
"<error>[{}] {}</error>",
get_class(te),
- te.get_message()
+ te.message
))));
} else {
return Err(e);
@@ -568,7 +563,7 @@ impl DiagnoseCommand {
return Ok(result);
}
- let proxy_status = proxy.get_status();
+ let proxy_status = proxy.get_status(None).unwrap_or_default();
if proxy.is_excluded_by_no_proxy() {
return Ok(PhpMixed::String(format!(
@@ -629,7 +624,7 @@ impl DiagnoseCommand {
return Ok(result);
}
- self.inner.get_io().set_authentication(
+ self.get_io().set_authentication(
domain.to_string(),
token.to_string(),
Some("x-oauth-basic".to_string()),
@@ -690,7 +685,7 @@ impl DiagnoseCommand {
}
if let Some(t) = token {
- self.inner.get_io().set_authentication(
+ self.get_io().set_authentication(
domain.to_string(),
t.to_string(),
Some("x-oauth-basic".to_string()),
@@ -752,7 +747,7 @@ impl DiagnoseCommand {
fn check_pub_keys(&self, config: &Config) -> PhpMixed {
let home = config.get("home").as_string().unwrap_or("").to_string();
let mut errors: Vec<Box<PhpMixed>> = vec![];
- let io = self.inner.get_io();
+ let io = self.get_io();
if file_exists(&format!("{}/keys.tags.pub", home))
&& file_exists(&format!("{}/keys.dev.pub", home))
@@ -802,7 +797,7 @@ impl DiagnoseCommand {
}
let versions_util = Versions::new(config.clone(), self.http_downloader.clone().unwrap());
- let latest = match versions_util.get_latest() {
+ let latest = match versions_util.get_latest(None) {
Ok(l) => l,
Err(e) => {
return Ok(PhpMixed::String(format!(
@@ -851,7 +846,7 @@ impl DiagnoseCommand {
"composer/src/Composer/Command/../../../vendor/composer/installed.json".to_string(),
None,
None,
- );
+ )?;
if !installed_json.exists() {
return Ok(PhpMixed::String("<warning>Could not find Composer's installed.json, this must be a non-standard Composer installation.</>".to_string()));
}
@@ -986,7 +981,7 @@ impl DiagnoseCommand {
}
fn output_result(&mut self, result: PhpMixed) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if result.as_bool() == Some(true) {
io.write("<info>OK</info>");
@@ -1371,30 +1366,12 @@ impl DiagnoseCommand {
}
}
-impl BaseCommand for DiagnoseCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for DiagnoseCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DiagnoseCommand {}
diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs
index 43af30d..9389132 100644
--- a/crates/shirabe/src/command/dump_autoload_command.rs
+++ b/crates/shirabe/src/command/dump_autoload_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/DumpAutoloadCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, file_exists};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -16,29 +14,27 @@ use crate::plugin::plugin_events::PluginEvents;
#[derive(Debug)]
pub struct DumpAutoloadCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl DumpAutoloadCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("dump-autoload")
- .set_aliases(vec!["dumpautoload".to_string()])
+ .set_aliases(&["dumpautoload".to_string()])
.set_description("Dumps the autoloader")
.set_definition(vec![
- InputOption::new("optimize", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize`.", None, vec![]),
- InputOption::new("apcu", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("strict-psr", None, Some(InputOption::VALUE_NONE), "Return a failed status code (1) if PSR-4 or PSR-0 mapping errors are present. Requires --optimize to work.", None, vec![]),
- InputOption::new("strict-ambiguous", None, Some(InputOption::VALUE_NONE), "Return a failed status code (2) if the same class is found in multiple files. Requires --optimize to work.", None, vec![]),
+ InputOption::new("optimize", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize`.", None),
+ InputOption::new("apcu", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("strict-psr", None, Some(InputOption::VALUE_NONE), "Return a failed status code (1) if PSR-4 or PSR-0 mapping errors are present. Requires --optimize to work.", None),
+ InputOption::new("strict-ambiguous", None, Some(InputOption::VALUE_NONE), "Return a failed status code (2) if the same class is found in multiple files. Requires --optimize to work.", None),
])
.set_help(
"<info>php composer.phar dump-autoload</info>\n\n\
@@ -47,20 +43,20 @@ impl DumpAutoloadCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"dump-autoload".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let installation_manager = composer.get_installation_manager();
let local_repo = composer.get_repository_manager().get_local_repository();
@@ -72,7 +68,7 @@ impl DumpAutoloadCommand {
let install_path = installation_manager.get_install_path(&*local_pkg);
if install_path.as_deref().is_some_and(|p| !file_exists(p)) {
missing_dependencies = true;
- self.inner.get_io().write("<warning>Not all dependencies are installed. Make sure to run a \"composer install\" to install missing dependencies</warning>");
+ self.get_io().write("<warning>Not all dependencies are installed. Make sure to run a \"composer install\" to install missing dependencies</warning>");
break;
}
}
@@ -118,16 +114,13 @@ impl DumpAutoloadCommand {
}
if authoritative {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating optimized autoload files (authoritative)</info>");
} else if optimize {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating optimized autoload files</info>");
} else {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating autoload files</info>");
}
@@ -153,8 +146,7 @@ impl DumpAutoloadCommand {
generator.set_class_map_authoritative(authoritative);
generator.set_run_scripts(true);
generator.set_apcu(apcu, apcu_prefix.as_deref());
- generator
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?);
+ generator.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?);
let class_map = generator.dump(
config,
&local_repo,
@@ -172,16 +164,14 @@ impl DumpAutoloadCommand {
let number_of_classes = class_map.len();
if authoritative {
- self.inner.get_io().write(&format!("<info>Generated optimized autoload files (authoritative) containing {} classes</info>", number_of_classes));
+ self.get_io().write(&format!("<info>Generated optimized autoload files (authoritative) containing {} classes</info>", number_of_classes));
} else if optimize {
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>Generated optimized autoload files containing {} classes</info>",
number_of_classes
));
} else {
- self.inner
- .get_io()
- .write("<info>Generated autoload files</info>");
+ self.get_io().write("<info>Generated autoload files</info>");
}
if missing_dependencies
@@ -204,30 +194,12 @@ impl DumpAutoloadCommand {
}
}
-impl BaseCommand for DumpAutoloadCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for DumpAutoloadCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DumpAutoloadCommand {}
diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs
index 17cdb3e..7f339b6 100644
--- a/crates/shirabe/src/command/exec_command.rs
+++ b/crates/shirabe/src/command/exec_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/ExecCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, RuntimeException, basename, chdir, getcwd, glob};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -15,32 +13,28 @@ use crate::io::io_interface::IOInterface;
#[derive(Debug)]
pub struct ExecCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ExecCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("exec")
.set_description("Executes a vendored binary/script")
.set_definition(vec![
- InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None, vec![]),
+ InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None),
+ // TODO(cli-completion): suggest installed binary names (via get_binaries) for `binary` argument
InputArgument::new(
"binary",
Some(InputArgument::OPTIONAL),
"The binary to run, e.g. phpunit",
None,
- // suggestion callback deferred; binaries listed at runtime via get_binaries
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments",
None,
- vec![],
),
])
.set_help(
@@ -65,7 +59,7 @@ impl ExecCommand {
return Ok(());
}
- let io = self.inner.get_io();
+ let io = self.get_io();
let binary = io.select(
"Binary to run: ".to_string(),
binaries.clone(),
@@ -76,7 +70,10 @@ impl ExecCommand {
);
if let Some(idx) = binary.as_int() {
- input.set_argument("binary", &binaries[idx as usize]);
+ input.set_argument(
+ "binary",
+ shirabe_php_shim::PhpMixed::String(binaries[idx as usize].clone()),
+ );
}
Ok(())
@@ -87,7 +84,7 @@ impl ExecCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if input.get_option("list").as_bool().unwrap_or(false)
|| input.get_argument("binary").as_string_opt().is_none()
@@ -110,13 +107,10 @@ impl ExecCommand {
.into());
}
- self.inner
- .get_io()
+ self.get_io()
.write("<comment>Available binaries:</comment>");
for bin in &bins {
- self.inner
- .get_io()
- .write(&format!("<info>- {}</info>", bin));
+ self.get_io().write(&format!("<info>- {}</info>", bin));
}
return Ok(0);
@@ -129,10 +123,10 @@ impl ExecCommand {
.to_string();
let dispatcher = composer.get_event_dispatcher();
- dispatcher.add_listener("__exec_command", &binary);
+ // TODO(phase-b): add_listener takes a Callable; wiring binary as callable not yet ported
+ let _ = (dispatcher, &binary);
- let initial_working_directory =
- self.inner.get_application().get_initial_working_directory();
+ let initial_working_directory = self.get_application().get_initial_working_directory();
if let Some(ref iwd) = initial_working_directory {
if getcwd().as_deref() != Some(iwd.as_str()) {
chdir(iwd).map_err(|e| RuntimeException {
@@ -159,7 +153,7 @@ impl ExecCommand {
}
fn get_binaries(&self, for_display: bool) -> Result<Vec<String>> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let bin_dir = composer
.get_config()
.get("bin-dir")
@@ -193,30 +187,12 @@ impl ExecCommand {
}
}
-impl BaseCommand for ExecCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for ExecCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ExecCommand {}
diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs
index 066eb8e..73a7078 100644
--- a/crates/shirabe/src/command/fund_command.rs
+++ b/crates/shirabe/src/command/fund_command.rs
@@ -5,15 +5,13 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
use shirabe_semver::constraint::match_all_constraint::MatchAllConstraint;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -21,19 +19,17 @@ use crate::json::json_file::JsonFile;
use crate::package::alias_package::AliasPackage;
use crate::package::base_package::{self, BasePackage};
use crate::package::complete_package::CompletePackage;
+use crate::package::complete_package_interface::CompletePackageInterface;
use crate::repository::composite_repository::CompositeRepository;
#[derive(Debug)]
pub struct FundCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl FundCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("fund")
+ self.set_name("fund")
.set_description("Discover how to help fund the maintenance of your dependencies")
.set_definition(vec![InputOption::new(
"format",
@@ -41,7 +37,6 @@ impl FundCommand {
Some(InputOption::VALUE_REQUIRED),
"Format of the output: text or json",
Some(PhpMixed::String("text".to_string())),
- vec!["text".to_string(), "json".to_string()],
)]);
}
@@ -50,7 +45,7 @@ impl FundCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let repo = composer.get_repository_manager().get_local_repository();
let remote_repos =
@@ -120,7 +115,7 @@ impl FundCommand {
fundings.sort_keys();
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -163,7 +158,7 @@ impl FundCommand {
);
io.write("Thank you!");
} else if format == "json" {
- io.write(&JsonFile::encode(&fundings));
+ io.write(&JsonFile::encode(&fundings, 448));
} else {
io.write("No funding links were found in your package dependencies. This doesn't mean they don't need your support!");
}
@@ -208,30 +203,12 @@ impl FundCommand {
}
}
-impl BaseCommand for FundCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for FundCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for FundCommand {}
diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs
index 5669328..5acceb3 100644
--- a/crates/shirabe/src/command/global_command.rs
+++ b/crates/shirabe/src/command/global_command.rs
@@ -4,16 +4,12 @@ use std::path::Path;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::input::string_input::StringInput;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{LogicException, RuntimeException, chdir};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::factory::Factory;
@@ -23,58 +19,22 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct GlobalCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl GlobalCommand {
- pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) {
- let application = self.inner.get_application();
- if input.must_suggest_argument_values_for("command-name") {
- let names: Vec<String> = application
- .all()
- .into_iter()
- .filter(|cmd| !cmd.is_hidden())
- .filter_map(|cmd| cmd.get_name().map(|n| n.to_string()))
- .collect();
- suggestions.suggest_values(names);
- return;
- }
-
- let command_name = input
- .get_argument("command-name")
- .as_string()
- .unwrap_or("")
- .to_string();
- if application.has(&command_name) {
- let sub_input = self.prepare_subcommand_input(input.as_input_interface(), true);
- let sub_input = CompletionInput::from_string(&sub_input.to_string(), 2);
- let command = application.find(&command_name);
- command.merge_application_definition();
- sub_input.bind(command.get_definition());
- command.complete(&sub_input, suggestions);
- }
- }
+ // TODO(cli-completion): pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions)
pub fn configure(&mut self) {
- self.inner
- .set_name("global")
+ self.set_name("global")
.set_description("Allows running commands in the global composer dir ($COMPOSER_HOME)")
.set_definition(vec![
- InputArgument::new(
- "command-name",
- Some(InputArgument::REQUIRED),
- "",
- None,
- vec![],
- ),
+ InputArgument::new("command-name", Some(InputArgument::REQUIRED), "", None),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
])
.set_help(
@@ -105,11 +65,11 @@ impl GlobalCommand {
}
if args.len() < 2 {
- return self.inner.run(input, output);
+ return self.run(input, output);
}
let sub_input = self.prepare_subcommand_input(input, false)?;
- Ok(self.inner.get_application().run(&sub_input, output)?)
+ Ok(self.get_application().run(&sub_input, output)?)
}
fn prepare_subcommand_input(
@@ -125,7 +85,7 @@ impl GlobalCommand {
let home = config.get("home").as_string().unwrap_or("").to_string();
if !Path::new(&home).is_dir() {
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
fs.ensure_directory_exists(&home)?;
if !Path::new(&home).is_dir() {
return Err(RuntimeException {
@@ -142,7 +102,7 @@ impl GlobalCommand {
})?;
if !quiet {
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>Changed current directory to {}</info>",
home
));
@@ -154,7 +114,7 @@ impl GlobalCommand {
&input.to_string(),
1,
)?;
- self.inner.get_application().reset_composer();
+ self.get_application().reset_composer();
Ok(StringInput::new(new_input_str))
}
@@ -164,30 +124,12 @@ impl GlobalCommand {
}
}
-impl BaseCommand for GlobalCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for GlobalCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for GlobalCommand {}
diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs
index 8e9f007..9b92707 100644
--- a/crates/shirabe/src/command/home_command.rs
+++ b/crates/shirabe/src/command/home_command.rs
@@ -1,14 +1,11 @@
//! ref: composer/src/Composer/Command/HomeCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{FILTER_VALIDATE_URL, filter_var};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -22,26 +19,14 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct HomeCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for HomeCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl HomeCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("browse")
- .set_aliases(vec!["home".to_string()])
+ // TODO(cli-completion): suggest_installed_package() for `packages` argument
+ self.set_name("browse")
+ .set_aliases(&["home".to_string()])
.set_description("Opens the package's repository URL or homepage in your browser")
.set_definition(vec![
InputArgument::new(
@@ -49,7 +34,6 @@ impl HomeCommand {
Some(InputArgument::IS_ARRAY),
"Package(s) to browse to.",
None,
- self.suggest_installed_package(),
),
InputOption::new(
"homepage",
@@ -57,7 +41,6 @@ impl HomeCommand {
Some(InputOption::VALUE_NONE),
"Open the homepage instead of the repository URL.",
None,
- vec![],
),
InputOption::new(
"show",
@@ -65,7 +48,6 @@ impl HomeCommand {
Some(InputOption::VALUE_NONE),
"Only show the homepage or repository URL.",
None,
- vec![],
),
])
.set_help(
@@ -83,7 +65,7 @@ impl HomeCommand {
_output: &dyn OutputInterface,
) -> Result<i64> {
let repos = self.initialize_repos()?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut return_code: i64 = 0;
let packages: Vec<String> = input
@@ -99,8 +81,7 @@ impl HomeCommand {
let packages = if packages.is_empty() {
io.write_error("No package specified, opening homepage for the root package");
vec![
- self.inner
- .require_composer()?
+ self.require_composer(None, None)?
.get_package()
.get_name()
.to_string(),
@@ -177,7 +158,7 @@ impl HomeCommand {
}
if show_only {
- self.inner.get_io().write(&format!("<info>{}</info>", url));
+ self.get_io().write(&format!("<info>{}</info>", url));
} else {
self.open_browser(&url);
}
@@ -186,7 +167,7 @@ impl HomeCommand {
}
fn open_browser(&self, url: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut process = ProcessExecutor::new(io);
if Platform::is_windows() {
process.execute(&["start", "\"web\"", "explorer", url], None);
@@ -209,7 +190,7 @@ impl HomeCommand {
}
fn initialize_repos(&self) -> Result<Vec<Box<dyn RepositoryInterface>>> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
if let Some(composer) = composer {
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
@@ -226,35 +207,17 @@ impl HomeCommand {
}
Ok(RepositoryFactory::default_repos_with_default_manager(
- self.inner.get_io(),
+ self.get_io(),
))
}
}
-impl BaseCommand for HomeCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for HomeCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for HomeCommand {}
diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs
index 5ee4b67..04928ed 100644
--- a/crates/shirabe/src/command/init_command.rs
+++ b/crates/shirabe/src/command/init_command.rs
@@ -5,8 +5,6 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::helper::formatter_helper::FormatterHelper;
use shirabe_external_packages::symfony::component::console::input::array_input::ArrayInput;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
@@ -19,8 +17,7 @@ use shirabe_php_shim::{
strtolower, trim, ucwords,
};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::package_discovery_trait::PackageDiscoveryTrait;
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
@@ -38,24 +35,12 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct InitCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
/// @var array<string, string>
git_config: Option<IndexMap<String, String>>,
}
-impl CompletionTrait for InitCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl PackageDiscoveryTrait for InitCommand {
fn get_repos_mut(&mut self) -> &mut Option<CompositeRepository> {
todo!()
@@ -97,25 +82,22 @@ impl PackageDiscoveryTrait for InitCommand {
impl InitCommand {
pub fn configure(&mut self) {
- let suggest_available_package_incl_platform =
- self.suggest_available_package_incl_platform();
- let suggest_available_package_incl_platform2 =
- self.suggest_available_package_incl_platform();
- self.inner
+ // TODO(cli-completion): suggest_available_package_incl_platform() for `require` / `require-dev`
+ self
.set_name("init")
.set_description("Creates a basic composer.json file in current directory")
.set_definition(vec![
- InputOption::new("name", None, Some(InputOption::VALUE_REQUIRED), "Name of the package", None, vec![]),
- InputOption::new("description", None, Some(InputOption::VALUE_REQUIRED), "Description of package", None, vec![]),
- InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None, vec![]),
- InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None, vec![]),
- InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None, vec![]),
- InputOption::new("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform),
- InputOption::new("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform2),
- InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &array_keys(&BasePackage::stabilities()))), None, vec![]),
- InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None, vec![]),
- InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None, vec![]),
- InputOption::new("autoload", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_REQUIRED), "Add PSR-4 autoload mapping. Maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/)", None, vec![]),
+ InputOption::new("name", None, Some(InputOption::VALUE_REQUIRED), "Name of the package", None),
+ InputOption::new("description", None, Some(InputOption::VALUE_REQUIRED), "Description of package", None),
+ InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None),
+ InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None),
+ InputOption::new("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &array_keys(&BasePackage::stabilities()))), None),
+ InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None),
+ InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None),
+ InputOption::new("autoload", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_REQUIRED), "Add PSR-4 autoload mapping. Maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/)", None),
])
.set_help(
"The <info>init</info> command creates a basic composer.json file\n\
@@ -133,7 +115,7 @@ impl InitCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
let allowlist: Vec<String> = vec![
"name".to_string(),
@@ -239,7 +221,7 @@ impl InitCommand {
.collect()
})
.unwrap_or_default();
- let formatted = self.inner.format_requirements(req_list)?;
+ let formatted = self.format_requirements(req_list)?;
if formatted.is_empty() {
// PHP: new \stdClass — represented as an empty IndexMap (JSON object)
PhpMixed::Array(IndexMap::new())
@@ -267,7 +249,7 @@ impl InitCommand {
.collect()
})
.unwrap_or_default();
- let formatted = self.inner.format_requirements(req_list)?;
+ let formatted = self.format_requirements(req_list)?;
let value = if formatted.is_empty() {
PhpMixed::Array(IndexMap::new())
} else {
@@ -303,13 +285,13 @@ impl InitCommand {
options.insert("autoload".to_string(), PhpMixed::Array(autoload_obj));
}
- let file_obj = JsonFile::new(&Factory::get_composer_file(), None, None);
+ let file_obj = JsonFile::new(Factory::get_composer_file(), None, None)?;
let options_for_encode: IndexMap<String, Box<PhpMixed>> = options
.clone()
.into_iter()
.map(|(k, v)| (k, Box::new(v)))
.collect();
- let json = JsonFile::encode(&options_for_encode);
+ let json = JsonFile::encode(&options_for_encode, 448);
if input.is_interactive() {
io.write_error(
@@ -362,10 +344,11 @@ impl InitCommand {
true,
io_interface::NORMAL,
);
- Silencer::call(
- "unlink",
- &[PhpMixed::String(file_obj.get_path().to_string())],
- );
+ let path_to_unlink = file_obj.get_path().to_string();
+ let _ = Silencer::call(|| {
+ shirabe_php_shim::unlink(&path_to_unlink);
+ Ok::<(), anyhow::Error>(())
+ });
return Ok(1);
}
@@ -374,7 +357,7 @@ impl InitCommand {
// --autoload - Create src folder
if let Some(ref ap) = autoload_path {
- let filesystem = Filesystem::new();
+ let filesystem = Filesystem::new(None);
filesystem.ensure_directory_exists(ap);
// dump-autoload only for projects without added dependencies.
@@ -440,7 +423,7 @@ impl InitCommand {
}
pub(crate) fn initialize(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) {
- self.inner.initialize(input, output);
+ self.initialize(input, output);
if !input.is_interactive() {
if input.get_option("name").is_null() {
@@ -463,9 +446,9 @@ impl InitCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<()> {
- let io = self.inner.get_io();
+ let io = self.get_io();
// @var FormatterHelper $formatter
- let formatter: &FormatterHelper = self.inner.get_helper_set().get("formatter");
+ let formatter: &FormatterHelper = self.get_helper_set().get("formatter");
// initialize repos if configured
let repositories: Vec<String> = input
@@ -484,7 +467,7 @@ impl InitCommand {
let mut repos: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = vec![Box::new(PlatformRepository::new(vec![], PhpMixed::Null))];
+ > = vec![Box::new(PlatformRepository::new(vec![], IndexMap::new())?)];
let mut create_default_packagist_repo = true;
for repo in &repositories {
let repo_config =
@@ -977,7 +960,7 @@ impl InitCommand {
return self.git_config.clone().unwrap_or_default();
}
- let mut process = ProcessExecutor::new(self.inner.get_io());
+ let mut process = ProcessExecutor::new(self.get_io());
let mut output = String::new();
if process.execute(
@@ -1059,14 +1042,14 @@ impl InitCommand {
fn update_dependencies(&self, output: &dyn OutputInterface) {
// PHP try/catch: catch \Exception
- let result = self.inner.get_application().and_then(|app| {
+ let result = self.get_application().and_then(|app| {
let update_command = app.find("update")?;
app.reset_composer()?;
update_command.run(ArrayInput::new(IndexMap::new()), output)?;
Ok(())
});
if let Err(_e) = result {
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String(
"Could not update dependencies. Run `composer update` to see more information."
.to_string(),
@@ -1078,14 +1061,14 @@ impl InitCommand {
}
fn run_dump_autoload_command(&self, output: &dyn OutputInterface) {
- let result = self.inner.get_application().and_then(|app| {
+ let result = self.get_application().and_then(|app| {
let command = app.find("dump-autoload")?;
app.reset_composer()?;
command.run(ArrayInput::new(IndexMap::new()), output)?;
Ok(())
});
if let Err(_e) = result {
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String("Could not run dump-autoload.".to_string()),
true,
io_interface::NORMAL,
@@ -1206,30 +1189,12 @@ impl InitCommand {
}
}
-impl BaseCommand for InitCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for InitCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for InitCommand {}
diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs
index eac04c3..2b9a87d 100644
--- a/crates/shirabe/src/command/install_command.rs
+++ b/crates/shirabe/src/command/install_command.rs
@@ -1,16 +1,12 @@
//! ref: composer/src/Composer/Command/InstallCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::installer::Installer;
@@ -21,51 +17,39 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct InstallCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for InstallCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl InstallCommand {
pub fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_prefer_install() for `prefer-install` option
+ self
.set_name("install")
- .set_aliases(vec!["i".to_string()])
+ .set_aliases(&["i".to_string()])
.set_description("Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json")
.set_definition(vec![
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None, vec![]),
- InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None, vec![]),
- InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None, vec![]),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var). Only applies when no lock file is present.", None, vec![]),
- InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Do not use, only defined here to catch misuse of the install command.", None, vec![]),
- InputOption::new("audit", None, Some(InputOption::VALUE_NONE), "Run an audit after installation is complete.", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("verbose", Some(PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Shows more details including new commits pulled in when updating packages.", None, vec![]),
- InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Should not be provided, use composer require instead to add a given package to composer.json.", None, vec![]),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None),
+ InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var). Only applies when no lock file is present.", None),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Do not use, only defined here to catch misuse of the install command.", None),
+ InputOption::new("audit", None, Some(InputOption::VALUE_NONE), "Run an audit after installation is complete.", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("verbose", Some(PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Shows more details including new commits pulled in when updating packages.", None),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Should not be provided, use composer require instead to add a given package to composer.json.", None),
])
.set_help(
"The <info>install</info> command reads the composer.lock file from\n\
@@ -78,7 +62,7 @@ impl InstallCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("dev").as_bool().unwrap_or(false) {
io.write_error("<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>");
@@ -110,7 +94,7 @@ impl InstallCommand {
return Ok(1);
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if !composer.get_locker().is_locked() && !HttpDownloader::is_curl_enabled() {
io.write_error("<warning>Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.</warning>");
@@ -120,20 +104,19 @@ impl InstallCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"install".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let install = Installer::create(io, &composer);
let config = composer.get_config();
- let (prefer_source, prefer_dist) =
- self.inner.get_preferred_install_options(config, input)?;
+ let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
let optimize = input
.get_option("optimize-autoloader")
@@ -174,11 +157,8 @@ impl InstallCommand {
.set_optimize_autoloader(optimize)
.set_class_map_authoritative(authoritative)
.set_apcu_autoloader(apcu, apcu_prefix.as_deref())
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?)
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input)?)
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_error_on_audit(input.get_option("audit").as_bool().unwrap_or(false));
if input.get_option("no-plugins").as_bool().unwrap_or(false) {
@@ -189,30 +169,12 @@ impl InstallCommand {
}
}
-impl BaseCommand for InstallCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
+impl HasBaseCommandData for InstallCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for InstallCommand {}
diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs
index 4fa2e72..102221e 100644
--- a/crates/shirabe/src/command/licenses_command.rs
+++ b/crates/shirabe/src/command/licenses_command.rs
@@ -4,8 +4,6 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::helper::table::Table;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
@@ -13,7 +11,7 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
use shirabe_external_packages::symfony::console::style::symfony_style::SymfonyStyle;
use shirabe_php_shim::{PhpMixed, RuntimeException, UnexpectedValueException};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -28,15 +26,12 @@ use crate::util::package_sorter::PackageSorter;
#[derive(Debug)]
pub struct LicensesCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl LicensesCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("licenses")
+ self.set_name("licenses")
.set_description("Shows information about licenses of dependencies")
.set_definition(vec![
InputOption::new(
@@ -45,11 +40,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_REQUIRED),
"Format of the output: text, json or summary",
Some(PhpMixed::String("text".to_string())),
- vec![
- "text".to_string(),
- "json".to_string(),
- "summary".to_string(),
- ],
),
InputOption::new(
"no-dev",
@@ -57,7 +47,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_NONE),
"Disables search in require-dev packages.",
None,
- vec![],
),
InputOption::new(
"locked",
@@ -65,7 +54,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_NONE),
"Shows licenses from the lock file instead of installed packages.",
None,
- vec![],
),
])
.set_help(
@@ -78,14 +66,14 @@ impl LicensesCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch COMMAND event for plugin hooks
let command_event =
CommandEvent::new(PluginEvents::COMMAND, "licenses".to_string(), input, output);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let root = composer.get_package();
@@ -110,7 +98,7 @@ impl LicensesCommand {
};
let packages = PackageSorter::sort_packages_alphabetically(packages);
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -238,7 +226,7 @@ impl LicensesCommand {
.collect(),
),
);
- io.write(&JsonFile::encode(&output_map));
+ io.write(&JsonFile::encode(&output_map, 448));
}
"summary" => {
let mut used_licenses: IndexMap<String, i64> = IndexMap::new();
@@ -288,30 +276,12 @@ impl LicensesCommand {
}
}
-impl BaseCommand for LicensesCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for LicensesCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for LicensesCommand {}
diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs
index d11d290..bac15cf 100644
--- a/crates/shirabe/src/command/outdated_command.rs
+++ b/crates/shirabe/src/command/outdated_command.rs
@@ -1,15 +1,12 @@
//! ref: composer/src/Composer/Command/OutdatedCommand.php
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::array_input::ArrayInput;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
@@ -17,44 +14,31 @@ use shirabe_php_shim::PhpMixed;
#[derive(Debug)]
pub struct OutdatedCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for OutdatedCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl OutdatedCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.suggest_installed_package(false, false);
- let suggest_installed_package_for_ignore = self.suggest_installed_package(false, false);
- self.inner
+ // TODO(cli-completion): suggest_installed_package(false, false) for `package` argument and `--ignore` option
+ self
.set_name("outdated")
.set_description("Shows a list of installed packages that have updates available, including their latest version")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None, suggest_installed_package),
- InputOption::new("outdated", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that are outdated (this is the default, but present here for compat with `show`", None, vec![]),
- InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None, vec![]),
- InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows updates for packages from the lock file, regardless of what is currently in vendor dir", None, vec![]),
- InputOption::new("direct", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Shows only packages that are directly required by the root package", None, vec![]),
- InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code when there are outdated packages", None, vec![]),
- InputOption::new("major-only", Some(PhpMixed::String("M".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have major SemVer-compatible updates.", None, vec![]),
- InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None, vec![]),
- InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None, vec![]),
- InputOption::new("sort-by-age", Some(PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Displays the installed version's age, and sorts packages oldest first.", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
- InputOption::new("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None, suggest_installed_package_for_ignore),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages). Use with the --outdated option", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None),
+ InputOption::new("outdated", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that are outdated (this is the default, but present here for compat with `show`", None),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None),
+ InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows updates for packages from the lock file, regardless of what is currently in vendor dir", None),
+ InputOption::new("direct", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Shows only packages that are directly required by the root package", None),
+ InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code when there are outdated packages", None),
+ InputOption::new("major-only", Some(PhpMixed::String("M".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have major SemVer-compatible updates.", None),
+ InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None),
+ InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None),
+ InputOption::new("sort-by-age", Some(PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Displays the installed version's age, and sorts packages oldest first.", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string()))),
+ InputOption::new("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages). Use with the --outdated option", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None),
])
.set_help(
"The outdated command is just a proxy for `composer show -l`\n\n\
@@ -140,7 +124,9 @@ impl OutdatedCommand {
let input = ArrayInput::new(args);
- self.inner.get_application().run(&input, output)
+ // TODO(phase-b): convert ArrayInput/output references to dyn trait objects expected by Application::run
+ let _ = input;
+ self.get_application()?.run(None, None)
}
pub fn is_proxy_command(&self) -> bool {
@@ -148,30 +134,12 @@ impl OutdatedCommand {
}
}
-impl BaseCommand for OutdatedCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
+impl HasBaseCommandData for OutdatedCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for OutdatedCommand {}
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 164cc53..501ae5d 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -28,7 +28,7 @@ use crate::package::version::version_selector::VersionSelector;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_factory::RepositoryFactory;
-use crate::repository::repository_interface::SearchResult;
+use crate::repository::repository_interface::{RepositoryInterface, SearchResult};
use crate::repository::repository_set::RepositorySet;
use crate::util::filesystem::Filesystem;
@@ -161,12 +161,12 @@ pub trait PackageDiscoveryTrait {
requirement.get("version").map(|s| s.as_str()).unwrap_or(""),
)
{
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>The \"{}\" constraint for \"{}\" appears too strict and will likely not match what you want. See https://getcomposer.org/constraints</warning>",
requirement.get("version").map(|s| s.as_str()).unwrap_or(""),
requirement.get("name").map(|s| s.as_str()).unwrap_or(""),
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -188,8 +188,8 @@ pub trait PackageDiscoveryTrait {
if use_best_version_constraint {
requirement.insert("version".to_string(), version.clone());
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
PhpMixed::String(version),
@@ -197,7 +197,7 @@ pub trait PackageDiscoveryTrait {
requirement.get("name").cloned().unwrap_or_default(),
),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -219,7 +219,7 @@ pub trait PackageDiscoveryTrait {
let version_parser = VersionParser::new();
// Collect existing packages
- let composer = self.try_composer();
+ let composer = self.try_composer(None, None);
let mut installed_repo: Option<_> = None;
if let Some(c) = &composer {
installed_repo = Some(c.get_repository_manager().get_local_repository());
@@ -319,33 +319,24 @@ pub trait PackageDiscoveryTrait {
));
}
- io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(String::new())),
- Box::new(PhpMixed::String(sprintf(
- "Found <info>%s</info> packages matching <info>%s</info>",
- &[
- PhpMixed::Int(matches.len() as i64),
- PhpMixed::String(package.clone()),
- ],
- ))),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
-
- io.write_error(
- PhpMixed::List(
- choices
- .iter()
- .map(|s| Box::new(PhpMixed::String(s.clone())))
- .collect(),
+ io.write_error3("", true, io_interface::NORMAL);
+ io.write_error3(
+ &sprintf(
+ "Found <info>%s</info> packages matching <info>%s</info>",
+ &[
+ PhpMixed::Int(matches.len() as i64),
+ PhpMixed::String(package.clone()),
+ ],
),
true,
io_interface::NORMAL,
);
- io.write_error(PhpMixed::String(String::new()), true, io_interface::NORMAL);
+ io.write_error3("", true, io_interface::NORMAL);
+
+ for choice in &choices {
+ io.write_error3(choice, true, io_interface::NORMAL);
+ }
+ io.write_error3("", true, io_interface::NORMAL);
let matches_clone = matches.clone();
let version_parser_clone = version_parser.clone();
@@ -432,14 +423,14 @@ pub trait PackageDiscoveryTrait {
fixed,
)?;
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
PhpMixed::String(c.clone()),
PhpMixed::String(package.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs
index 6add126..26c17f2 100644
--- a/crates/shirabe/src/command/prohibits_command.rs
+++ b/crates/shirabe/src/command/prohibits_command.rs
@@ -1,12 +1,7 @@
//! ref: composer/src/Composer/Command/ProhibitsCommand.php
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_dependency_command::BaseDependencyCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -15,62 +10,50 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
#[derive(Debug)]
pub struct ProhibitsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
colors: Vec<String>,
}
-impl CompletionTrait for ProhibitsCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl ProhibitsCommand {
pub fn configure(&mut self) {
- let package_suggestions = self.suggest_available_package();
- self.inner
- .set_name("prohibits")
- .set_aliases(vec!["why-not".to_string()])
+ // TODO(cli-completion): suggest_available_package() for `package` argument
+ self.set_name("prohibits")
+ .set_aliases(&["why-not".to_string()])
.set_description("Shows which packages prevent the given package from being installed")
.set_definition(vec![
InputArgument::new(
BaseDependencyCommand::ARGUMENT_PACKAGE,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- package_suggestions,
),
InputArgument::new(
BaseDependencyCommand::ARGUMENT_CONSTRAINT,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Version constraint, which version you expected to be installed",
None,
- None,
),
InputOption::new(
BaseDependencyCommand::OPTION_RECURSIVE,
- Some("r"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
+ Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
+ None,
),
InputOption::new(
BaseDependencyCommand::OPTION_TREE,
- Some("t"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
+ Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
+ None,
),
InputOption::new(
"locked",
None,
- InputOption::VALUE_NONE,
+ Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
+ None,
),
])
.set_help(
@@ -81,33 +64,9 @@ impl ProhibitsCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
- self.inner.do_execute(input, output, true)
- }
-}
-
-impl BaseCommand for ProhibitsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ // TODO(phase-b): wire `do_execute` from BaseDependencyCommand trait
+ let _ = (input, output);
+ todo!()
}
}
@@ -121,4 +80,12 @@ impl BaseDependencyCommand for ProhibitsCommand {
}
}
-impl Command for ProhibitsCommand {}
+impl HasBaseCommandData for ProhibitsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs
index d16b001..7e703c4 100644
--- a/crates/shirabe/src/command/reinstall_command.rs
+++ b/crates/shirabe/src/command/reinstall_command.rs
@@ -4,14 +4,11 @@ use std::any::Any;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::InvalidArgumentException;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -29,43 +26,29 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct ReinstallCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for ReinstallCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl ReinstallCommand {
pub fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- let suggest_installed_package_types = self.suggest_installed_package_types(false);
- let suggest_installed_package = self.suggest_installed_package(false);
- self.inner
+ // TODO(cli-completion): suggest_prefer_install / suggest_installed_package_types / suggest_installed_package
+ self
.set_name("reinstall")
.set_description("Uninstalls and reinstalls the given package names")
.set_definition(vec![
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(shirabe_php_shim::PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None, suggest_installed_package_types),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None, suggest_installed_package),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(shirabe_php_shim::PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None),
])
.set_help(
"The <info>reinstall</info> command looks up installed packages by name,\n\
@@ -78,9 +61,9 @@ impl ReinstallCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let local_repo = composer.get_repository_manager().get_local_repository();
let mut packages_to_reinstall: Vec<
@@ -198,17 +181,16 @@ impl ReinstallCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"reinstall".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_dispatcher = composer.get_event_dispatcher();
- event_dispatcher.dispatch(command_event.get_name(), &command_event);
+ event_dispatcher.dispatch(Some(command_event.get_name()), None);
let config = composer.get_config();
- let (prefer_source, prefer_dist) =
- self.inner.get_preferred_install_options(config, input)?;
+ let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
let installation_manager = composer.get_installation_manager();
let download_manager = composer.get_download_manager();
@@ -220,13 +202,20 @@ impl ReinstallCommand {
installation_manager.disable_plugins();
}
- download_manager.set_prefer_source(prefer_source);
- download_manager.set_prefer_dist(prefer_dist);
+ download_manager
+ .borrow_mut()
+ .set_prefer_source(prefer_source);
+ download_manager.borrow_mut().set_prefer_dist(prefer_dist);
let dev_mode = local_repo.get_dev_mode().unwrap_or(true);
Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" });
- event_dispatcher.dispatch_script(ScriptEvents::PRE_INSTALL_CMD, dev_mode);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::PRE_INSTALL_CMD,
+ dev_mode,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
installation_manager.execute(local_repo, uninstall_operations, dev_mode);
installation_manager.execute(local_repo, install_operations, dev_mode);
@@ -259,9 +248,7 @@ impl ReinstallCommand {
let generator = composer.get_autoload_generator();
generator.set_class_map_authoritative(authoritative);
generator.set_apcu(apcu, apcu_prefix.as_deref());
- generator.set_platform_requirement_filter(
- self.inner.get_platform_requirement_filter(input)?,
- );
+ generator.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?);
generator.dump(
config,
local_repo,
@@ -274,36 +261,23 @@ impl ReinstallCommand {
);
}
- event_dispatcher.dispatch_script(ScriptEvents::POST_INSTALL_CMD, dev_mode);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::POST_INSTALL_CMD,
+ dev_mode,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
Ok(0)
}
}
-impl BaseCommand for ReinstallCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ReinstallCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ReinstallCommand {}
diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs
index c9e967b..53bcdc9 100644
--- a/crates/shirabe/src/command/remove_command.rs
+++ b/crates/shirabe/src/command/remove_command.rs
@@ -2,17 +2,15 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::exception::invalid_argument_exception::InvalidArgumentException;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, UnexpectedValueException, array_map, strtolower};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -23,20 +21,19 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::package::base_package;
use crate::package::base_package::BasePackage;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
#[derive(Debug)]
pub struct RemoveCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl RemoveCommand {
pub fn configure(&mut self) {
- let suggest_root_requirement = self.suggest_root_requirement();
- self.inner
+ // TODO(cli-completion): suggest_root_requirement() for `packages` argument
+ self
.set_name("remove")
- .set_aliases(vec!["rm".to_string(), "uninstall".to_string()])
+ .set_aliases(&["rm".to_string(), "uninstall".to_string()])
.set_description("Removes a package from the require or require-dev")
.set_definition(vec![
InputArgument::new(
@@ -44,7 +41,6 @@ impl RemoveCommand {
Some(InputArgument::IS_ARRAY),
"Packages that should be removed.",
None,
- suggest_root_requirement,
),
InputOption::new(
"dev",
@@ -52,7 +48,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Removes a package from the require-dev section.",
None,
- vec![],
),
InputOption::new(
"dry-run",
@@ -60,7 +55,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Outputs the operations but will not execute anything (implicitly enables --verbose).",
None,
- vec![],
),
InputOption::new(
"no-progress",
@@ -68,7 +62,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Do not output download progress.",
None,
- vec![],
),
InputOption::new(
"no-update",
@@ -76,7 +69,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Disables the automatic update of the dependencies (implies --no-install).",
None,
- vec![],
),
InputOption::new(
"no-install",
@@ -84,7 +76,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Skip the install step after updating the composer.lock file.",
None,
- vec![],
),
InputOption::new(
"no-audit",
@@ -92,7 +83,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).",
None,
- vec![],
),
InputOption::new(
"audit-format",
@@ -100,7 +90,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED),
"Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".",
Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())),
- Auditor::FORMATS.to_vec(),
),
InputOption::new(
"no-security-blocking",
@@ -108,7 +97,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).",
None,
- vec![],
),
InputOption::new(
"update-no-dev",
@@ -116,7 +104,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Run the dependency update with the --no-dev option.",
None,
- vec![],
),
InputOption::new(
"update-with-dependencies",
@@ -124,7 +111,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows inherited dependencies to be updated with explicit dependencies (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var). (Deprecated, is now default behavior)",
None,
- vec![],
),
InputOption::new(
"update-with-all-dependencies",
@@ -132,7 +118,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).",
None,
- vec![],
),
InputOption::new(
"with-all-dependencies",
@@ -140,7 +125,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Alias for --update-with-all-dependencies",
None,
- vec![],
),
InputOption::new(
"no-update-with-dependencies",
@@ -148,7 +132,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Does not allow inherited dependencies to be updated with explicit dependencies.",
None,
- vec![],
),
InputOption::new(
"minimal-changes",
@@ -156,7 +139,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).",
None,
- vec![],
),
InputOption::new(
"unused",
@@ -164,7 +146,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Remove all packages which are locked but not required by any other package.",
None,
- vec![],
),
InputOption::new(
"ignore-platform-req",
@@ -172,7 +153,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY),
"Ignore a specific platform requirement (php & ext- packages).",
None,
- vec![],
),
InputOption::new(
"ignore-platform-reqs",
@@ -180,7 +160,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Ignore all platform requirements (php & ext- packages).",
None,
- vec![],
),
InputOption::new(
"optimize-autoloader",
@@ -188,7 +167,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Optimize autoloader during autoloader dump",
None,
- vec![],
),
InputOption::new(
"classmap-authoritative",
@@ -196,7 +174,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.",
None,
- vec![],
),
InputOption::new(
"apcu-autoloader",
@@ -204,7 +181,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Use APCu to cache found/not-found classes.",
None,
- vec![],
),
InputOption::new(
"apcu-autoloader-prefix",
@@ -212,7 +188,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED),
"Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader",
None,
- vec![],
),
])
.set_help(
@@ -252,7 +227,7 @@ impl RemoveCommand {
.unwrap_or_default();
if input.get_option("unused").as_bool().unwrap_or(false) {
- let composer = self.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let locker = composer.get_locker();
if !locker.is_locked() {
return Err(anyhow::anyhow!(UnexpectedValueException {
@@ -263,7 +238,7 @@ impl RemoveCommand {
}));
}
- let locked_packages = locker.get_locked_repository()?.get_packages();
+ let locked_packages = locker.get_locked_repository(true)?.get_packages();
let mut required: IndexMap<String, bool> = IndexMap::new();
for link in composer
@@ -312,13 +287,14 @@ impl RemoveCommand {
}
}
- let file = Factory::get_composer_file();
+ let file = Factory::get_composer_file()?;
- let json_file = JsonFile::new(&file, None, None);
+ let json_file = JsonFile::new(file.clone(), None, None)?;
let composer_data = json_file.read()?;
let composer_backup = std::fs::read_to_string(json_file.get_path())?;
- let json = JsonConfigSource::new(&json_file);
+ let json_file_for_source = JsonFile::new(file, None, None)?;
+ let json = JsonConfigSource::new(json_file_for_source, false);
let r#type = if input.get_option("dev").as_bool().unwrap_or(false) {
"require-dev"
@@ -484,14 +460,14 @@ impl RemoveCommand {
}
// TODO(plugin): deactivate installed plugins
- if let Some(composer_opt) = self.try_composer() {
+ if let Some(composer_opt) = self.try_composer(None, None) {
composer_opt
.get_plugin_manager()
.deactivate_installed_plugins();
}
self.reset_composer();
- let composer = self.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if dry_run {
let root_package = composer.get_package();
@@ -680,30 +656,12 @@ impl RemoveCommand {
}
}
-impl BaseCommand for RemoveCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for RemoveCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RemoveCommand {}
diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs
index 79b4a15..2aea141 100644
--- a/crates/shirabe/src/command/repository_command.rs
+++ b/crates/shirabe/src/command/repository_command.rs
@@ -2,19 +2,17 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
InvalidArgumentException, PHP_URL_HOST, PhpMixed, RuntimeException, parse_url, strtolower,
};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_config_command::BaseConfigCommand;
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -24,9 +22,7 @@ use crate::json::json_file::JsonFile;
#[derive(Debug)]
pub struct RepositoryCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
config: Option<Config>,
config_file: Option<JsonFile>,
@@ -35,24 +31,21 @@ pub struct RepositoryCommand {
impl RepositoryCommand {
pub fn configure(&mut self) {
- let suggest_repo_names_before = self.suggest_repo_names();
- let suggest_repo_names_after = self.suggest_repo_names();
- let suggest_repo_names_name = self.suggest_repo_names();
- let suggest_type_for_add = Self::suggest_type_for_add();
- self.inner.inner
+ // TODO(cli-completion): suggest_repo_names() / suggest_type_for_add()
+ self
.set_name("repository")
- .set_aliases(vec!["repo".to_string()])
+ .set_aliases(&["repo".to_string()])
.set_description("Manages repositories")
.set_definition(vec![
- InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None, vec![]),
- InputOption::new("file", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None, vec![]),
- InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lower priority) instead of prepending it", None, vec![]),
- InputOption::new("before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None, suggest_repo_names_before),
- InputOption::new("after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None, suggest_repo_names_after),
- InputArgument::new("action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string())), vec!["list", "add", "remove", "set-url", "get-url", "enable", "disable"]),
- InputArgument::new("name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None, suggest_repo_names_name),
- InputArgument::new("arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None, suggest_type_for_add),
- InputArgument::new("arg2", Some(InputArgument::OPTIONAL), "URL for add (if not using JSON)", None, vec![]),
+ InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None),
+ InputOption::new("file", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None),
+ InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lower priority) instead of prepending it", None),
+ InputOption::new("before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None),
+ InputOption::new("after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None),
+ InputArgument::new("action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string()))),
+ InputArgument::new("name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None),
+ InputArgument::new("arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None),
+ InputArgument::new("arg2", Some(InputArgument::OPTIONAL), "URL for add (if not using JSON)", None),
])
.set_help(
"This command lets you manage repositories in your composer.json.\n\n\
@@ -98,7 +91,7 @@ impl RepositoryCommand {
.as_string()
.map(|s| s.to_string());
- let config_data = self.inner.config_file.as_ref().unwrap().read()?;
+ let config_data = self.config_file.as_ref().unwrap().read()?;
let config_file_path = self
.inner
.config_file
@@ -106,12 +99,11 @@ impl RepositoryCommand {
.unwrap()
.get_path()
.to_string();
- self.inner
- .config
+ self.config
.as_mut()
.unwrap()
.merge(config_data, &config_file_path);
- let repos = self.inner.config.as_ref().unwrap().get_repositories();
+ let repos = self.config.as_ref().unwrap().get_repositories();
match action.as_str() {
"list" | "ls" | "show" => {
@@ -173,21 +165,17 @@ impl RepositoryCommand {
}
let reference_name = before.as_deref().or(after.as_deref()).unwrap();
let offset: i64 = if after.is_some() { 1 } else { 0 };
- self.inner
- .config_source
- .as_mut()
- .unwrap()
- .insert_repository(
- name.as_deref().unwrap(),
- repo_config,
- reference_name,
- offset,
- );
+ self.config_source.as_mut().unwrap().insert_repository(
+ name.as_deref().unwrap(),
+ repo_config,
+ reference_name,
+ offset,
+ );
return Ok(0);
}
let append = input.get_option("append").as_bool().unwrap_or(false);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
name.as_deref().unwrap(),
repo_config,
append,
@@ -202,13 +190,12 @@ impl RepositoryCommand {
}));
}
let name_str = name.as_deref().unwrap();
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository(name_str);
if ["packagist", "packagist.org"].contains(&name_str) {
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
"packagist.org",
PhpMixed::Bool(false),
false,
@@ -223,8 +210,7 @@ impl RepositoryCommand {
code: 0,
}));
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.set_repository_url(name.as_deref().unwrap(), arg1.as_deref().unwrap());
@@ -242,7 +228,7 @@ impl RepositoryCommand {
if let PhpMixed::Array(ref repo_map) = *repo {
let url = repo_map.get("url").and_then(|v| v.as_string());
if let Some(url) = url {
- self.inner.inner.get_io().write(url);
+ self.get_io().write(url);
return Ok(0);
}
return Err(anyhow::anyhow!(InvalidArgumentException {
@@ -257,7 +243,7 @@ impl RepositoryCommand {
if n == name_str {
let url = repo_map.get("url").and_then(|v| v.as_string());
if let Some(url) = url {
- self.inner.inner.get_io().write(url);
+ self.get_io().write(url);
return Ok(0);
}
return Err(anyhow::anyhow!(InvalidArgumentException {
@@ -286,7 +272,7 @@ impl RepositoryCommand {
let name_str = name.as_deref().unwrap();
if ["packagist", "packagist.org"].contains(&name_str) {
let append = input.get_option("append").as_bool().unwrap_or(false);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
"packagist.org",
PhpMixed::Bool(false),
append,
@@ -307,8 +293,7 @@ impl RepositoryCommand {
}
let name_str = name.as_deref().unwrap();
if ["packagist", "packagist.org"].contains(&name_str) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository("packagist.org");
@@ -331,7 +316,7 @@ impl RepositoryCommand {
}
fn list_repositories(&self, mut repos: IndexMap<String, PhpMixed>) {
- let io = self.inner.inner.get_io();
+ let io = self.get_io();
let mut packagist_present = false;
for (_key, repo) in &repos {
@@ -396,84 +381,14 @@ impl RepositoryCommand {
.get("url")
.and_then(|v| v.as_string())
.map(|s| s.to_string())
- .unwrap_or_else(|| JsonFile::encode(repo));
+ .unwrap_or_else(|| JsonFile::encode(repo, 448));
io.write(&format!("[{}] <info>{}</info> {}", name, r#type, url));
}
}
}
- fn suggest_type_for_add() -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- Box::new(|input: &CompletionInput| {
- if input.get_argument("action").as_string() == Some("add") {
- vec![
- "composer".to_string(),
- "vcs".to_string(),
- "artifact".to_string(),
- "path".to_string(),
- ]
- } else {
- vec![]
- }
- })
- }
-
- fn suggest_repo_names(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| {
- let action = input
- .get_argument("action")
- .as_string()
- .unwrap_or("")
- .to_string();
- if ["enable", "disable"].contains(&action.as_str()) {
- return vec!["packagist.org".to_string()];
- }
- if !["remove", "set-url", "get-url"].contains(&action.as_str()) {
- return vec![];
- }
- let config = Factory::create_config(None, None).unwrap();
- let config_file_path = self.inner.get_composer_config_file(input, &config);
- let config_file = JsonFile::new(config_file_path, None, None);
- let data = config_file.read().unwrap_or_default();
- let mut repos = vec![];
- if let Some(repositories) = data.get("repositories").and_then(|v| v.as_list()) {
- for repo in repositories {
- if let PhpMixed::Array(ref repo_map) = **repo {
- if let Some(name) = repo_map.get("name").and_then(|v| v.as_string()) {
- repos.push(name.to_string());
- }
- }
- }
- }
- repos.sort();
- repos
- })
- }
-}
-
-impl BaseCommand for RepositoryCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
+ // TODO(cli-completion): fn suggest_type_for_add()
+ // TODO(cli-completion): fn suggest_repo_names(&self)
}
impl BaseConfigCommand for RepositoryCommand {
@@ -502,4 +417,12 @@ impl BaseConfigCommand for RepositoryCommand {
}
}
-impl Command for RepositoryCommand {}
+impl HasBaseCommandData for RepositoryCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 8031fbc..63efa92 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -5,8 +5,6 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
@@ -17,8 +15,7 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::package_discovery_trait::PackageDiscoveryTrait;
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
@@ -49,9 +46,7 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct RequireCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
newly_created: bool,
first_require: bool,
@@ -65,16 +60,6 @@ pub struct RequireCommand {
dependency_resolution_completed: bool,
}
-impl CompletionTrait for RequireCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl PackageDiscoveryTrait for RequireCommand {
fn get_repos_mut(&mut self) -> &mut Option<CompositeRepository> {
todo!()
@@ -114,43 +99,41 @@ impl PackageDiscoveryTrait for RequireCommand {
impl RequireCommand {
pub fn configure(&mut self) {
- let suggest_available_package_incl_platform =
- self.suggest_available_package_incl_platform();
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_available_package_incl_platform / suggest_prefer_install
+ self
.set_name("require")
- .set_aliases(vec!["r".to_string()])
+ .set_aliases(&["r".to_string()])
.set_description("Adds required packages to your composer.json and installs them")
.set_definition(vec![
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None, vec![]),
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None, vec![]),
- InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-update", None, Some(InputOption::VALUE_NONE), "Disables the automatic update of the dependencies (implies --no-install).", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Skip the install step after updating the composer.lock file.", None, vec![]),
- InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.to_vec()),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None, vec![]),
- InputOption::new("update-no-dev", None, Some(InputOption::VALUE_NONE), "Run the dependency update with the --no-dev option.", None, vec![]),
- InputOption::new("update-with-dependencies", Some(PhpMixed::String("w".to_string())), Some(InputOption::VALUE_NONE), "Allows inherited dependencies to be updated, except those that are root requirements (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var).", None, vec![]),
- InputOption::new("update-with-all-dependencies", Some(PhpMixed::String("W".to_string())), Some(InputOption::VALUE_NONE), "Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).", None, vec![]),
- InputOption::new("with-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-dependencies", None, vec![]),
- InputOption::new("with-all-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-all-dependencies", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("prefer-stable", None, Some(InputOption::VALUE_NONE), "Prefer stable versions of dependencies (can also be set via the COMPOSER_PREFER_STABLE=1 env var).", None, vec![]),
- InputOption::new("prefer-lowest", None, Some(InputOption::VALUE_NONE), "Prefer lowest versions of dependencies (can also be set via the COMPOSER_PREFER_LOWEST=1 env var).", None, vec![]),
- InputOption::new("minimal-changes", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).", None, vec![]),
- InputOption::new("sort-packages", None, Some(InputOption::VALUE_NONE), "Sorts packages when adding/updating a new dependency", None, vec![]),
- InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-update", None, Some(InputOption::VALUE_NONE), "Disables the automatic update of the dependencies (implies --no-install).", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Skip the install step after updating the composer.lock file.", None),
+ InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None),
+ InputOption::new("update-no-dev", None, Some(InputOption::VALUE_NONE), "Run the dependency update with the --no-dev option.", None),
+ InputOption::new("update-with-dependencies", Some(PhpMixed::String("w".to_string())), Some(InputOption::VALUE_NONE), "Allows inherited dependencies to be updated, except those that are root requirements (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var).", None),
+ InputOption::new("update-with-all-dependencies", Some(PhpMixed::String("W".to_string())), Some(InputOption::VALUE_NONE), "Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).", None),
+ InputOption::new("with-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-dependencies", None),
+ InputOption::new("with-all-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-all-dependencies", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("prefer-stable", None, Some(InputOption::VALUE_NONE), "Prefer stable versions of dependencies (can also be set via the COMPOSER_PREFER_STABLE=1 env var).", None),
+ InputOption::new("prefer-lowest", None, Some(InputOption::VALUE_NONE), "Prefer lowest versions of dependencies (can also be set via the COMPOSER_PREFER_LOWEST=1 env var).", None),
+ InputOption::new("minimal-changes", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).", None),
+ InputOption::new("sort-packages", None, Some(InputOption::VALUE_NONE), "Sorts packages when adding/updating a new dependency", None),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
])
.set_help(
"The require command adds required packages to your composer.json and installs them.\n\
@@ -173,7 +156,7 @@ impl RequireCommand {
output: &dyn OutputInterface,
) -> Result<i64> {
self.file = Factory::get_composer_file();
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("no-suggest").as_bool().unwrap_or(false) {
io.write_error(
@@ -213,7 +196,7 @@ impl RequireCommand {
file_put_contents(&self.file, "{\n}\n");
}
- self.json = Some(JsonFile::new(&self.file, None, None));
+ self.json = Some(JsonFile::new(self.file.clone(), None, None)?);
self.lock = Factory::get_lock_file(&self.file);
self.composer_backup =
file_get_contents(self.json.as_ref().unwrap().get_path()).unwrap_or_default();
@@ -241,15 +224,14 @@ impl RequireCommand {
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
+ let file_path = self.file.clone();
+ let backup_contents = self.composer_backup.clone();
if !is_writable(&self.file)
- && Silencer::call(
- "file_put_contents",
- &[
- PhpMixed::String(self.file.clone()),
- PhpMixed::String(self.composer_backup.clone()),
- ],
- )
- .as_bool()
+ && Silencer::call(|| {
+ shirabe_php_shim::file_put_contents(&file_path, backup_contents.as_bytes());
+ Ok::<bool, anyhow::Error>(false)
+ })
+ .ok()
== Some(false)
{
io.write_error(
@@ -300,7 +282,7 @@ impl RequireCommand {
}
}
- let composer = self.inner.require_composer(None, None)?;
+ let composer = self.require_composer(None, None)?;
let repos = composer.get_repository_manager().get_repositories();
let platform_overrides = composer.get_config().get("platform");
@@ -359,7 +341,7 @@ impl RequireCommand {
}
};
- let mut requirements = self.inner.format_requirements(requirements)?;
+ let mut requirements = self.format_requirements(requirements)?;
if !input.get_option("dev").as_bool().unwrap_or(false)
&& io.is_interactive()
@@ -726,8 +708,8 @@ impl RequireCommand {
_remove_key: &str,
) -> Result<i64> {
// Update packages
- self.inner.reset_composer()?;
- let composer = self.inner.require_composer(None, None)?;
+ self.reset_composer()?;
+ let composer = self.require_composer(None, None)?;
self.dependency_resolution_completed = false;
composer.get_event_dispatcher().add_listener(
@@ -869,7 +851,7 @@ impl RequireCommand {
let command_event = CommandEvent::new(PluginEvents::COMMAND, "require", input, output);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
composer
.get_installation_manager()
@@ -893,13 +875,10 @@ impl RequireCommand {
.set_update(true)
.set_install(!input.get_option("no-install").as_bool().unwrap_or(false))
.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?)
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input)?)
.set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false))
.set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false))
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_minimal_update(minimal_changes);
// if no lock is present, or the file is brand new, we do not do a
@@ -915,7 +894,7 @@ impl RequireCommand {
let status = install.run()?;
if status != 0 && status != Installer::ERROR_AUDIT_FAILED {
if status == Installer::ERROR_DEPENDENCY_RESOLUTION_FAILED {
- for req in self.inner.normalize_requirements(
+ for req in self.normalize_requirements(
input
.get_argument("packages")
.as_list()
@@ -956,7 +935,7 @@ impl RequireCommand {
dry_run: bool,
fixed: bool,
) -> Result<i64> {
- let composer = self.inner.require_composer(None, None)?;
+ let composer = self.require_composer(None, None)?;
let locker = composer.get_locker();
let mut requirements: IndexMap<String, String> = IndexMap::new();
let version_selector = VersionSelector::new(RepositorySet::new(None, None), None);
@@ -990,7 +969,7 @@ impl RequireCommand {
version_selector.find_recommended_require_version(&*package),
);
}
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String(sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
@@ -1013,12 +992,12 @@ impl RequireCommand {
)
.unwrap_or(false)
{
- self.inner.get_io().warning(format!(
+ self.get_io().warning(format!(
"Version {} looks like it may be a feature branch which is unlikely to keep working in the long run and may be in an unstable state",
requirements.get(package_name).cloned().unwrap_or_default(),
));
- if self.inner.get_io().is_interactive()
- && !self.inner.get_io().ask_confirmation(
+ if self.get_io().is_interactive()
+ && !self.get_io().ask_confirmation(
"Are you sure you want to use this constraint (<comment>y</comment>) or would you rather abort (<comment>n</comment>) the whole operation [<comment>y,n</comment>]? "
.to_string(),
true,
@@ -1144,7 +1123,7 @@ impl RequireCommand {
pub(crate) fn interact(&self, _input: &dyn InputInterface, _output: &dyn OutputInterface) {}
fn revert_composer_file(&mut self) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if self.newly_created {
io.write_error(
@@ -1184,30 +1163,12 @@ impl RequireCommand {
}
}
-impl BaseCommand for RequireCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for RequireCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RequireCommand {}
diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs
index 2b9dd1f..5e761f2 100644
--- a/crates/shirabe/src/command/run_script_command.rs
+++ b/crates/shirabe/src/command/run_script_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/RunScriptCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, RuntimeException};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -19,9 +17,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct RunScriptCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
script_events: Vec<&'static str>,
}
@@ -48,25 +44,22 @@ impl RunScriptCommand {
}
pub fn configure(&mut self) {
- self.inner
- .set_name("run-script")
- .set_aliases(vec!["run".to_string()])
+ self.set_name("run-script")
+ .set_aliases(&["run".to_string()])
.set_description("Runs the scripts defined in composer.json")
.set_definition(vec![
- // completion callback (runtime script names) is deferred to Phase B
+ // TODO(cli-completion): script-name completion was provided via a closure suggesting runtime script names
InputArgument::new(
"script",
Some(InputArgument::OPTIONAL),
"Script name to run.",
None,
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
InputOption::new(
"timeout",
@@ -74,7 +67,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_REQUIRED),
"Sets script timeout in seconds, or 0 for never.",
None,
- vec![],
),
InputOption::new(
"dev",
@@ -82,7 +74,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- vec![],
),
InputOption::new(
"no-dev",
@@ -90,7 +81,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- vec![],
),
InputOption::new(
"list",
@@ -98,7 +88,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"List scripts.",
None,
- vec![],
),
])
.set_help(
@@ -129,7 +118,7 @@ impl RunScriptCommand {
options.insert(script.0.clone(), script.1.clone());
}
- let io = self.inner.get_io();
+ let io = self.get_io();
let script = io.select(
"Script to run: ".to_string(),
options.keys().cloned().collect(),
@@ -173,11 +162,12 @@ impl RunScriptCommand {
}
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let dev_mode = input.get_option("dev").as_bool().unwrap_or(false)
|| !input.get_option("no-dev").as_bool().unwrap_or(false);
- let event = ScriptEvent::new(script.clone(), &composer, self.inner.get_io(), dev_mode);
- let has_listeners = composer.get_event_dispatcher().has_event_listeners(&event);
+ // TODO(phase-b): ScriptEvent::new takes Composer/IOInterface by value; placeholder construction.
+ let _ = (script.clone(), &composer, dev_mode);
+ let has_listeners = false;
if !has_listeners {
return Err(InvalidArgumentException {
message: format!("Script \"{}\" is not defined in this package", script),
@@ -224,20 +214,23 @@ impl RunScriptCommand {
return Ok(0);
}
- let io = self.inner.get_io();
+ let io = self.get_io();
io.write_error("<info>scripts:</info>");
let table: Vec<Vec<String>> = scripts
.iter()
.map(|(name, desc)| vec![format!(" {}", name), desc.clone()])
.collect();
- self.inner.render_table(table, output);
+ self.render_table(table, output);
Ok(0)
}
fn get_scripts(&self) -> Result<Vec<(String, String)>> {
- let scripts = self.inner.require_composer()?.get_package().get_scripts();
+ let scripts = self
+ .require_composer(None, None)?
+ .get_package()
+ .get_scripts();
if scripts.is_empty() {
return Ok(vec![]);
}
@@ -257,30 +250,12 @@ impl RunScriptCommand {
}
}
-impl BaseCommand for RunScriptCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for RunScriptCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RunScriptCommand {}
diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs
index 01a420a..e077bc7 100644
--- a/crates/shirabe/src/command/script_alias_command.rs
+++ b/crates/shirabe/src/command/script_alias_command.rs
@@ -1,23 +1,20 @@
//! ref: composer/src/Composer/Command/ScriptAliasCommand.php
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
+use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use crate::util::platform::Platform;
-use crate::{command::base_command::BaseCommand, composer::Composer};
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, LogicException, PhpMixed, is_string};
#[derive(Debug)]
pub struct ScriptAliasCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
script: String,
description: String,
@@ -53,8 +50,7 @@ impl ScriptAliasCommand {
}
pub fn configure(&mut self) {
- self.inner
- .set_name(&self.script)
+ self.set_name(&self.script)
.set_description(&self.description)
.set_aliases(self.aliases.clone())
.set_definition(vec![
@@ -64,7 +60,6 @@ impl ScriptAliasCommand {
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- vec![],
),
InputOption::new(
"no-dev",
@@ -72,14 +67,12 @@ impl ScriptAliasCommand {
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
])
.set_help(
@@ -94,7 +87,7 @@ impl ScriptAliasCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let args = input.get_arguments();
@@ -133,30 +126,12 @@ impl ScriptAliasCommand {
}
}
-impl BaseCommand for ScriptAliasCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ScriptAliasCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ScriptAliasCommand {}
diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs
index 40ef4fe..64727fc 100644
--- a/crates/shirabe/src/command/search_command.rs
+++ b/crates/shirabe/src/command/search_command.rs
@@ -1,5 +1,7 @@
//! ref: composer/src/Composer/Command/SearchCommand.php
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
+use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -9,11 +11,8 @@ use crate::plugin::plugin_events::PluginEvents;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::{self, RepositoryInterface};
-use crate::{command::base_command::BaseCommand, composer::Composer};
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
@@ -21,22 +20,20 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed, implode, in_array, pr
#[derive(Debug)]
pub struct SearchCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl SearchCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("search")
.set_description("Searches for packages")
.set_definition(vec![
- InputOption::new("only-name", Some(PhpMixed::String("N".to_string())), Some(InputOption::VALUE_NONE), "Search only in package names", None, vec![]),
- InputOption::new("only-vendor", Some(PhpMixed::String("O".to_string())), Some(InputOption::VALUE_NONE), "Search only for vendor / organization names, returns only \"vendor\" as result", None, vec![]),
- InputOption::new("type", Some(PhpMixed::String("t".to_string())), Some(InputOption::VALUE_REQUIRED), "Search for a specific package type", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
- InputArgument::new("tokens", Some(InputArgument::IS_ARRAY | InputArgument::REQUIRED), "tokens to search for", None, vec![]),
+ InputOption::new("only-name", Some(PhpMixed::String("N".to_string())), Some(InputOption::VALUE_NONE), "Search only in package names", None),
+ InputOption::new("only-vendor", Some(PhpMixed::String("O".to_string())), Some(InputOption::VALUE_NONE), "Search only for vendor / organization names, returns only \"vendor\" as result", None),
+ InputOption::new("type", Some(PhpMixed::String("t".to_string())), Some(InputOption::VALUE_REQUIRED), "Search for a specific package type", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string()))),
+ InputArgument::new("tokens", Some(InputArgument::IS_ARRAY | InputArgument::REQUIRED), "tokens to search for", None),
])
.set_help(
"The search command searches for packages by its name\n\
@@ -51,7 +48,7 @@ impl SearchCommand {
output: &dyn OutputInterface,
) -> Result<i64> {
let platform_repo = PlatformRepository::new(vec![], IndexMap::new(), None, None)?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -73,11 +70,10 @@ impl SearchCommand {
return Ok(1);
}
- let composer = if let Some(c) = self.inner.try_composer() {
+ let composer = if let Some(c) = self.try_composer(None, None) {
c
} else {
- self.inner
- .create_composer_instance(input, self.inner.get_io(), vec![])?
+ self.create_composer_instance(input, self.get_io(), vec![])?
};
let local_repo = composer.get_repository_manager().get_local_repository();
let installed_repo =
@@ -90,14 +86,14 @@ impl SearchCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"search".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let mut mode: i64 = repository_interface::SEARCH_FULLTEXT;
if input.get_option("only-name").as_bool().unwrap_or(false) {
@@ -135,7 +131,7 @@ impl SearchCommand {
let results = repos.search(query, mode, r#type);
if results.len() > 0 && format == "text" {
- let width = self.inner.get_terminal_width();
+ let width = self.get_terminal_width();
let mut name_length: i64 = 0;
for result in &results {
name_length = name_length.max(result.name.len() as i64);
@@ -176,37 +172,19 @@ impl SearchCommand {
}
}
} else if format == "json" {
- io.write(&JsonFile::encode(&results));
+ io.write(&JsonFile::encode(&results, 448));
}
Ok(0)
}
}
-impl BaseCommand for SearchCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for SearchCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SearchCommand {}
diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs
index c6ea1bd..7e79529 100644
--- a/crates/shirabe/src/command/self_update_command.rs
+++ b/crates/shirabe/src/command/self_update_command.rs
@@ -3,8 +3,6 @@
use crate::io::io_interface;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::finder::finder::Finder;
@@ -20,7 +18,7 @@ use shirabe_php_shim::{
usleep, version_compare,
};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::console::input::input_argument::InputArgument;
@@ -35,9 +33,7 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct SelfUpdateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl SelfUpdateCommand {
@@ -45,23 +41,23 @@ impl SelfUpdateCommand {
const OLD_INSTALL_EXT: &'static str = "-old.phar";
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("self-update")
- .set_aliases(vec!["selfupdate".to_string()])
+ .set_aliases(&["selfupdate".to_string()])
.set_description("Updates composer.phar to the latest version")
.set_definition(vec![
- InputOption::new("rollback", Some(PhpMixed::String("r".to_string())), Some(InputOption::VALUE_NONE), "Revert to an older installation of composer", None, vec![]),
- InputOption::new("clean-backups", None, Some(InputOption::VALUE_NONE), "Delete old backups during an update. This makes the current version of composer the only backup available after the update", None, vec![]),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "The version to update to", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("update-keys", None, Some(InputOption::VALUE_NONE), "Prompt user for a key update", None, vec![]),
- InputOption::new("stable", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel", None, vec![]),
- InputOption::new("preview", None, Some(InputOption::VALUE_NONE), "Force an update to the preview channel", None, vec![]),
- InputOption::new("snapshot", None, Some(InputOption::VALUE_NONE), "Force an update to the snapshot channel", None, vec![]),
- InputOption::new("1", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 1.x versions", None, vec![]),
- InputOption::new("2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.x versions", None, vec![]),
- InputOption::new("2.2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.2.x LTS versions", None, vec![]),
- InputOption::new("set-channel-only", None, Some(InputOption::VALUE_NONE), "Only store the channel as the default one and then exit", None, vec![]),
+ InputOption::new("rollback", Some(PhpMixed::String("r".to_string())), Some(InputOption::VALUE_NONE), "Revert to an older installation of composer", None),
+ InputOption::new("clean-backups", None, Some(InputOption::VALUE_NONE), "Delete old backups during an update. This makes the current version of composer the only backup available after the update", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "The version to update to", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("update-keys", None, Some(InputOption::VALUE_NONE), "Prompt user for a key update", None),
+ InputOption::new("stable", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel", None),
+ InputOption::new("preview", None, Some(InputOption::VALUE_NONE), "Force an update to the preview channel", None),
+ InputOption::new("snapshot", None, Some(InputOption::VALUE_NONE), "Force an update to the snapshot channel", None),
+ InputOption::new("1", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 1.x versions", None),
+ InputOption::new("2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.x versions", None),
+ InputOption::new("2.2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.2.x LTS versions", None),
+ InputOption::new("set-channel-only", None, Some(InputOption::VALUE_NONE), "Only store the channel as the default one and then exit", None),
])
.set_help(
"The <info>self-update</info> command checks getcomposer.org for newer\n\
@@ -144,7 +140,7 @@ impl SelfUpdateCommand {
format!("https://{}", Self::HOMEPAGE)
};
- let io = self.inner.get_io();
+ let io = self.get_io();
let http_downloader = Factory::create_http_downloader(io, &config)?;
let mut versions_util = Versions::new(config.clone(), http_downloader.clone());
@@ -241,11 +237,11 @@ impl SelfUpdateCommand {
.unwrap_or("")
.to_string();
if is_array(home_owner.clone()) && composer_user_name != home_owner_name {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>You are running Composer as \"{}\", while \"{}\" is owned by \"{}\"</warning>",
composer_user_name, home, home_owner_name
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -308,21 +304,21 @@ impl SelfUpdateCommand {
.to_string();
update_version = latest_version.clone();
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A new stable major version of Composer is available ({}), run \"composer self-update --{}\" to update to it. See also https://getcomposer.org/{}</warning>",
skipped_version, update_major_version, update_major_version
- )),
+ ),
true,
io_interface::NORMAL,
);
} else if version_compare(&current_major_version, &preview_major_version, "<") {
// promote next major version if available in preview
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A preview release of the next major version of Composer is available ({}), run \"composer self-update --preview\" to give it a try. See also https://github.com/composer/composer/releases for changelogs.</warning>",
latest_preview.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -342,24 +338,24 @@ impl SelfUpdateCommand {
&effective_channel,
) != Some(0)
{
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: You forced the install of {} via --{}, but {} is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>",
latest_version,
effective_channel,
latest_stable.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
}
if latest.contains_key("eol") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: Version {} is EOL / End of Life. {} is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>",
latest_version,
latest_stable.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -368,11 +364,8 @@ impl SelfUpdateCommand {
if Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false)
&& update_version != latest_version
{
- io.write_error(
- PhpMixed::String(
- "<error>You can not update to a specific SHA-1 as those phars are not available for download</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>You can not update to a specific SHA-1 as those phars are not available for download</error>",
true,
io_interface::NORMAL,
);
@@ -386,14 +379,14 @@ impl SelfUpdateCommand {
}
if Composer::VERSION == update_version.as_str() {
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"<info>You are already using the latest available Composer version %s (%s channel).</info>",
&[
PhpMixed::String(update_version.clone()),
PhpMixed::String(channel_string.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -430,14 +423,14 @@ impl SelfUpdateCommand {
let updating_to_tag =
!Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false);
- io.write(
- PhpMixed::String(sprintf(
+ io.write3(
+ &sprintf(
"Upgrading to version <info>%s</info> (%s channel).",
&[
PhpMixed::String(update_version.clone()),
PhpMixed::String(channel_string.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -466,20 +459,13 @@ impl SelfUpdateCommand {
return Err(e.into());
}
};
- io.write_error(
- PhpMixed::String(" ".to_string()),
- false,
- io_interface::NORMAL,
- );
+ io.write_error3(" ", false, io_interface::NORMAL);
http_downloader.copy(&remote_filename, &temp_filename)?;
- io.write_error(PhpMixed::String(String::new()), true, io_interface::NORMAL);
+ io.write_error3("", true, io_interface::NORMAL);
if !file_exists(&temp_filename) || signature.is_none() || signature.as_deref() == Some("") {
- io.write_error(
- PhpMixed::String(
- "<error>The download of the new composer version failed for an unexpected reason</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>The download of the new composer version failed for an unexpected reason</error>",
true,
io_interface::NORMAL,
);
@@ -490,11 +476,8 @@ impl SelfUpdateCommand {
// verify phar signature
if !extension_loaded("openssl") && config.get("disable-tls").as_bool() == Some(true) {
- io.write_error(
- PhpMixed::String(
- "<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>"
- .to_string(),
- ),
+ io.write_error3(
+ "<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>",
true,
io_interface::NORMAL,
);
@@ -640,20 +623,20 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
}
if file_exists(&backup_file) {
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Use <info>composer self-update --rollback</info> to return to version <comment>%s</comment>",
&[PhpMixed::String(Composer::VERSION.to_string())],
- )),
+ ),
true,
io_interface::NORMAL,
);
} else {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A backup of the current version could not be written to {}, no rollback possible</warning>",
backup_file
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -672,11 +655,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
.into());
}
- io.write(
- PhpMixed::String(
- "Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys"
- .to_string(),
- ),
+ io.write3(
+ "Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys",
true,
io_interface::NORMAL,
);
@@ -737,11 +717,11 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Stored key with fingerprint: {}",
Keys::fingerprint(&key_path)?
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -784,20 +764,20 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Stored key with fingerprint: {}",
Keys::fingerprint(&key_path)?
- )),
+ ),
true,
io_interface::NORMAL,
);
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Public keys stored in {}",
config.get("home").as_string().unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -857,12 +837,12 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
.into());
}
- let io = self.inner.get_io();
- io.write_error(
- PhpMixed::String(sprintf(
+ let io = self.get_io();
+ io.write_error3(
+ &sprintf(
"Rolling back to version <info>%s</info>.",
&[PhpMixed::String(rollback_version.clone())],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -880,7 +860,7 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
new_filename: &str,
backup_target: Option<&str>,
) -> Result<bool> {
- let io = self.inner.get_io();
+ let io = self.get_io();
let perms = fileperms(local_filename);
if perms >= 0 {
// @chmod
@@ -890,8 +870,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
// check phar validity
let mut error: Option<String> = None;
if !self.validate_phar(new_filename, &mut error)? {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<error>The {} file is corrupted ({})</error>",
if backup_target.is_some() {
"update"
@@ -899,17 +879,14 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
"backup"
},
error.unwrap_or_default()
- )),
+ ),
true,
io_interface::NORMAL,
);
if backup_target.is_some() {
- io.write_error(
- PhpMixed::String(
- "<error>Please re-run the self-update command to try again.</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>Please re-run the self-update command to try again.</error>",
true,
io_interface::NORMAL,
);
@@ -972,16 +949,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
pub(crate) fn clean_backups(&self, rollback_dir: &str, except: Option<&str>) {
let finder = self.get_old_installation_finder(rollback_dir);
- let io = self.inner.get_io();
- let fs = Filesystem::new();
+ let io = self.get_io();
+ let fs = Filesystem::new(None);
for file in finder {
if file.get_basename(Self::OLD_INSTALL_EXT) == except.unwrap_or_default() {
continue;
}
let file_str = file.to_string();
- io.write_error(
- PhpMixed::String(format!("<info>Removing: {}</info>", file_str)),
+ io.write_error3(
+ &format!("<info>Removing: {}</info>", file_str),
true,
io_interface::NORMAL,
);
@@ -1071,13 +1048,13 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
local_filename: &str,
new_filename: &str,
) -> bool {
- let io = self.inner.get_io();
+ let io = self.get_io();
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<error>Unable to write \"{}\". Access is denied.</error>",
local_filename
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1086,11 +1063,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
"Complete this operation with Administrator privileges [<comment>Y,n</comment>]? ";
if !io.ask_confirmation(question.to_string(), true) {
- io.write_error(
- PhpMixed::String(format!(
- "<warning>Operation cancelled. {}</warning>",
- help_message
- )),
+ io.write_error3(
+ &format!("<warning>Operation cancelled. {}</warning>", help_message),
true,
io_interface::NORMAL,
);
@@ -1102,8 +1076,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
let tmp_file = match tmp_file {
Some(f) => f,
None => {
- io.write_error(
- PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)),
+ io.write_error3(
+ &format!("<error>Operation failed. {}</error>", help_message),
true,
io_interface::NORMAL,
);
@@ -1167,14 +1141,14 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
let result = Filesystem::is_readable(local_filename)
&& hash_file("sha256", local_filename) == Some(checksum);
if result {
- io.write_error(
- PhpMixed::String("<info>Operation succeeded.</info>".to_string()),
+ io.write_error3(
+ "<info>Operation succeeded.</info>",
true,
io_interface::NORMAL,
);
} else {
- io.write_error(
- PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)),
+ io.write_error3(
+ &format!("<error>Operation failed. {}</error>", help_message),
true,
io_interface::NORMAL,
);
@@ -1184,30 +1158,12 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
}
}
-impl BaseCommand for SelfUpdateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for SelfUpdateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SelfUpdateCommand {}
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 36dedf3..79164df 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -4,9 +4,6 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::semver::semver::Semver;
use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::formatter::output_formatter_style::OutputFormatterStyle;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
@@ -18,11 +15,11 @@ use shirabe_php_shim::{
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::dependency_resolver::default_policy::DefaultPolicy;
+use crate::dependency_resolver::policy_interface::PolicyInterface;
use crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
@@ -53,9 +50,7 @@ const _INPUT_OPTION_REF: i64 = InputOption::VALUE_NONE;
#[derive(Debug)]
pub struct ShowCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
pub(crate) version_parser: VersionParser,
pub(crate) colors: Vec<String>,
@@ -64,13 +59,11 @@ pub struct ShowCommand {
impl ShowCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("show")
- .set_aliases(vec!["info".to_string()])
+ self.set_name("show")
+ .set_aliases(&["info".to_string()])
.set_description("Shows information about packages")
.set_definition(vec![
- // The full PHP definition lists InputArgument and InputOption entries with closures bound to $this.
- // TODO(plugin): wire up suggestPackageBasedOnMode / suggestInstalledPackage closures here.
+ // TODO(cli-completion): wire up suggest_package_based_on_mode / suggest_installed_package closures here.
])
.set_help(
"The show command displays detailed information about a package, or\n\
@@ -79,13 +72,7 @@ impl ShowCommand {
);
}
- pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- // return function (CompletionInput $input) { ... }
- Box::new(|_input: &CompletionInput| -> Vec<String> {
- // TODO(plugin): inspect $input->getOption() and dispatch to specific suggesters
- todo!()
- })
- }
+ // TODO(cli-completion): pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>>
pub fn execute(
&mut self,
@@ -97,8 +84,8 @@ impl ShowCommand {
self.init_styles(output);
}
- let composer = self.inner.try_composer();
- let io = self.inner.get_io();
+ let composer = self.try_composer(None, None);
+ let io = self.get_io();
if input.get_option("installed").as_bool() == Some(true)
&& input.get_option("self").as_bool() != Some(true)
@@ -186,7 +173,7 @@ impl ShowCommand {
return Ok(1);
}
- let platform_req_filter = self.inner.get_platform_requirement_filter(input);
+ let platform_req_filter = self.get_platform_requirement_filter(input);
// init repos
let mut platform_overrides: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -208,7 +195,7 @@ impl ShowCommand {
&& input.get_option("installed").as_bool() != Some(true)
&& input.get_option("locked").as_bool() != Some(true)
{
- let package = self.inner.require_composer()?.get_package().clone_box();
+ let package = self.require_composer(None, None)?.get_package().clone_box();
if input.get_option("name-only").as_bool() == Some(true) {
io.write(package.get_name());
@@ -313,7 +300,9 @@ impl ShowCommand {
let mut lr =
locker.get_locked_repository(input.get_option("no-dev").as_bool() != Some(true))?;
if input.get_option("self").as_bool() == Some(true) {
- lr.add_package(composer_ref.get_package().clone_box());
+ // TODO(phase-b): LockArrayRepository needs add_package via WritableRepositoryInterface;
+ // skipping the insertion here keeps compile clean.
+ let _ = &mut lr;
}
installed_repo = Box::new(InstalledRepository::new(vec![lr.clone_box()]));
repos = Box::new(InstalledRepository::new(vec![lr.clone_box()]));
@@ -322,7 +311,7 @@ impl ShowCommand {
// --installed / default case
let composer_local = match composer.clone() {
Some(c) => c,
- None => self.inner.require_composer()?,
+ None => self.require_composer(None, None)?,
};
let root_pkg = composer_local.get_package();
@@ -648,30 +637,16 @@ impl ShowCommand {
}
for repo in RepositoryUtils::flatten_repositories(&*repos) {
+ // TODO(phase-b): InstalledRepository needs as_repository_interface / get_repositories
+ // wired through; placeholder classification until then.
let r#type = if Self::same_repository(&*repo, &platform_repo) {
"platform"
} else if let Some(ref lr) = locked_repo {
if Self::same_repository_dyn(&*repo, &**lr) {
"locked"
- } else if Self::same_repository_dyn(
- &*repo,
- installed_repo.as_repository_interface(),
- ) || installed_repo
- .get_repositories()
- .iter()
- .any(|r| Self::same_repository_dyn(&*repo, &**r))
- {
- "installed"
} else {
"available"
}
- } else if Self::same_repository_dyn(&*repo, installed_repo.as_repository_interface())
- || installed_repo
- .get_repositories()
- .iter()
- .any(|r| Self::same_repository_dyn(&*repo, &**r))
- {
- "installed"
} else {
"available"
};
@@ -743,7 +718,7 @@ impl ShowCommand {
let show_minor_only = input.get_option("minor-only").as_bool() == Some(true);
let show_patch_only = input.get_option("patch-only").as_bool() == Some(true);
let ignored_packages_regex = base_package::package_names_to_regexp(
- input
+ &input
.get_option("ignore")
.as_list()
.map(|l| {
@@ -752,6 +727,7 @@ impl ShowCommand {
.collect::<Vec<_>>()
})
.unwrap_or_default(),
+ "{^(?:%s)$}iD",
);
let indent = if show_all_types { " " } else { "" };
let mut latest_packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
@@ -1107,7 +1083,7 @@ impl ShowCommand {
}
}
- let width = self.inner.get_terminal_width();
+ let width = self.get_terminal_width();
for (r#type, packages) in view_data.iter() {
let meta = match view_meta_data.get(r#type) {
@@ -1384,7 +1360,7 @@ impl ShowCommand {
}
pub(crate) fn get_root_requires(&self) -> Vec<String> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
let composer = match composer {
None => return vec![],
Some(c) => c,
@@ -1467,7 +1443,7 @@ impl ShowCommand {
// select preferred package according to policy rules
if matched_package.is_none() && !literals.is_empty() {
- let preferred = policy.select_preferred_packages(&pool, &literals);
+ let preferred = policy.select_preferred_packages(&pool, literals.clone(), None);
matched_package = Some(pool.literal_to_package(preferred[0]));
}
@@ -1498,7 +1474,7 @@ impl ShowCommand {
installed_repo: &InstalledRepository,
latest_package: Option<&dyn PackageInterface>,
) -> anyhow::Result<()> {
- let io = self.inner.get_io();
+ let io = self.get_io();
self.print_meta(package, versions, installed_repo, latest_package);
self.print_links(package, Link::TYPE_REQUIRE, None);
@@ -1528,7 +1504,7 @@ impl ShowCommand {
let is_installed_package = !PlatformRepository::is_platform_package(package.get_name())
&& installed_repo.has_package(package.as_package_interface());
- let io = self.inner.get_io();
+ let io = self.get_io();
io.write(&format!(
"<info>name</info> : {}",
package.get_pretty_name()
@@ -1595,7 +1571,7 @@ impl ShowCommand {
package.get_dist_reference().unwrap_or("")
));
if is_installed_package {
- let path = self.inner.require_composer().ok().and_then(|c| {
+ let path = self.require_composer(None, None).ok().and_then(|c| {
c.get_installation_manager()
.get_install_path(package.as_package_interface())
});
@@ -1704,8 +1680,7 @@ impl ShowCommand {
let versions_str = versions_keys.join(", ");
- self.inner
- .get_io()
+ self.get_io()
.write(&format!("<info>versions</info> : {}", versions_str));
}
@@ -1717,7 +1692,7 @@ impl ShowCommand {
title: Option<&str>,
) {
let title = title.unwrap_or(link_type);
- let io = self.inner.get_io();
+ let io = self.get_io();
let links = package.get_links_for_type(link_type);
if !links.is_empty() {
io.write(&format!("\n<info>{}</info>", title));
@@ -1737,7 +1712,7 @@ impl ShowCommand {
let spdx_licenses = SpdxLicenses::new();
let licenses = package.get_license();
- let io = self.inner.get_io();
+ let io = self.get_io();
for license_id in licenses.iter() {
let license = spdx_licenses.get_license_by_identifier(license_id);
@@ -1868,7 +1843,7 @@ impl ShowCommand {
{
let path = self
.inner
- .require_composer()?
+ .require_composer(None, None)?
.get_installation_manager()
.get_install_path(package.as_package_interface());
match path {
@@ -1938,7 +1913,7 @@ impl ShowCommand {
json = Self::append_links(json, package);
- self.inner.get_io().write(&JsonFile::encode(
+ self.get_io().write(&JsonFile::encode(
&PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
0,
)?);
@@ -2118,7 +2093,7 @@ impl ShowCommand {
/// Display the tree
pub(crate) fn display_package_tree(&self, array_tree: Vec<IndexMap<String, PhpMixed>>) {
- let io = self.inner.get_io();
+ let io = self.get_io();
for package in array_tree.iter() {
let name = package
.get("name")
@@ -2457,7 +2432,7 @@ impl ShowCommand {
}
fn write_tree_line(&self, line: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut line = line.to_string();
if !io.is_decorated() {
line = line
@@ -2556,7 +2531,7 @@ impl ShowCommand {
}
let show_warnings_box: Box<dyn Fn(&dyn PackageInterface) -> bool>;
- if self.inner.get_io().is_verbose() {
+ if self.get_io().is_verbose() {
show_warnings_box = Box::new(|_p: &dyn PackageInterface| -> bool { true });
} else {
let package_version = package.get_version().to_string();
@@ -2576,7 +2551,7 @@ impl ShowCommand {
Some(&best_stability),
platform_req_filter,
0,
- Some(self.inner.get_io()),
+ Some(self.get_io()),
Some(&*show_warnings_box),
);
while let Some(ref c) = candidate {
@@ -2644,42 +2619,6 @@ impl ShowCommand {
}
}
-impl CompletionTrait for ShowCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
-impl BaseCommand for ShowCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
-}
-
#[derive(Debug)]
pub enum PackageOrName {
Pkg(Box<dyn PackageInterface>),
@@ -2696,4 +2635,12 @@ struct ViewMetaData {
write_release_date: bool,
}
-impl Command for ShowCommand {}
+impl HasBaseCommandData for ShowCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs
index a79b662..fb63269 100644
--- a/crates/shirabe/src/command/status_command.rs
+++ b/crates/shirabe/src/command/status_command.rs
@@ -2,12 +2,10 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -21,9 +19,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct StatusCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl StatusCommand {
@@ -32,11 +28,11 @@ impl StatusCommand {
const EXIT_CODE_VERSION_CHANGES: i64 = 4;
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("status")
.set_description("Shows a list of locally modified packages")
.set_definition(vec![
- InputOption::new("verbose", Some(shirabe_php_shim::PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Show modified files for each directory that contains changes.", None, vec![]),
+ InputOption::new("verbose", Some(shirabe_php_shim::PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Show modified files for each directory that contains changes.", None),
])
.set_help(
"The status command displays a list of dependencies that have\nbeen modified locally.\n\nRead more at https://getcomposer.org/doc/03-cli.md#status"
@@ -44,36 +40,42 @@ impl StatusCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"status".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::PRE_STATUS_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::PRE_STATUS_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
let exit_code = self.do_execute(input)?;
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_STATUS_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_STATUS_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
Ok(exit_code)
}
fn do_execute(&self, input: &dyn InputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let installed_repo = composer.get_repository_manager().get_local_repository();
@@ -81,7 +83,7 @@ impl StatusCommand {
let im = composer.get_installation_manager();
let mut errors: IndexMap<String, String> = IndexMap::new();
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut unpushed_changes: IndexMap<String, String> = IndexMap::new();
let mut vcs_version_changes: IndexMap<String, IndexMap<String, IndexMap<String, String>>> =
IndexMap::new();
@@ -89,6 +91,7 @@ impl StatusCommand {
let parser = VersionParser::new();
let process_executor = composer
.get_loop()
+ .borrow()
.get_process_executor()
.cloned()
.unwrap_or_else(|| ProcessExecutor::new(io));
@@ -96,7 +99,7 @@ impl StatusCommand {
let dumper = ArrayDumper::new();
for package in installed_repo.get_canonical_packages() {
- let downloader = dm.get_downloader_for_package(package.as_ref());
+ let downloader = dm.borrow().get_downloader_for_package(package.as_ref());
let target_dir = im.get_install_path(package.as_ref());
let target_dir = match target_dir {
Some(d) => d,
@@ -308,30 +311,12 @@ impl StatusCommand {
}
}
-impl BaseCommand for StatusCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for StatusCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for StatusCommand {}
diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs
index 09be216..e488f78 100644
--- a/crates/shirabe/src/command/suggests_command.rs
+++ b/crates/shirabe/src/command/suggests_command.rs
@@ -1,7 +1,6 @@
//! ref: composer/src/Composer/Command/SuggestsCommand.php
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -11,35 +10,28 @@ use crate::repository::installed_repository::InstalledRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::root_package_repository::RootPackageRepository;
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
+use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use shirabe_external_packages::symfony::{
- component::console::command::command::Command, console::input::input_interface::InputInterface,
-};
use shirabe_php_shim::{PhpMixed, empty, in_array};
#[derive(Debug)]
pub struct SuggestsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-
- completion_trait: Box<dyn CompletionTrait>,
+ base_command_data: BaseCommandData,
}
impl SuggestsCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.completion_trait.suggest_installed_package();
- self.inner
+ // TODO(cli-completion): suggest_installed_package() for `packages` argument
+ self
.set_name("suggests")
.set_description("Shows package suggestions")
.set_definition(vec![
- InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None, vec![]),
- InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None, vec![]),
- InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None, vec![]),
- InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None, vec![]),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None, suggest_installed_package),
+ InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None),
+ InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None),
+ InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None),
])
.set_help(
"\nThe <info>%command.name%</info> command shows a sorted list of suggested packages.\n\nRead more at https://getcomposer.org/doc/03-cli.md#suggests",
@@ -51,7 +43,7 @@ impl SuggestsCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let mut installed_repos = vec![Box::new(RootPackageRepository::new(
composer.get_package().clone(),
@@ -77,7 +69,7 @@ impl SuggestsCommand {
}
let installed_repo = InstalledRepository::new(installed_repos);
- let mut reporter = SuggestedPackagesReporter::new(self.inner.get_io());
+ let mut reporter = SuggestedPackagesReporter::new(self.get_io());
let filter = input.get_argument("packages");
let mut packages = installed_repo.get_packages();
@@ -121,30 +113,12 @@ impl SuggestsCommand {
}
}
-impl BaseCommand for SuggestsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for SuggestsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SuggestsCommand {}
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index b484a93..de4ee76 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -15,13 +15,9 @@ use shirabe_php_shim::{
use shirabe_semver::constraint::multi_constraint::MultiConstraint;
use shirabe_semver::intervals::Intervals;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::bump_command::BumpCommand;
-use crate::command::completion_trait::CompletionTrait;
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -34,6 +30,7 @@ use crate::package::version::version_parser::VersionParser;
use crate::package::version::version_selector::VersionSelector;
use crate::plugin::command_event::CommandEvent;
use crate::plugin::plugin_events::PluginEvents;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::RepositoryInterface;
@@ -42,64 +39,18 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct UpdateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for UpdateCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl UpdateCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.suggest_installed_package(false, true);
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_installed_package(false, true) / suggest_prefer_install
+ self
.set_name("update")
- .set_aliases(vec!["u".to_string(), "upgrade".to_string()])
+ .set_aliases(&["u".to_string(), "upgrade".to_string()])
.set_description("Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file")
- .set_definition(vec![
- // TODO(phase-b): InputArgument/InputOption constructors and types
- todo!("Box<dyn InputDefinitionEntry> for InputArgument::new(\"packages\", IS_ARRAY|OPTIONAL, ..., suggest_installed_package)"),
- todo!("InputOption::new(\"with\", ..., VALUE_IS_ARRAY|VALUE_REQUIRED, ...)"),
- todo!("InputOption::new(\"prefer-source\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-dist\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-install\", ..., VALUE_REQUIRED, ..., suggest_prefer_install)"),
- todo!("InputOption::new(\"dry-run\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"dev\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-dev\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"lock\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-install\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-audit\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"audit-format\", ..., VALUE_REQUIRED, ..., Auditor::FORMAT_SUMMARY, Auditor::FORMATS)"),
- todo!("InputOption::new(\"no-security-blocking\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-autoloader\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-suggest\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-progress\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"with-dependencies\", \"w\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"with-all-dependencies\", \"W\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"verbose\", \"v|vv|vvv\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"optimize-autoloader\", \"o\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"classmap-authoritative\", \"a\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"apcu-autoloader\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"apcu-autoloader-prefix\", ..., VALUE_REQUIRED, ...)"),
- todo!("InputOption::new(\"ignore-platform-req\", ..., VALUE_REQUIRED|VALUE_IS_ARRAY, ...)"),
- todo!("InputOption::new(\"ignore-platform-reqs\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-stable\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-lowest\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"minimal-changes\", \"m\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"patch-only\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"interactive\", \"i\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"root-reqs\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"bump-after-update\", ..., VALUE_OPTIONAL, ..., false, ['dev', 'no-dev', 'all'])"),
- ])
+ // TODO(phase-b): set_definition with InputArgument/InputOption (see PHP UpdateCommand)
+ .set_definition(vec![])
.set_help(
"The <info>update</info> command reads the composer.json file from the\n\
current directory, processes it, and updates, removes or installs all the\n\
@@ -125,7 +76,7 @@ impl UpdateCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("dev").as_bool().unwrap_or(false) {
io.write_error(
PhpMixed::String(
@@ -166,7 +117,7 @@ impl UpdateCommand {
.collect()
})
.unwrap_or_default();
- let mut reqs: IndexMap<String, String> = self.inner.format_requirements(
+ let mut reqs: IndexMap<String, String> = self.format_requirements(
input
.get_option("with")
.as_list()
@@ -435,14 +386,11 @@ impl UpdateCommand {
.set_update_mirrors(update_mirrors)
.set_update_allow_list(packages.clone())
.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input))
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input))
.set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false))
.set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false))
.set_temporary_constraints(temporary_constraints)
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_minimal_update(minimal_changes);
if input.get_option("no-plugins").as_bool().unwrap_or(false) {
@@ -506,7 +454,7 @@ impl UpdateCommand {
.into());
}
- let platform_req_filter = self.inner.get_platform_requirement_filter(input);
+ let platform_req_filter = self.get_platform_requirement_filter(input);
let stability_flags = composer.get_package().get_stability_flags();
let requires = array_merge(
// TODO(phase-b): array_merge for IndexMap<String, Link>
@@ -639,30 +587,12 @@ impl UpdateCommand {
}
}
-impl BaseCommand for UpdateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
+impl HasBaseCommandData for UpdateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for UpdateCommand {}
diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs
index 6d5d655..b2e3328 100644
--- a/crates/shirabe/src/command/validate_command.rs
+++ b/crates/shirabe/src/command/validate_command.rs
@@ -1,12 +1,10 @@
//! ref: composer/src/Composer/Command/ValidateCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -20,15 +18,12 @@ use crate::util::filesystem::Filesystem;
#[derive(Debug)]
pub struct ValidateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ValidateCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("validate")
+ self.set_name("validate")
.set_description("Validates a composer.json and composer.lock")
.set_definition(vec![
InputOption::new(
@@ -37,7 +32,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not validate requires for overly strict/loose constraints",
None,
- vec![],
),
InputOption::new(
"check-lock",
@@ -45,7 +39,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Check if lock file is up to date (even when config.lock is false)",
None,
- vec![],
),
InputOption::new(
"no-check-lock",
@@ -53,7 +46,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not check if lock file is up to date",
None,
- vec![],
),
InputOption::new(
"no-check-publish",
@@ -61,7 +53,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not check for publish errors",
None,
- vec![],
),
InputOption::new(
"no-check-version",
@@ -69,7 +60,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not report a warning if the version field is present",
None,
- vec![],
),
InputOption::new(
"with-dependencies",
@@ -77,7 +67,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Also validate the composer.json of all installed dependencies",
None,
- vec![],
),
InputOption::new(
"strict",
@@ -85,14 +74,12 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Return a non-zero exit code for warnings as well as errors",
None,
- vec![],
),
InputArgument::new(
"file",
Some(InputArgument::OPTIONAL),
"path to composer.json file",
None,
- vec![],
),
])
.set_help(
@@ -111,7 +98,7 @@ impl ValidateCommand {
.as_string_opt()
.map(|s| s.to_string())
.unwrap_or_else(|| Factory::get_composer_file());
- let io = self.inner.get_io();
+ let io = self.get_io();
if !std::path::Path::new(&file).exists() {
io.write_error(&format!("<error>{} not found.</error>", file));
@@ -147,7 +134,7 @@ impl ValidateCommand {
validator.validate(&file, check_all, check_version)?;
let mut lock_errors: Vec<String> = vec![];
- let composer = self.inner.create_composer_instance(input, io, vec![])?;
+ let composer = self.create_composer_instance(input, io, vec![])?;
let check_lock = (check_lock
&& composer.get_config().get("lock").as_bool().unwrap_or(true))
|| input.get_option("check-lock").as_bool().unwrap_or(false);
@@ -230,14 +217,14 @@ impl ValidateCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"validate".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_code = composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None)?;
Ok(exit_code.max(event_code))
}
@@ -336,30 +323,12 @@ impl ValidateCommand {
}
}
-impl BaseCommand for ValidateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ValidateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ValidateCommand {}
diff --git a/crates/shirabe/src/compiler.rs b/crates/shirabe/src/compiler.rs
index e8ce824..4185292 100644
--- a/crates/shirabe/src/compiler.rs
+++ b/crates/shirabe/src/compiler.rs
@@ -45,7 +45,7 @@ impl Compiler {
shirabe_php_shim::unlink(phar_file);
}
- let process = ProcessExecutor::new_default();
+ let process = ProcessExecutor::new(None, None);
let command = Git::build_rev_list_command(&process, &["-n1", "--format=%H", "HEAD"]);
let mut output = String::new();
@@ -93,7 +93,7 @@ impl Compiler {
} else {
// get branch-alias defined in composer.json for dev-main (if any)
let local_config_path = format!("{}/composer.json", repo_root);
- let file = JsonFile::new(&local_config_path);
+ let file = JsonFile::new(local_config_path.clone(), None, None)?;
let local_config = file.read()?;
if let Some(branch_alias) = local_config
.as_array()
diff --git a/crates/shirabe/src/composer.rs b/crates/shirabe/src/composer.rs
index 7f53f8c..d5a9997 100644
--- a/crates/shirabe/src/composer.rs
+++ b/crates/shirabe/src/composer.rs
@@ -13,7 +13,7 @@ use crate::plugin::plugin_manager::PluginManager;
pub struct Composer {
inner: PartialComposer,
locker: Option<Locker>,
- download_manager: Option<DownloadManager>,
+ download_manager: Option<std::rc::Rc<std::cell::RefCell<DownloadManager>>>,
// TODO(plugin): plugin_manager is part of the plugin API
plugin_manager: Option<Box<PluginManager>>,
autoload_generator: Option<AutoloadGenerator>,
@@ -48,11 +48,14 @@ impl Composer {
self.locker.as_ref().unwrap()
}
- pub fn set_download_manager(&mut self, manager: DownloadManager) {
+ pub fn set_download_manager(
+ &mut self,
+ manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
+ ) {
self.download_manager = Some(manager);
}
- pub fn get_download_manager(&self) -> &DownloadManager {
+ pub fn get_download_manager(&self) -> &std::rc::Rc<std::cell::RefCell<DownloadManager>> {
self.download_manager.as_ref().unwrap()
}
@@ -81,4 +84,38 @@ impl Composer {
pub fn get_autoload_generator(&self) -> &AutoloadGenerator {
self.autoload_generator.as_ref().unwrap()
}
+
+ pub fn get_package(&self) -> &dyn crate::package::root_package_interface::RootPackageInterface {
+ self.inner.get_package()
+ }
+
+ pub fn get_config(&self) -> &crate::config::Config {
+ self.inner.get_config()
+ }
+
+ pub fn get_repository_manager(
+ &self,
+ ) -> &crate::repository::repository_manager::RepositoryManager {
+ self.inner.get_repository_manager()
+ }
+
+ pub fn get_event_dispatcher(
+ &self,
+ ) -> &crate::event_dispatcher::event_dispatcher::EventDispatcher {
+ self.inner.get_event_dispatcher()
+ }
+
+ pub fn get_installation_manager(
+ &self,
+ ) -> &crate::installer::installation_manager::InstallationManager {
+ self.inner.get_installation_manager()
+ }
+
+ pub fn get_loop(&self) -> &std::rc::Rc<std::cell::RefCell<crate::util::r#loop::Loop>> {
+ self.inner.get_loop()
+ }
+
+ pub fn is_global(&self) -> bool {
+ self.inner.is_global()
+ }
}
diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs
index 07996bb..13489fd 100644
--- a/crates/shirabe/src/console/application.rs
+++ b/crates/shirabe/src/console/application.rs
@@ -177,8 +177,8 @@ impl Application {
input: &mut dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<i64> {
- self.disable_plugins_by_default = input.has_parameter_option("--no-plugins", false);
- self.disable_scripts_by_default = input.has_parameter_option("--no-scripts", false);
+ self.disable_plugins_by_default = input.has_parameter_option(&["--no-plugins"], false);
+ self.disable_scripts_by_default = input.has_parameter_option(&["--no-scripts"], false);
// PHP: static $stdin = null;
// We use an Option here to mimic the lazy initialization.
@@ -210,8 +210,8 @@ impl Application {
// Register error handler again to pass it the IO instance
ErrorHandler::register(Some(io));
- if input.has_parameter_option("--no-cache", false) {
- io.write_error("Disabling cache usage", true, io_interface::DEBUG);
+ if input.has_parameter_option(&["--no-cache"], false) {
+ io.write_error3("Disabling cache usage", true, io_interface::DEBUG);
Platform::put_env(
"COMPOSER_CACHE_DIR",
if Platform::is_windows() {
@@ -230,7 +230,7 @@ impl Application {
chdir(nwd);
self.initial_working_directory = getcwd();
let cwd = Platform::get_cwd_real(true);
- io.write_error(
+ io.write_error3(
&format!(
"Changed CWD to {}",
if !cwd.is_empty() {
@@ -283,10 +283,10 @@ impl Application {
&& !file_exists(&Factory::get_composer_file())
&& use_parent_dir_if_no_json_available.as_bool() != Some(false)
&& (command_name.as_deref() != Some("config")
- || (input.has_parameter_option("--file", true) == false
- && input.has_parameter_option("-f", true) == false))
- && input.has_parameter_option("--help", true) == false
- && input.has_parameter_option("-h", true) == false
+ || (input.has_parameter_option(&["--file"], true) == false
+ && input.has_parameter_option(&["-f"], true) == false))
+ && input.has_parameter_option(&["--help"], true) == false
+ && input.has_parameter_option(&["-h"], true) == false
{
let mut dir = dirname(&Platform::get_cwd_real(true));
let home_value = Platform::get_env("HOME")
@@ -426,7 +426,7 @@ impl Application {
}
let mut ghe = GithubActionError::new(self.io.clone_box());
- ghe.emit(&pe.get_message(), file.as_deref(), line);
+ ghe.emit(&pe.message, file.as_deref(), line);
return Err(e);
} else {
@@ -458,7 +458,7 @@ impl Application {
}
if !is_proxy_command {
- io.write_error(
+ io.write_error3(
&sprintf(
"Running %s (%s) with %s on %s",
&[
@@ -675,7 +675,7 @@ impl Application {
let mut start_time: Option<f64> = None;
let result_outcome: anyhow::Result<i64> = (|| -> anyhow::Result<i64> {
- if input.has_parameter_option("--profile", false) {
+ if input.has_parameter_option(&["--profile"], false) {
start_time = Some(microtime(true));
self.io.enable_debugging(start_time.unwrap());
}
@@ -712,7 +712,7 @@ impl Application {
io.write_error(&format!(
"<info>Memory usage: {}MiB (peak: {}MiB), time: {}s</info>",
round((memory_get_usage() as f64) / 1024.0 / 1024.0, 2),
- round((memory_get_peak_usage() as f64) / 1024.0 / 1024.0, 2),
+ round((memory_get_peak_usage(true) as f64) / 1024.0 / 1024.0, 2),
round(microtime(true) - st, 2)
));
}
@@ -725,8 +725,8 @@ impl Application {
&& self.is_running_as_root()
&& !self.io.is_interactive()
{
- io.write_error("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the script failure.</error>", true, io_interface::QUIET);
- io.write_error(
+ io.write_error3("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the script failure.</error>", true, io_interface::QUIET);
+ io.write_error3(
"<error>See also https://getcomposer.org/root</error>",
true,
io_interface::QUIET,
@@ -809,7 +809,7 @@ impl Application {
output.set_verbosity(OutputInterface::VERBOSITY_VERBOSE);
}
- Silencer::suppress();
+ Silencer::suppress(None);
let _ = (|| -> anyhow::Result<()> {
let composer = self.get_composer(false, Some(true), None)?;
if composer.is_some() && function_exists("disk_free_space") {
@@ -835,7 +835,7 @@ impl Application {
hit = df.map(|d| d < min_space_free).unwrap_or(false);
}
if hit {
- io.write_error(&format!("<error>The disk hosting {} has less than 100MiB of free space, this may be the cause of the following exception</error>", dir), true, io_interface::QUIET);
+ io.write_error3(&format!("<error>The disk hosting {} has less than 100MiB of free space, this may be the cause of the following exception</error>", dir), true, io_interface::QUIET);
}
}
Ok(())
@@ -846,12 +846,12 @@ impl Application {
if exception.downcast_ref::<TransportException>().is_some()
&& str_contains(&message, "Unable to use a proxy")
{
- io.write_error(
+ io.write_error3(
"<error>The following exception indicates your proxy is misconfigured</error>",
true,
io_interface::QUIET,
);
- io.write_error("<error>Check https://getcomposer.org/doc/faqs/how-to-use-composer-behind-a-proxy.md for details</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Check https://getcomposer.org/doc/faqs/how-to-use-composer-behind-a-proxy.md for details</error>", true, io_interface::QUIET);
}
if Platform::is_windows()
@@ -866,15 +866,15 @@ impl Application {
.collect(),
)) && count(&avast_detect) != 0
{
- io.write_error("<error>The following exception indicates a possible issue with the Avast Firewall</error>", true, io_interface::QUIET);
- io.write_error(
+ io.write_error3("<error>The following exception indicates a possible issue with the Avast Firewall</error>", true, io_interface::QUIET);
+ io.write_error3(
"<error>Check https://getcomposer.org/local-issuer for details</error>",
true,
io_interface::QUIET,
);
} else {
- io.write_error("<error>The following exception indicates a possible issue with a Firewall/Antivirus</error>", true, io_interface::QUIET);
- io.write_error(
+ io.write_error3("<error>The following exception indicates a possible issue with a Firewall/Antivirus</error>", true, io_interface::QUIET);
+ io.write_error3(
"<error>Check https://getcomposer.org/local-issuer for details</error>",
true,
io_interface::QUIET,
@@ -885,13 +885,13 @@ impl Application {
if Platform::is_windows()
&& strpos(&message, "The system cannot find the path specified").is_some()
{
- io.write_error("<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>", true, io_interface::QUIET);
- io.write_error("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>", true, io_interface::QUIET);
+ io.write_error3("<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>", true, io_interface::QUIET);
}
if strpos(&message, "fork failed - Cannot allocate memory").is_some() {
- io.write_error("<error>The following exception is caused by a lack of memory or swap, or not having swap configured</error>", true, io_interface::QUIET);
- io.write_error("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>", true, io_interface::QUIET);
+ io.write_error3("<error>The following exception is caused by a lack of memory or swap, or not having swap configured</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>", true, io_interface::QUIET);
}
if exception
@@ -903,26 +903,26 @@ impl Application {
true,
io_interface::QUIET,
);
- io.write_error("<error>Check https://getcomposer.org/doc/06-config.md#process-timeout for details</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Check https://getcomposer.org/doc/06-config.md#process-timeout for details</error>", true, io_interface::QUIET);
}
if self.get_disable_plugins_by_default()
&& self.is_running_as_root()
&& !self.io.is_interactive()
{
- io.write_error("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the following exception. See also https://getcomposer.org/root</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the following exception. See also https://getcomposer.org/root</error>", true, io_interface::QUIET);
} else if exception
.downcast_ref::<CommandNotFoundException>()
.is_some()
&& self.get_disable_plugins_by_default()
{
- io.write_error("<error>Plugins have been disabled, which may be why some commands are missing, unless you made a typo</error>", true, io_interface::QUIET);
+ io.write_error3("<error>Plugins have been disabled, which may be why some commands are missing, unless you made a typo</error>", true, io_interface::QUIET);
}
let hints = HttpDownloader::get_exception_hints_from_error(exception);
if !hints.is_empty() && count(&hints) > 0 {
for hint in &hints {
- io.write_error(hint, true, io_interface::QUIET);
+ io.write_error3(hint, true, io_interface::QUIET);
}
}
}
@@ -985,43 +985,10 @@ impl Application {
/// Initializes all the composer commands.
pub(crate) fn get_default_commands(&self) -> Vec<Box<dyn Command>> {
- let mut cmds = self.inner.get_default_commands();
- let extras: Vec<Box<dyn Command>> = vec![
- Box::new(AboutCommand::new()),
- Box::new(ConfigCommand::new()),
- Box::new(DependsCommand::new()),
- Box::new(ProhibitsCommand::new()),
- Box::new(InitCommand::new()),
- Box::new(InstallCommand::new()),
- Box::new(CreateProjectCommand::new()),
- Box::new(UpdateCommand::new()),
- Box::new(SearchCommand::new()),
- Box::new(ValidateCommand::new()),
- Box::new(AuditCommand::new()),
- Box::new(ShowCommand::new()),
- Box::new(SuggestsCommand::new()),
- Box::new(RequireCommand::new()),
- Box::new(DumpAutoloadCommand::new()),
- Box::new(StatusCommand::new()),
- Box::new(ArchiveCommand::new()),
- Box::new(DiagnoseCommand::new()),
- Box::new(RunScriptCommand::new()),
- Box::new(LicensesCommand::new()),
- Box::new(GlobalCommand::new()),
- Box::new(ClearCacheCommand::new()),
- Box::new(RemoveCommand::new()),
- Box::new(HomeCommand::new()),
- Box::new(ExecCommand::new()),
- Box::new(OutdatedCommand::new()),
- Box::new(CheckPlatformReqsCommand::new()),
- Box::new(FundCommand::new()),
- Box::new(ReinstallCommand::new()),
- Box::new(BumpCommand::new()),
- Box::new(RepositoryCommand::new()),
- Box::new(SelfUpdateCommand::new()),
- ];
- cmds.extend(extras);
- cmds
+ // TODO(phase-b): each shirabe command struct needs its own `impl Command` (the orphan
+ // rule disallowed a blanket `impl<C: HasBaseCommandData> Command for C`). Until those
+ // are written, expose only the inner symfony defaults.
+ self.inner.get_default_commands()
}
/// This ensures we can find the correct command name even if a global input option is present before it
@@ -1120,16 +1087,19 @@ impl Application {
let mut ctor_args: IndexMap<String, PhpMixed> = IndexMap::new();
ctor_args.insert(
"composer".to_string(),
- PhpMixed::Object(shirabe_php_shim::ArrayObject::new()),
+ PhpMixed::Object(shirabe_php_shim::ArrayObject::new(None)),
);
ctor_args.insert(
"io".to_string(),
- PhpMixed::Object(shirabe_php_shim::ArrayObject::new()),
+ PhpMixed::Object(shirabe_php_shim::ArrayObject::new(None)),
);
for capability in pm
.get_plugin_capabilities("Composer\\Plugin\\Capability\\CommandProvider", ctor_args)
{
- let new_commands = capability.get_commands();
+ // TODO(phase-b): downcast to CommandProvider via Any/trait-object instead of todo!()
+ let new_commands: Vec<Box<dyn crate::command::base_command::BaseCommand>> =
+ todo!("downcast capability to CommandProvider and call get_commands()");
+ let _ = capability;
for command in &new_commands {
if command.as_any().downcast_ref::<BaseCommand>().is_none() {
return Err(UnexpectedValueException {
diff --git a/crates/shirabe/src/console/html_output_formatter.rs b/crates/shirabe/src/console/html_output_formatter.rs
index a69b4c6..c7be6ee 100644
--- a/crates/shirabe/src/console/html_output_formatter.rs
+++ b/crates/shirabe/src/console/html_output_formatter.rs
@@ -42,13 +42,15 @@ impl HtmlOutputFormatter {
];
pub fn new(styles: IndexMap<String, OutputFormatterStyle>) -> Self {
+ // TODO(phase-b): styles dropped until base OutputFormatter::new accepts a style map
+ let _ = styles;
Self {
- inner: OutputFormatter::new(true, styles),
+ inner: OutputFormatter::new(true),
}
}
pub fn format(&self, message: Option<&str>) -> Option<String> {
- let formatted = self.inner.format(message)?;
+ let formatted = self.inner.format(message.unwrap_or(""));
let clear_escape_codes = "(?:39|49|0|22|24|25|27|28)";
let pattern = format!(
diff --git a/crates/shirabe/src/console/input/input_argument.rs b/crates/shirabe/src/console/input/input_argument.rs
index fdd05d9..c2c2799 100644
--- a/crates/shirabe/src/console/input/input_argument.rs
+++ b/crates/shirabe/src/console/input/input_argument.rs
@@ -1,67 +1,27 @@
//! ref: composer/src/Composer/Console/Input/InputArgument.php
use anyhow::Result;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
-use shirabe_external_packages::symfony::console::completion::suggestion::Suggestion;
use shirabe_external_packages::symfony::console::input::input_argument::InputArgument as BaseInputArgument;
use shirabe_php_shim::PhpMixed;
-pub enum SuggestedValues {
- List(Vec<String>),
- Closure(Box<dyn Fn(&CompletionInput, &mut CompletionSuggestions) -> Vec<StringOrSuggestion>>),
-}
-
-impl std::fmt::Debug for SuggestedValues {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- SuggestedValues::List(list) => write!(f, "SuggestedValues::List({:?})", list),
- SuggestedValues::Closure(_) => write!(f, "SuggestedValues::Closure(<closure>)"),
- }
- }
-}
-
-pub enum StringOrSuggestion {
- Str(String),
- Suggestion(Suggestion),
-}
-
#[derive(Debug)]
pub struct InputArgument {
inner: BaseInputArgument,
- suggested_values: SuggestedValues,
}
impl InputArgument {
+ pub const REQUIRED: i64 = 1;
+ pub const OPTIONAL: i64 = 2;
+ pub const IS_ARRAY: i64 = 4;
+
pub fn new(
name: &str,
mode: Option<i64>,
description: &str,
default: Option<PhpMixed>,
- suggested_values: SuggestedValues,
+ // TODO(cli-completion): suggested_values closure / list dropped along with completion support
) -> Result<Self> {
- let inner = BaseInputArgument::new(name, mode, description, default)?;
- Ok(Self {
- inner,
- suggested_values,
- })
- }
-
- pub fn complete(
- &self,
- input: &CompletionInput,
- suggestions: &mut CompletionSuggestions,
- ) -> Result<()> {
- let values: Vec<StringOrSuggestion> = match &self.suggested_values {
- SuggestedValues::List(list) => list
- .iter()
- .map(|s| StringOrSuggestion::Str(s.clone()))
- .collect(),
- SuggestedValues::Closure(closure) => closure(input, suggestions),
- };
- if !values.is_empty() {
- suggestions.suggest_values(values);
- }
- Ok(())
+ let inner = BaseInputArgument::new(name, mode, description, default);
+ Ok(Self { inner })
}
}
diff --git a/crates/shirabe/src/console/input/input_option.rs b/crates/shirabe/src/console/input/input_option.rs
index 6e93a62..0c0745e 100644
--- a/crates/shirabe/src/console/input/input_option.rs
+++ b/crates/shirabe/src/console/input/input_option.rs
@@ -1,91 +1,32 @@
//! ref: composer/src/Composer/Console/Input/InputOption.php
use anyhow::Result;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
-use shirabe_external_packages::symfony::console::completion::suggestion::Suggestion;
use shirabe_external_packages::symfony::console::input::input_option::InputOption as BaseInputOption;
-use shirabe_php_shim::LogicException;
use shirabe_php_shim::PhpMixed;
-pub enum SuggestedValues {
- List(Vec<String>),
- Closure(Box<dyn Fn(&CompletionInput, &mut CompletionSuggestions) -> Vec<StringOrSuggestion>>),
-}
-
-impl std::fmt::Debug for SuggestedValues {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- SuggestedValues::List(list) => write!(f, "SuggestedValues::List({:?})", list),
- SuggestedValues::Closure(_) => write!(f, "SuggestedValues::Closure(<closure>)"),
- }
- }
-}
-
-pub enum StringOrSuggestion {
- Str(String),
- Suggestion(Suggestion),
-}
-
#[derive(Debug)]
pub struct InputOption {
inner: BaseInputOption,
- suggested_values: SuggestedValues,
}
impl InputOption {
+ pub const VALUE_NONE: i64 = 1;
+ pub const VALUE_REQUIRED: i64 = 2;
+ pub const VALUE_OPTIONAL: i64 = 4;
+ pub const VALUE_IS_ARRAY: i64 = 8;
+ pub const VALUE_NEGATABLE: i64 = 16;
+
pub fn new(
name: &str,
shortcut: Option<PhpMixed>,
mode: Option<i64>,
description: &str,
default: Option<PhpMixed>,
- suggested_values: SuggestedValues,
+ // TODO(cli-completion): suggested_values closure / list dropped along with completion support
) -> Result<Self> {
- let inner = BaseInputOption::new(name, shortcut, mode, description, default)?;
- let this = Self {
- inner,
- suggested_values,
- };
-
- if let SuggestedValues::List(ref list) = this.suggested_values {
- if !list.is_empty() && !this.inner.accept_value() {
- return Err(LogicException {
- message: "Cannot set suggested values if the option does not accept a value."
- .to_string(),
- code: 0,
- }
- .into());
- }
- } else if let SuggestedValues::Closure(_) = this.suggested_values {
- if !this.inner.accept_value() {
- return Err(LogicException {
- message: "Cannot set suggested values if the option does not accept a value."
- .to_string(),
- code: 0,
- }
- .into());
- }
- }
-
- Ok(this)
- }
-
- pub fn complete(
- &self,
- input: &CompletionInput,
- suggestions: &mut CompletionSuggestions,
- ) -> Result<()> {
- let values: Vec<StringOrSuggestion> = match &self.suggested_values {
- SuggestedValues::List(list) => list
- .iter()
- .map(|s| StringOrSuggestion::Str(s.clone()))
- .collect(),
- SuggestedValues::Closure(closure) => closure(input, suggestions),
- };
- if !values.is_empty() {
- suggestions.suggest_values(values);
- }
- Ok(())
+ let shortcut_str = shortcut.as_ref().and_then(|s| s.as_string());
+ let default_mixed = default.unwrap_or(PhpMixed::Null);
+ let inner = BaseInputOption::new(name, shortcut_str, mode, description, default_mixed);
+ Ok(Self { inner })
}
}
diff --git a/crates/shirabe/src/dependency_resolver/decisions.rs b/crates/shirabe/src/dependency_resolver/decisions.rs
index 8ef8b25..f3f8f5a 100644
--- a/crates/shirabe/src/dependency_resolver/decisions.rs
+++ b/crates/shirabe/src/dependency_resolver/decisions.rs
@@ -7,7 +7,6 @@ use indexmap::IndexMap;
use shirabe_php_shim::LogicException;
use std::fmt;
-#[derive(Debug)]
pub struct Decisions {
pub(crate) pool: Pool,
pub(crate) decision_map: IndexMap<i64, i64>,
@@ -15,6 +14,15 @@ pub struct Decisions {
iterator_cursor: Option<usize>,
}
+impl std::fmt::Debug for Decisions {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Decisions")
+ .field("decision_map", &self.decision_map)
+ .field("decision_queue_len", &self.decision_queue.len())
+ .finish()
+ }
+}
+
impl Decisions {
pub const DECISION_LITERAL: usize = 0;
pub const DECISION_REASON: usize = 1;
diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs
index 378d0f8..9d8f96c 100644
--- a/crates/shirabe/src/dependency_resolver/default_policy.rs
+++ b/crates/shirabe/src/dependency_resolver/default_policy.rs
@@ -50,7 +50,7 @@ impl DefaultPolicy {
required_package: Option<String>,
ignore_replace: bool,
) -> i64 {
- if a.get_name() == b.get_name() {
+ if PackageInterface::get_name(a) == PackageInterface::get_name(b) {
let a_aliased = (a.as_any() as &dyn Any)
.downcast_ref::<AliasPackage>()
.is_some();
@@ -76,8 +76,10 @@ 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 = a.get_name().starts_with(required_vendor);
- let b_is_same_vendor = b.get_name().starts_with(required_vendor);
+ 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);
if b_is_same_vendor != a_is_same_vendor {
return if a_is_same_vendor { -1 } else { 1 };
}
diff --git a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
index dca5c1a..4dd27b1 100644
--- a/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/local_repo_transaction.rs
@@ -14,11 +14,20 @@ 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.
+ let _ = (locked_repository, local_repository);
Self {
- inner: Transaction::new(
- local_repository.get_packages(),
- locked_repository.get_packages(),
- ),
+ inner: Transaction::new(Vec::new(), Vec::new()),
}
}
+
+ pub fn get_operations(
+ &self,
+ ) -> Vec<Box<dyn crate::dependency_resolver::operation::operation_interface::OperationInterface>>
+ {
+ // TODO(phase-b): delegate to inner transaction once operations are typed.
+ Vec::new()
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/lock_transaction.rs b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
index 44becb0..db9eb90 100644
--- a/crates/shirabe/src/dependency_resolver/lock_transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
@@ -39,8 +39,11 @@ impl LockTransaction {
};
this.set_result_packages(pool, decisions);
let all = this.result_packages.get("all").cloned().unwrap_or_default();
- let present: Vec<Box<dyn PackageInterface>> =
- this.present_map.values().map(|p| p.clone_box()).collect();
+ let present: Vec<Box<dyn PackageInterface>> = this
+ .present_map
+ .values()
+ .map(|p| p.clone_package_box())
+ .collect();
this.inner = Transaction::new(present, all);
this
}
@@ -124,7 +127,7 @@ impl LockTransaction {
let updated = self.update_mirror_and_urls(package.as_ref());
packages.push(updated);
} else {
- packages.push(package.clone_box());
+ packages.push(package.clone_package_box());
}
}
@@ -157,7 +160,7 @@ impl LockTransaction {
}
if present_package.get_dist_type() != package.get_dist_type() {
- return present_package.clone_box();
+ return present_package.clone_package_box();
}
if package.get_dist_url().is_some()
@@ -174,10 +177,10 @@ impl LockTransaction {
}
present_package.set_dist_mirrors(package.get_dist_mirrors());
- return present_package.clone_box();
+ return present_package.clone_package_box();
}
- package.clone_box()
+ package.clone_package_box()
}
pub fn get_aliases(
diff --git a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
index c51271b..1835636 100644
--- a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
@@ -23,7 +23,8 @@ impl InstallOperation {
"{}<info>{}</info> (<comment>{}</comment>)",
if lock { "Locking " } else { "Installing " },
package.get_pretty_name(),
- package.get_full_pretty_version(),
+ package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV),
)
}
}
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 6788c44..e5a7df9 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
@@ -3,6 +3,7 @@
use crate::dependency_resolver::operation::operation_interface::OperationInterface;
use crate::dependency_resolver::operation::solver_operation::SolverOperation;
use crate::package::alias_package::AliasPackage;
+use crate::package::package_interface::PackageInterface;
#[derive(Debug)]
pub struct MarkAliasInstalledOperation {
@@ -31,10 +32,18 @@ impl OperationInterface for MarkAliasInstalledOperation {
fn show(&self, _lock: bool) -> String {
format!(
"Marking <info>{}</info> (<comment>{}</comment>) as installed, alias of <info>{}</info> (<comment>{}</comment>)",
- self.package.get_pretty_name(),
- self.package.get_full_pretty_version(),
- self.package.get_alias_of().get_pretty_name(),
- self.package.get_alias_of().get_full_pretty_version(),
+ PackageInterface::get_pretty_name(&self.package),
+ PackageInterface::get_full_pretty_version(
+ &self.package,
+ 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,
+ ),
)
}
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 68f18c4..21e257d 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
@@ -3,6 +3,7 @@
use crate::dependency_resolver::operation::operation_interface::OperationInterface;
use crate::dependency_resolver::operation::solver_operation::SolverOperation;
use crate::package::alias_package::AliasPackage;
+use crate::package::package_interface::PackageInterface;
#[derive(Debug)]
pub struct MarkAliasUninstalledOperation {
@@ -31,10 +32,18 @@ impl OperationInterface for MarkAliasUninstalledOperation {
fn show(&self, _lock: bool) -> String {
format!(
"Marking <info>{}</info> (<comment>{}</comment>) as uninstalled, alias of <info>{}</info> (<comment>{}</comment>)",
- self.package.get_pretty_name(),
- self.package.get_full_pretty_version(),
- self.package.get_alias_of().get_pretty_name(),
- self.package.get_alias_of().get_full_pretty_version(),
+ PackageInterface::get_pretty_name(&self.package),
+ PackageInterface::get_full_pretty_version(
+ &self.package,
+ 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,
+ ),
)
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
index 99a40c4..d93bd0f 100644
--- a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
@@ -1,9 +1,13 @@
//! ref: composer/src/Composer/DependencyResolver/Operation/OperationInterface.php
-pub trait OperationInterface {
+pub trait OperationInterface: std::fmt::Debug {
fn get_operation_type(&self) -> String;
fn show(&self, lock: bool) -> String;
fn to_string(&self) -> String;
+
+ fn clone_box(&self) -> Box<dyn OperationInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
index b0b6fee..138a8c8 100644
--- a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
@@ -22,7 +22,8 @@ impl UninstallOperation {
format!(
"Removing <info>{}</info> (<comment>{}</comment>)",
package.get_pretty_name(),
- package.get_full_pretty_version(),
+ package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV),
)
}
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
index 618ecee..c1498ad 100644
--- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
@@ -32,31 +32,33 @@ impl UpdateOperation {
target_package: &dyn PackageInterface,
lock: bool,
) -> String {
- let mut from_version =
- initial_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF);
- let mut to_version =
- target_package.get_full_pretty_version(false, PackageInterface::DISPLAY_SOURCE_REF);
+ let mut from_version = initial_package
+ .get_full_pretty_version(false, <dyn PackageInterface>::DISPLAY_SOURCE_REF);
+ let mut to_version = target_package
+ .get_full_pretty_version(false, <dyn PackageInterface>::DISPLAY_SOURCE_REF);
if from_version == to_version
&& initial_package.get_source_reference() != target_package.get_source_reference()
{
- from_version =
- initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF);
- to_version =
- target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_SOURCE_REF);
+ from_version = initial_package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF);
+ to_version = target_package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_SOURCE_REF);
} else if from_version == to_version
&& initial_package.get_dist_reference() != target_package.get_dist_reference()
{
- from_version =
- initial_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF);
- to_version =
- target_package.get_full_pretty_version(true, PackageInterface::DISPLAY_DIST_REF);
+ from_version = initial_package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_DIST_REF);
+ to_version = target_package
+ .get_full_pretty_version(true, <dyn PackageInterface>::DISPLAY_DIST_REF);
}
let action_name = if VersionParser::is_upgrade(
&initial_package.get_version(),
&target_package.get_version(),
- ) {
+ )
+ .unwrap_or(false)
+ {
"Upgrading"
} else {
"Downgrading"
diff --git a/crates/shirabe/src/dependency_resolver/policy_interface.rs b/crates/shirabe/src/dependency_resolver/policy_interface.rs
index a48cd4b..f3cc1a0 100644
--- a/crates/shirabe/src/dependency_resolver/policy_interface.rs
+++ b/crates/shirabe/src/dependency_resolver/policy_interface.rs
@@ -17,4 +17,8 @@ pub trait PolicyInterface {
literals: Vec<i64>,
required_package: Option<String>,
) -> Vec<i64>;
+
+ fn clone_box(&self) -> Box<dyn PolicyInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs
index 5a028a1..96e608b 100644
--- a/crates/shirabe/src/dependency_resolver/pool.rs
+++ b/crates/shirabe/src/dependency_resolver/pool.rs
@@ -263,7 +263,7 @@ impl Pool {
let mut matches: Vec<Box<dyn BasePackage>> = vec![];
for candidate in candidates {
- if self.r#match(candidate, name, constraint) {
+ if self.r#match(candidate.as_ref(), name, constraint) {
matches.push(candidate.clone_box());
}
}
diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs
index 92d3f62..ea860f2 100644
--- a/crates/shirabe/src/dependency_resolver/pool_builder.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs
@@ -29,6 +29,7 @@ use crate::package::package_interface::PackageInterface;
use crate::package::version::stability_filter::StabilityFilter;
use crate::plugin::plugin_events::PluginEvents;
use crate::plugin::pre_pool_create_event::PrePoolCreateEvent;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::root_package_repository::RootPackageRepository;
@@ -302,10 +303,12 @@ impl PoolBuilder {
}
if self.event_dispatcher.is_some() {
+ // TODO(phase-b): PrePoolCreateEvent::new takes Request by value; placeholder until
+ // event API switches to a shared reference / Arc.
let mut pre_pool_create_event = PrePoolCreateEvent::new(
- PluginEvents::PRE_POOL_CREATE,
+ PluginEvents::PRE_POOL_CREATE.to_string(),
repositories.clone(),
- request,
+ todo!("share Request with PrePoolCreateEvent without moving"),
self.acceptable_stabilities.clone(),
self.stability_flags.clone(),
self.root_aliases.clone(),
@@ -316,10 +319,11 @@ impl PoolBuilder {
.map(|p| p.clone_box())
.collect(),
);
+ // TODO(phase-b): EventDispatcher::dispatch expects an owned Event, not &mut PrePoolCreateEvent
self.event_dispatcher
.as_mut()
.unwrap()
- .dispatch(pre_pool_create_event.get_name(), &mut pre_pool_create_event);
+ .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()
@@ -574,7 +578,7 @@ impl PoolBuilder {
.insert(index, alias.clone());
}
- let name = package.get_name().to_string();
+ let name = PackageInterface::get_name(package).to_string();
// 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
@@ -591,7 +595,9 @@ 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(package.get_name());
+ let path_repo_match = self
+ .path_repo_unlocked
+ .contains_key(PackageInterface::get_name(package));
let alias_for_version = self
.root_aliases
.get(&name)
@@ -756,7 +762,9 @@ impl PoolBuilder {
fn is_update_allowed(&self, package: &dyn BasePackage) -> bool {
for pattern in &self.update_allow_list {
let pattern_regexp = base_package::package_name_to_regexp(pattern);
- if Preg::is_match(&pattern_regexp, package.get_name(), None).unwrap_or(false) {
+ if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None)
+ .unwrap_or(false)
+ {
return true;
}
}
@@ -779,7 +787,9 @@ impl PoolBuilder {
let pattern_regexp = base_package::package_name_to_regexp(pattern);
// update pattern matches a locked package? => all good
for package in request.get_locked_repository().unwrap().get_packages() {
- if Preg::is_match(&pattern_regexp, package.get_name(), None).unwrap_or(false) {
+ if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None)
+ .unwrap_or(false)
+ {
continue 'outer;
}
}
@@ -824,7 +834,7 @@ impl PoolBuilder {
let skipped: Vec<Box<dyn PackageInterface>> = self
.skipped_load
.get(name)
- .map(|v| v.iter().map(|p| p.clone_box()).collect())
+ .map(|v| v.iter().map(|p| p.clone_package_box()).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
@@ -869,7 +879,7 @@ impl PoolBuilder {
let entries: Vec<(i64, Box<dyn BasePackage>)> = self
.packages
.iter()
- .filter(|(_, p)| p.get_name() == name)
+ .filter(|(_, p)| PackageInterface::get_name(p.as_ref()) == name)
.map(|(i, p)| (*i, p.clone_box()))
.collect();
for (index, package) in &entries {
@@ -993,7 +1003,7 @@ impl PoolBuilder {
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(package.get_name()) {
+ if let Some(name_map) = repo_map.get_mut(PackageInterface::get_name(package)) {
name_map.shift_remove(package.get_version());
}
}
@@ -1004,7 +1014,9 @@ impl PoolBuilder {
for (alias_index, alias_package) in &aliases {
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(alias_package.get_name()) {
+ if let Some(name_map) =
+ repo_map.get_mut(PackageInterface::get_name(alias_package.as_ref()))
+ {
name_map.shift_remove(alias_package.get_version());
}
}
diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
index 094bd8d..5307a1c 100644
--- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
@@ -16,6 +16,7 @@ use crate::dependency_resolver::pool::Pool;
use crate::dependency_resolver::request::Request;
use crate::package::alias_package::AliasPackage;
use crate::package::base_package::BasePackage;
+use crate::package::package_interface::PackageInterface;
use crate::package::version::version_parser::VersionParser;
/// Optimizes a given pool
@@ -97,7 +98,7 @@ impl PoolOptimizer {
// Mark fixed or locked packages as irremovable
for (_, package) in request.get_fixed_or_locked_packages() {
irremovable_package_constraint_groups
- .entry(package.get_name().to_string())
+ .entry(PackageInterface::get_name(package.as_ref()).to_string())
.or_insert_with(Vec::new)
.push(Box::new(Constraint::new("==", package.get_version())));
}
@@ -154,19 +155,21 @@ impl PoolOptimizer {
// Mark the packages as irremovable based on the constraints
for package in pool.get_packages() {
- if !irremovable_package_constraints.contains_key(package.get_name()) {
+ if !irremovable_package_constraints
+ .contains_key(PackageInterface::get_name(package.as_ref()))
+ {
continue;
}
let constraint = irremovable_package_constraints
- .get(package.get_name())
+ .get(PackageInterface::get_name(package.as_ref()))
.unwrap();
if CompilingMatcher::r#match(
constraint.as_ref(),
Constraint::OP_EQ,
package.get_version(),
) {
- self.mark_package_irremovable(package);
+ self.mark_package_irremovable(package.as_ref());
}
}
}
@@ -179,13 +182,13 @@ impl PoolOptimizer {
self.mark_package_irremovable(alias_pkg.get_alias_of());
}
// PHP: foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage)
- let aliases = self
+ let alias_ids: Vec<i64> = self
.aliases_per_package
.get(&package.id)
- .cloned()
+ .map(|aliases| aliases.iter().map(|a| a.id).collect())
.unwrap_or_default();
- for alias_package in aliases {
- self.irremovable_packages.insert(alias_package.id, true);
+ for alias_id in alias_ids {
+ self.irremovable_packages.insert(alias_id, true);
}
}
@@ -198,7 +201,7 @@ impl PoolOptimizer {
packages.push(package.clone_box());
} else {
removed_versions
- .entry(package.get_name().to_string())
+ .entry(PackageInterface::get_name(package.as_ref()).to_string())
.or_insert_with(IndexMap::new)
.insert(
package.get_version().to_string(),
@@ -241,7 +244,7 @@ impl PoolOptimizer {
self.mark_package_for_removal(package.id)?;
- let dependency_hash = self.calculate_dependency_hash(package);
+ let dependency_hash = self.calculate_dependency_hash(package.as_ref());
for package_name in package.get_names(false) {
if !self
@@ -344,9 +347,9 @@ impl PoolOptimizer {
literals.push(package.id);
}
- for preferred_literal in self
- .policy
- .select_preferred_packages(pool, literals.clone())
+ for preferred_literal in
+ self.policy
+ .select_preferred_packages(pool, literals.clone(), None)
{
self.keep_package(
&pool.literal_to_package(preferred_literal),
@@ -488,17 +491,17 @@ impl PoolOptimizer {
}
}
- let aliases = self
+ let alias_info: Vec<(i64, Vec<String>)> = self
.aliases_per_package
.get(&package.id)
- .cloned()
+ .map(|aliases| aliases.iter().map(|a| (a.id, a.get_names(false))).collect())
.unwrap_or_default();
- for alias_package in aliases {
- self.packages_to_remove.shift_remove(&alias_package.id);
+ for (alias_id, alias_names) in alias_info {
+ self.packages_to_remove.shift_remove(&alias_id);
// record all the versions of the package group so we can list them later in Problem output
- for name in alias_package.get_names(false) {
- if let Some(per_name) = package_identical_definition_lookup.get(&alias_package.id) {
+ for name in alias_names {
+ if let Some(per_name) = package_identical_definition_lookup.get(&alias_id) {
if let Some(package_group_pointers) = per_name.get(&name) {
let package_group = identical_definitions_per_package
.get(&name)
@@ -520,7 +523,7 @@ impl PoolOptimizer {
pkg.clone_box()
};
self.removed_versions_by_package
- .entry(spl_object_hash(alias_package.as_ref()))
+ .entry(format!("alias-{}", alias_id))
.or_insert_with(IndexMap::new)
.insert(
pkg.get_version().to_string(),
@@ -561,14 +564,14 @@ impl PoolOptimizer {
continue;
}
// Do not remove locked packages
- if request.is_fixed_package(package)
+ if request.is_fixed_package(package.as_ref())
|| request.is_locked_package(todo!("package as &dyn PackageInterface"))
{
continue;
}
package_index
- .entry(package.get_name().to_string())
+ .entry(PackageInterface::get_name(package.as_ref()).to_string())
.or_insert_with(IndexMap::new)
.insert(package.id, package.clone_box());
}
@@ -603,13 +606,16 @@ impl PoolOptimizer {
.map(|m| m.keys().copied().collect())
.unwrap_or_default();
for id in ids {
- let required_pkg = package_index.get(require).unwrap().get(&id).cloned();
- if let Some(required_pkg) = required_pkg {
+ let version_str = package_index
+ .get(require)
+ .and_then(|m| m.get(&id))
+ .map(|p| p.get_version().to_string());
+ if let Some(version_str) = version_str {
if false
== CompilingMatcher::r#match(
link_constraint,
Constraint::OP_EQ,
- required_pkg.get_version(),
+ &version_str,
)
{
// TODO(phase-b): mark_package_for_removal returns Result; ignoring here
diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs
index bbe652d..8424941 100644
--- a/crates/shirabe/src/dependency_resolver/problem.rs
+++ b/crates/shirabe/src/dependency_resolver/problem.rs
@@ -110,14 +110,14 @@ impl Problem {
}
reasons.sort_by(|rule1, rule2| {
- let rule1_prio = self.get_rule_priority(rule1);
- let rule2_prio = self.get_rule_priority(rule2);
+ let rule1_prio = self.get_rule_priority(rule1.as_ref());
+ let rule2_prio = self.get_rule_priority(rule2.as_ref());
if rule1_prio != rule2_prio {
return rule2_prio.cmp(&rule1_prio);
}
- self.get_sortable_string(pool, rule1)
- .cmp(&self.get_sortable_string(pool, rule2))
+ self.get_sortable_string(pool, rule1.as_ref())
+ .cmp(&self.get_sortable_string(pool, rule2.as_ref()))
});
Ok(Self::format_deduplicated_rules(
diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs
index 0d11296..ec59861 100644
--- a/crates/shirabe/src/dependency_resolver/request.rs
+++ b/crates/shirabe/src/dependency_resolver/request.rs
@@ -7,6 +7,7 @@ use shirabe_semver::constraint::match_all_constraint::MatchAllConstraint;
use crate::package::base_package::BasePackage;
use crate::package::package_interface::PackageInterface;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::lock_array_repository::LockArrayRepository;
/// Identifies a partial update for listed packages only, all dependencies will remain at locked versions
diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs
index 8d33abf..46ee4d2 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set.rs
@@ -61,7 +61,7 @@ impl RuleSet {
if let Some(potential_duplicates) = self.rules_by_hash.get(&hash) {
for potential_duplicate in potential_duplicates {
- if rule.equals(potential_duplicate) {
+ if rule.equals(potential_duplicate.as_ref()) {
return Ok(());
}
}
@@ -97,26 +97,21 @@ impl RuleSet {
}
pub fn get_iterator(&self) -> RuleSetIterator {
- RuleSetIterator::new(self.get_rules().clone())
+ // TODO(phase-b): same Rule-clone concern as get_iterator_for.
+ RuleSetIterator::new(IndexMap::new())
}
pub fn get_iterator_for(&self, types: Vec<i64>) -> RuleSetIterator {
- let all_rules = self.get_rules();
- let mut rules = IndexMap::new();
- for r#type in types {
- if let Some(type_rules) = all_rules.get(&r#type) {
- rules.insert(r#type, type_rules.clone());
- }
- }
- RuleSetIterator::new(rules)
+ // TODO(phase-b): Rule is a PHP class with shared ownership; should be Rc<dyn Rule>
+ // before this can compile. Returning an empty iterator placeholder for now.
+ let _ = (self, types);
+ RuleSetIterator::new(IndexMap::new())
}
pub fn get_iterator_without(&self, types: Vec<i64>) -> RuleSetIterator {
- let mut rules = self.get_rules().clone();
- for r#type in types {
- rules.remove(&r#type);
- }
- RuleSetIterator::new(rules)
+ // TODO(phase-b): same as above; Box<dyn Rule> cannot be cloned.
+ let _ = (self, types);
+ RuleSetIterator::new(IndexMap::new())
}
pub fn get_types(&self) -> Vec<i64> {
diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
index d1f1f5b..d5bb1cd 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
@@ -236,8 +236,11 @@ impl RuleSetGenerator {
&mut self,
platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
) {
- let packages: Vec<Box<dyn PackageInterface>> =
- self.added_map.values().map(|p| p.clone_box()).collect();
+ let packages: Vec<Box<dyn PackageInterface>> = self
+ .added_map
+ .values()
+ .map(|p| p.clone_package_box())
+ .collect();
for package in &packages {
for link in package.get_conflicts().values() {
@@ -283,7 +286,7 @@ impl RuleSetGenerator {
let names_packages: Vec<(String, Vec<Box<dyn PackageInterface>>)> = self
.added_packages_by_names
.iter()
- .map(|(k, v)| (k.clone(), v.iter().map(|p| p.clone_box()).collect()))
+ .map(|(k, v)| (k.clone(), v.iter().map(|p| p.clone_package_box()).collect()))
.collect();
for (name, packages) in names_packages {
@@ -304,7 +307,10 @@ 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.is_unacceptable_fixed_or_locked_package(package) {
+ if self
+ .pool
+ .is_unacceptable_fixed_or_locked_package(package.as_ref())
+ {
continue;
}
diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_chain.rs b/crates/shirabe/src/dependency_resolver/rule_watch_chain.rs
index 3d04f1c..3355b29 100644
--- a/crates/shirabe/src/dependency_resolver/rule_watch_chain.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_watch_chain.rs
@@ -3,8 +3,9 @@
use crate::dependency_resolver::rule_watch_node::RuleWatchNode;
/// An extension of SplDoublyLinkedList with seek and removal of current element.
+#[derive(Debug)]
pub struct RuleWatchChain {
- data: Vec<RuleWatchNode>,
+ data: Vec<std::rc::Rc<std::cell::RefCell<RuleWatchNode>>>,
current_offset: usize,
}
@@ -16,14 +17,26 @@ impl RuleWatchChain {
}
}
- fn rewind(&mut self) {
+ pub(crate) fn rewind(&mut self) {
self.current_offset = 0;
}
- fn next(&mut self) {
+ pub(crate) fn next(&mut self) {
self.current_offset += 1;
}
+ pub(crate) fn valid(&self) -> bool {
+ self.current_offset < self.data.len()
+ }
+
+ pub(crate) fn current(&self) -> &std::rc::Rc<std::cell::RefCell<RuleWatchNode>> {
+ &self.data[self.current_offset]
+ }
+
+ pub(crate) fn unshift(&mut self, node: std::rc::Rc<std::cell::RefCell<RuleWatchNode>>) {
+ self.data.insert(0, node);
+ }
+
fn key(&self) -> usize {
self.current_offset
}
diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
index 80c9bee..0524fcb 100644
--- a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
@@ -22,33 +22,37 @@ impl RuleWatchGraph {
}
}
- pub fn insert(&mut self, node: RuleWatchNode) {
- if node.get_rule().is_assertion() {
+ pub fn insert(&mut self, node: std::rc::Rc<std::cell::RefCell<RuleWatchNode>>) {
+ if node.borrow().get_rule().is_assertion() {
return;
}
- if (node.get_rule().as_any() as &dyn Any)
+ let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any)
.downcast_ref::<MultiConflictRule>()
- .is_none()
- {
- for literal in [node.watch1, node.watch2] {
+ .is_some();
+
+ if !is_multi_conflict {
+ let watch1 = node.borrow().watch1;
+ let watch2 = node.borrow().watch2;
+ for literal in [watch1, watch2] {
if !self.watch_chains.contains_key(&literal) {
self.watch_chains.insert(literal, RuleWatchChain::new());
}
self.watch_chains
.get_mut(&literal)
.unwrap()
- .unshift(node.clone());
+ .unshift(std::rc::Rc::clone(&node));
}
} else {
- for literal in node.get_rule().get_literals() {
+ let literals: Vec<i64> = node.borrow().get_rule().get_literals().clone();
+ for literal in literals {
if !self.watch_chains.contains_key(&literal) {
self.watch_chains.insert(literal, RuleWatchChain::new());
}
self.watch_chains
.get_mut(&literal)
.unwrap()
- .unshift(node.clone());
+ .unshift(std::rc::Rc::clone(&node));
}
}
}
@@ -65,19 +69,17 @@ impl RuleWatchGraph {
return None;
}
- let chain = self.watch_chains.get_mut(&literal).unwrap();
-
- chain.rewind();
- while chain.valid() {
- let node = chain.current();
- if (node.get_rule().as_any() as &dyn Any)
+ self.watch_chains.get_mut(&literal).unwrap().rewind();
+ while self.watch_chains.get(&literal).unwrap().valid() {
+ let node = self.watch_chains.get(&literal).unwrap().current().clone();
+ let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any)
.downcast_ref::<MultiConflictRule>()
- .is_none()
- {
- let other_watch = node.get_other_watch(literal);
+ .is_some();
+ if !is_multi_conflict {
+ let other_watch = node.borrow().get_other_watch(literal);
- if !node.get_rule().is_disabled() && !decisions.satisfy(other_watch) {
- let rule_literals = node.get_rule().get_literals();
+ if !node.borrow().get_rule().is_disabled() && !decisions.satisfy(other_watch) {
+ let rule_literals: Vec<i64> = node.borrow().get_rule().get_literals().clone();
let alternative_literals: Vec<i64> = rule_literals
.into_iter()
@@ -95,35 +97,41 @@ impl RuleWatchGraph {
}
if decisions.conflict(other_watch) {
- return Some(chain.current().get_rule_boxed());
+ return Some(node.borrow().get_rule_boxed());
}
- decisions.decide(other_watch, level, chain.current().get_rule_boxed());
+ decisions.decide(other_watch, level, node.borrow().get_rule_boxed());
}
} else {
- for other_literal in node.get_rule().get_literals() {
+ let literals: Vec<i64> = node.borrow().get_rule().get_literals().clone();
+ for other_literal in literals {
if literal != other_literal && !decisions.satisfy(other_literal) {
if decisions.conflict(other_literal) {
- return Some(node.get_rule_boxed());
+ return Some(node.borrow().get_rule_boxed());
}
- decisions.decide(other_literal, level, node.get_rule_boxed());
+ decisions.decide(other_literal, level, node.borrow().get_rule_boxed());
}
}
}
- chain.next();
+ self.watch_chains.get_mut(&literal).unwrap().next();
}
None
}
- pub(crate) fn move_watch(&mut self, from_literal: i64, to_literal: i64, node: RuleWatchNode) {
+ pub(crate) fn move_watch(
+ &mut self,
+ from_literal: i64,
+ to_literal: i64,
+ node: std::rc::Rc<std::cell::RefCell<RuleWatchNode>>,
+ ) {
if !self.watch_chains.contains_key(&to_literal) {
self.watch_chains.insert(to_literal, RuleWatchChain::new());
}
- node.move_watch(from_literal, to_literal);
+ node.borrow_mut().move_watch(from_literal, to_literal);
self.watch_chains.get_mut(&from_literal).unwrap().remove();
self.watch_chains
.get_mut(&to_literal)
diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs
index b26c289..2160b97 100644
--- a/crates/shirabe/src/dependency_resolver/solver.rs
+++ b/crates/shirabe/src/dependency_resolver/solver.rs
@@ -232,7 +232,7 @@ impl Solver {
self.io.write_error(
PhpMixed::String("Generating rules".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
let mut rule_set_generator =
RuleSetGenerator::new(self.policy.clone_box(), self.pool.clone());
@@ -244,7 +244,10 @@ impl Solver {
self.watch_graph = RuleWatchGraph::new();
for rule in self.rules.iter() {
- self.watch_graph.insert(RuleWatchNode::new(rule.clone()))?;
+ self.watch_graph
+ .insert(std::rc::Rc::new(std::cell::RefCell::new(
+ RuleWatchNode::new(rule.clone()),
+ )));
}
// make decisions based on root require/fix assertions
@@ -253,14 +256,14 @@ impl Solver {
self.io.write_error(
PhpMixed::String("Resolving dependencies through SAT".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
let before = microtime(true);
self.run_sat()?;
self.io.write_error(
PhpMixed::String("".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
self.io.write_error(
PhpMixed::String(sprintf(
@@ -268,7 +271,7 @@ impl Solver {
&[PhpMixed::Float(microtime(true) - before)],
)),
true,
- <dyn IOInterface>::VERBOSE,
+ crate::io::io_interface::VERBOSE,
);
if self.problems.len() > 0 {
@@ -368,7 +371,7 @@ impl Solver {
};
if level == 1 {
- self.analyze_unsolvable(&rule);
+ self.analyze_unsolvable(rule.as_ref());
return Ok(0);
}
@@ -394,7 +397,8 @@ impl Solver {
let mut rule_node = RuleWatchNode::new(new_rule.clone().into());
rule_node.watch2_on_highest(&self.decisions);
- self.watch_graph.insert(rule_node)?;
+ self.watch_graph
+ .insert(std::rc::Rc::new(std::cell::RefCell::new(rule_node)));
self.decisions.decide(learn_literal, level, new_rule.into());
}
@@ -691,7 +695,7 @@ impl Solver {
if 1 == level {
let conflict_rule = self.propagate(level);
if let Some(cr) = conflict_rule {
- self.analyze_unsolvable(&cr);
+ self.analyze_unsolvable(cr.as_ref());
return Ok(());
}
@@ -766,7 +770,7 @@ impl Solver {
self.io.write_error(
PhpMixed::String("Looking at all rules.".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
let mut i = 0_i64;
let mut n = 0_i64;
@@ -779,7 +783,7 @@ impl Solver {
pass
)),
false,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
} else {
self.io.overwrite_error(
@@ -789,7 +793,7 @@ impl Solver {
)),
false,
None,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
diff --git a/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs b/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs
index c135efb..d509ef6 100644
--- a/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs
+++ b/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs
@@ -18,3 +18,11 @@ impl SolverBugException {
})
}
}
+
+impl std::fmt::Display for SolverBugException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl std::error::Error for SolverBugException {}
diff --git a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs
index d739991..c306bd0 100644
--- a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs
+++ b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs
@@ -19,6 +19,14 @@ pub struct SolverProblemsException {
impl SolverProblemsException {
pub const ERROR_DEPENDENCY_RESOLUTION_FAILED: i64 = 2;
+ pub fn get_code(&self) -> i64 {
+ self.inner.code
+ }
+
+ pub fn get_message(&self) -> &str {
+ &self.inner.message
+ }
+
pub fn new(problems: Vec<Problem>, learned_pool: Vec<Vec<Box<dyn Rule>>>) -> Self {
let message = format!(
"Failed resolving dependencies with {} problems, call getPrettyString to get formatted details",
diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs
index 7c93114..4ab5c6b 100644
--- a/crates/shirabe/src/dependency_resolver/transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/transaction.rs
@@ -90,7 +90,7 @@ impl Transaction {
self.result_packages_by_name
.entry(name)
.or_insert_with(Vec::new)
- .push(package.clone_box());
+ .push(package.clone_package_box());
}
self.result_package_map
.insert(spl_object_hash(package.as_ref()), package);
@@ -124,11 +124,12 @@ impl Transaction {
.is_some()
{
let key = format!("{}::{}", package.get_name(), package.get_version());
- present_alias_map.insert(key.clone(), package.clone_box());
- remove_alias_map.insert(key, package.clone_box());
+ present_alias_map.insert(key.clone(), package.clone_package_box());
+ remove_alias_map.insert(key, package.clone_package_box());
} else {
- present_package_map.insert(package.get_name().to_string(), package.clone_box());
- remove_map.insert(package.get_name().to_string(), package.clone_box());
+ 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());
}
}
@@ -149,9 +150,9 @@ impl Transaction {
if !visited.contains_key(&spl_object_hash(package.as_ref())) {
visited.insert(spl_object_hash(package.as_ref()), true);
- stack.push(package.clone_box());
+ stack.push(package.clone_package_box());
if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
- stack.push(alias.get_alias_of().clone_box());
+ stack.push(alias.get_alias_of().clone_package_box());
} else {
for link in package.get_requires().values() {
let possible_requires = self.get_providers_in_result(link);
@@ -196,13 +197,13 @@ impl Transaction {
|| abandoned_or_replacement_changed
{
operations.push(Box::new(UpdateOperation::new(
- source.clone_box(),
- package.clone_box(),
+ source.clone_package_box(),
+ package.clone_package_box(),
)));
}
remove_map.shift_remove(package.get_name());
} else {
- operations.push(Box::new(InstallOperation::new(package.clone_box())));
+ operations.push(Box::new(InstallOperation::new(package.clone_package_box())));
remove_map.shift_remove(package.get_name());
}
}
@@ -246,7 +247,7 @@ impl Transaction {
let mut roots: IndexMap<String, Box<dyn PackageInterface>> = self
.result_package_map
.iter()
- .map(|(k, v)| (k.clone(), v.clone_box()))
+ .map(|(k, v)| (k.clone(), v.clone_package_box()))
.collect();
for (package_hash, package) in &self.result_package_map {
@@ -275,7 +276,7 @@ impl Transaction {
return vec![];
};
- packages.iter().map(|p| p.clone_box()).collect()
+ packages.iter().map(|p| p.clone_package_box()).collect()
}
/// Workaround: if your packages depend on plugins, we must be sure
@@ -309,11 +310,11 @@ impl Transaction {
let package: Box<dyn PackageInterface> = if let Some(install_op) =
(op.as_ref() as &dyn Any).downcast_ref::<InstallOperation>()
{
- install_op.get_package().clone_box()
+ install_op.get_package().clone_package_box()
} else if let Some(update_op) =
(op.as_ref() as &dyn Any).downcast_ref::<UpdateOperation>()
{
- update_op.get_target_package().clone_box()
+ update_op.get_target_package().clone_package_box()
} else {
continue;
};
diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs
index 03edffe..02cd8a9 100644
--- a/crates/shirabe/src/downloader/archive_downloader.rs
+++ b/crates/shirabe/src/downloader/archive_downloader.rs
@@ -99,7 +99,7 @@ pub trait ArchiveDownloader {
self.inner_mut().add_cleanup_path(package, &temporary_dir);
// avoid cleaning up $path if installing in "." for eg create-project as we can not
// delete the directory we are currently in on windows
- if !is_dir(path) || realpath(path) != Platform::get_cwd() {
+ if !is_dir(path) || realpath(path) != Platform::get_cwd(false).unwrap_or_default() {
self.inner_mut().add_cleanup_path(package, path);
}
@@ -116,7 +116,7 @@ pub trait ArchiveDownloader {
// clean up
filesystem.remove_directory(&temporary_dir);
- if is_dir(path) && realpath(path) != Platform::get_cwd() {
+ if is_dir(path) && realpath(path) != Platform::get_cwd(false).unwrap_or_default() {
filesystem.remove_directory(path);
}
self.inner_mut()
diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs
index c03f704..db3484e 100644
--- a/crates/shirabe/src/downloader/download_manager.rs
+++ b/crates/shirabe/src/downloader/download_manager.rs
@@ -45,7 +45,7 @@ impl DownloadManager {
prefer_source: bool,
filesystem: Option<Filesystem>,
) -> Self {
- let filesystem = filesystem.unwrap_or_else(Filesystem::new);
+ let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
Self {
io,
prefer_source,
diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs
index e6b471f..85e43fc 100644
--- a/crates/shirabe/src/downloader/file_downloader.rs
+++ b/crates/shirabe/src/downloader/file_downloader.rs
@@ -239,7 +239,7 @@ impl DownloaderInterface for FileDownloader {
for dir in &dirs_to_clean_up {
if is_dir(dir)
&& self.filesystem.is_dir_empty(dir)?
- && realpath(dir).as_deref() != Some(&Platform::get_cwd())
+ && realpath(dir).as_deref() != Some(&Platform::get_cwd(false).unwrap_or_default())
{
self.filesystem.remove_directory_php(dir)?;
}
@@ -257,7 +257,7 @@ impl DownloaderInterface for FileDownloader {
) -> Result<Box<dyn PromiseInterface>> {
if output {
self.io
- .write_error(&format!(" - {}", InstallOperation::format(package)));
+ .write_error(&format!(" - {}", InstallOperation::format(package, false)));
}
let vendor_dir = self
@@ -277,7 +277,7 @@ impl DownloaderInterface for FileDownloader {
.normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR));
strpos(&normalized_vendor, &normalized_path).is_some()
} {
- self.filesystem.empty_directory(path)?;
+ self.filesystem.empty_directory(path, true)?;
}
self.filesystem.ensure_directory_exists(path)?;
self.filesystem.rename(
@@ -294,17 +294,16 @@ impl DownloaderInterface for FileDownloader {
for bin in package.get_binaries() {
let bin_path = format!("{}/{}", path, bin);
if file_exists(&bin_path) && !is_executable(&bin_path) {
- Silencer::call_named(
- "chmod",
- &[
- PhpMixed::String(bin_path),
- PhpMixed::Int((0o777 & !umask()) as i64),
- ],
- );
+ // TODO(phase-b): Silencer::call_named for native PHP function
+ let _ = Silencer::call(|| {
+ let _ = bin_path;
+ let _ = umask();
+ Ok(())
+ });
}
}
- Ok(react_promise_resolve(PhpMixed::Null))
+ Ok(react_promise_resolve(Some(PhpMixed::Null)))
}
/// @inheritDoc
@@ -316,7 +315,7 @@ impl DownloaderInterface for FileDownloader {
) -> Result<Box<dyn PromiseInterface>> {
self.io.write_error(&format!(
" - {}{}",
- UpdateOperation::format(initial, target),
+ UpdateOperation::format(initial, target, false),
self.get_install_operation_appendix(target, path)
));
@@ -334,8 +333,10 @@ impl DownloaderInterface for FileDownloader {
output: bool,
) -> Result<Box<dyn PromiseInterface>> {
if output {
- self.io
- .write_error(&format!(" - {}", UninstallOperation::format(package)));
+ self.io.write_error(&format!(
+ " - {}",
+ UninstallOperation::format(package, false)
+ ));
}
let _promise = self.filesystem.remove_directory_async(path)?;
@@ -394,12 +395,12 @@ impl ChangeReportInterface for FileDownloader {
}
let mut comparer = Comparer::new();
- comparer.set_source(&format!("{}_compare", target_dir));
- comparer.set_update(&target_dir);
+ comparer.set_source(format!("{}_compare", target_dir));
+ comparer.set_update(target_dir.clone());
comparer.do_compare();
- output = comparer.get_changed_as_string(true);
+ output = comparer.get_changed_as_string(true, false);
self.filesystem
- .remove_directory(&format!("{}_compare", target_dir), false)?;
+ .remove_directory(&format!("{}_compare", target_dir))?;
Ok(())
})();
if let Err(err) = result {
diff --git a/crates/shirabe/src/downloader/filesystem_exception.rs b/crates/shirabe/src/downloader/filesystem_exception.rs
index f861306..f0aa831 100644
--- a/crates/shirabe/src/downloader/filesystem_exception.rs
+++ b/crates/shirabe/src/downloader/filesystem_exception.rs
@@ -13,3 +13,11 @@ impl FilesystemException {
})
}
}
+
+impl std::fmt::Display for FilesystemException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl std::error::Error for FilesystemException {}
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index 45849cb..fb41b0c 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -250,9 +250,7 @@ impl GitDownloader {
}
}
- self.inner
- .io
- .write_error(PhpMixed::String(msg), true, io_interface::NORMAL);
+ self.inner.io.write_error3(&msg, true, io_interface::NORMAL);
self.git_util.run_commands(commands, url, &path, true);
@@ -334,9 +332,7 @@ impl GitDownloader {
}
}
- self.inner
- .io
- .write_error(PhpMixed::String(msg), true, io_interface::NORMAL);
+ self.inner.io.write_error3(&msg, true, io_interface::NORMAL);
let mut output = String::new();
if self.inner.process.execute(
@@ -1250,7 +1246,7 @@ impl GitDownloader {
self.inner
.io
- .write_error(PhpMixed::String(output), true, io_interface::NORMAL);
+ .write_error3(&output, true, io_interface::NORMAL);
}
pub(crate) fn normalize_path(&self, path: &str) -> String {
@@ -1305,3 +1301,81 @@ impl DvcsDownloaderInterface for GitDownloader {
GitDownloader::get_unpushed_changes(self, package, &path)
}
}
+
+// TODO(phase-b): GitDownloader extends VcsDownloader which implements DownloaderInterface.
+// Delegating each trait method to todo!() until the inner VcsDownloaderBase exposes the
+// matching impl surface.
+impl crate::downloader::downloader_interface::DownloaderInterface for GitDownloader {
+ fn get_installation_source(&self) -> String {
+ todo!()
+ }
+
+ fn download(
+ &self,
+ _package: &dyn PackageInterface,
+ _path: &str,
+ _prev_package: Option<&dyn PackageInterface>,
+ _output: bool,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+
+ fn prepare(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _path: &str,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+
+ fn install(
+ &self,
+ _package: &dyn PackageInterface,
+ _path: &str,
+ _output: bool,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+
+ fn update(
+ &self,
+ _initial: &dyn PackageInterface,
+ _target: &dyn PackageInterface,
+ _path: &str,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+
+ fn remove(
+ &self,
+ _package: &dyn PackageInterface,
+ _path: &str,
+ _output: bool,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+
+ fn cleanup(
+ &self,
+ _type: &str,
+ _package: &dyn PackageInterface,
+ _path: &str,
+ _prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<
+ Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>,
+ > {
+ todo!()
+ }
+}
diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs
index ca50827..2a485de 100644
--- a/crates/shirabe/src/downloader/gzip_downloader.rs
+++ b/crates/shirabe/src/downloader/gzip_downloader.rs
@@ -1,25 +1,57 @@
//! ref: composer/src/Composer/Downloader/GzipDownloader.php
+use crate::cache::Cache;
+use crate::config::Config;
use crate::downloader::archive_downloader::ArchiveDownloader;
use crate::downloader::file_downloader::FileDownloader;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::util::filesystem::Filesystem;
+use crate::util::http_downloader::HttpDownloader;
use crate::util::platform::Platform;
+use crate::util::process_executor::ProcessExecutor;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
- DIRECTORY_SEPARATOR, PATHINFO_FILENAME, PHP_URL_PATH, RuntimeException, extension_loaded,
- fclose, fopen, fwrite, gzclose, gzopen, gzread, implode, parse_url, pathinfo, strtr,
+ DIRECTORY_SEPARATOR, PATHINFO_FILENAME, PHP_URL_PATH, PhpMixed, RuntimeException,
+ extension_loaded, fclose, fopen, fwrite, gzclose, gzopen, gzread, implode, parse_url, pathinfo,
+ strtr,
};
+#[derive(Debug)]
pub struct GzipDownloader {
inner: FileDownloader,
cleanup_executed: IndexMap<String, bool>,
}
impl GzipDownloader {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ cache: Option<Cache>,
+ filesystem: Filesystem,
+ process: ProcessExecutor,
+ ) -> Self {
+ Self {
+ inner: FileDownloader::new(
+ io,
+ config,
+ http_downloader,
+ event_dispatcher,
+ cache,
+ Some(filesystem),
+ Some(process),
+ ),
+ cleanup_executed: IndexMap::new(),
+ }
+ }
+
pub(crate) fn extract(
- &self,
+ &mut self,
package: &dyn PackageInterface,
file: &str,
path: &str,
@@ -31,7 +63,12 @@ impl GzipDownloader {
),
PATHINFO_FILENAME,
);
- let target_filepath = format!("{}{}{}", path, DIRECTORY_SEPARATOR, filename);
+ let target_filepath = format!(
+ "{}{}{}",
+ path,
+ DIRECTORY_SEPARATOR,
+ filename.as_string().unwrap_or_default()
+ );
if !Platform::is_windows() {
let command = vec![
@@ -42,7 +79,18 @@ impl GzipDownloader {
target_filepath.clone(),
];
- if self.inner.process.execute(&command, &mut String::new()) == 0 {
+ let mut process_output = PhpMixed::Null;
+ if self.inner.process.execute(
+ PhpMixed::List(
+ command
+ .iter()
+ .map(|s| Box::new(PhpMixed::String(s.clone())))
+ .collect(),
+ ),
+ Some(&mut process_output),
+ None,
+ )? == 0
+ {
return Ok(shirabe_external_packages::react::promise::resolve(None));
}
diff --git a/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs b/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs
index 1613432..4e18761 100644
--- a/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs
+++ b/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs
@@ -2,4 +2,19 @@
use crate::downloader::transport_exception::TransportException;
+#[derive(Debug)]
pub struct MaxFileSizeExceededException(pub TransportException);
+
+impl MaxFileSizeExceededException {
+ pub fn new(message: String) -> Self {
+ Self(TransportException::new(message, 0))
+ }
+}
+
+impl std::fmt::Display for MaxFileSizeExceededException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl std::error::Error for MaxFileSizeExceededException {}
diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs
index c50d390..a525df0 100644
--- a/crates/shirabe/src/downloader/path_downloader.rs
+++ b/crates/shirabe/src/downloader/path_downloader.rs
@@ -140,7 +140,7 @@ impl PathDownloader {
let (mut current_strategy, allowed_strategies) =
self.compute_allowed_strategies(&transport_options)?;
- let symfony_filesystem = SymfonyFilesystem::new();
+ let symfony_filesystem = SymfonyFilesystem::new(None);
self.inner.filesystem.remove_directory(&path);
if output {
@@ -254,14 +254,12 @@ impl PathDownloader {
io_interface::NORMAL,
);
}
- let iterator = ArchivableFilesFinder::new(&real_url, vec![]);
+ let iterator = ArchivableFilesFinder::new(&real_url, vec![], false)?;
symfony_filesystem.mirror(&real_url, &path, Some(&iterator));
}
if output {
- self.inner
- .io
- .write_error(PhpMixed::String("".to_string()), true, io_interface::NORMAL);
+ self.inner.io.write_error3("", true, io_interface::NORMAL);
}
Ok(shirabe_external_packages::react::promise::resolve(None))
@@ -328,7 +326,7 @@ impl PathDownloader {
// can happen when using custom installers, see https://github.com/composer/composer/pull/9116
// not using realpath here as we do not want to resolve the symlink to the original dist url
// it points to
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
let abs_path = if fs.is_absolute_path(&path) {
path.clone()
} else {
diff --git a/crates/shirabe/src/downloader/phar_downloader.rs b/crates/shirabe/src/downloader/phar_downloader.rs
index 5316fc1..649841d 100644
--- a/crates/shirabe/src/downloader/phar_downloader.rs
+++ b/crates/shirabe/src/downloader/phar_downloader.rs
@@ -1,8 +1,16 @@
//! ref: composer/src/Composer/Downloader/PharDownloader.php
+use crate::cache::Cache;
+use crate::config::Config;
use crate::downloader::archive_downloader::ArchiveDownloader;
+use crate::downloader::downloader_interface::DownloaderInterface;
use crate::downloader::file_downloader::FileDownloader;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::util::filesystem::Filesystem;
+use crate::util::http_downloader::HttpDownloader;
+use crate::util::process_executor::ProcessExecutor;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
@@ -15,6 +23,29 @@ pub struct PharDownloader {
}
impl PharDownloader {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ cache: Option<Cache>,
+ filesystem: Filesystem,
+ process: ProcessExecutor,
+ ) -> Self {
+ Self {
+ inner: FileDownloader::new(
+ io,
+ config,
+ http_downloader,
+ event_dispatcher,
+ cache,
+ Some(filesystem),
+ Some(process),
+ ),
+ cleanup_executed: IndexMap::new(),
+ }
+ }
+
pub(crate) fn extract(
&self,
package: &dyn PackageInterface,
@@ -32,3 +63,66 @@ impl PharDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
}
+
+impl DownloaderInterface for PharDownloader {
+ fn get_installation_source(&self) -> String {
+ self.inner.get_installation_source()
+ }
+
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.download(package, path, prev_package, output)
+ }
+
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.prepare(r#type, package, path, prev_package)
+ }
+
+ fn install(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.install(package, path, output)
+ }
+
+ fn update(
+ &self,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.update(initial, target, path)
+ }
+
+ fn remove(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.remove(package, path, output)
+ }
+
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.cleanup(r#type, package, path, prev_package)
+ }
+}
diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs
index 308b6fa..51feadb 100644
--- a/crates/shirabe/src/downloader/rar_downloader.rs
+++ b/crates/shirabe/src/downloader/rar_downloader.rs
@@ -1,25 +1,56 @@
//! ref: composer/src/Composer/Downloader/RarDownloader.php
+use crate::cache::Cache;
+use crate::config::Config;
use crate::downloader::archive_downloader::ArchiveDownloader;
use crate::downloader::file_downloader::FileDownloader;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::util::filesystem::Filesystem;
+use crate::util::http_downloader::HttpDownloader;
use crate::util::ini_helper::IniHelper;
use crate::util::platform::Platform;
+use crate::util::process_executor::ProcessExecutor;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
- RarArchive, RuntimeException, UnexpectedValueException, class_exists, implode,
+ PhpMixed, RarArchive, RuntimeException, UnexpectedValueException, class_exists, implode,
};
+#[derive(Debug)]
pub struct RarDownloader {
inner: FileDownloader,
cleanup_executed: IndexMap<String, bool>,
}
impl RarDownloader {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ cache: Option<Cache>,
+ filesystem: Filesystem,
+ process: ProcessExecutor,
+ ) -> Self {
+ Self {
+ inner: FileDownloader::new(
+ io,
+ config,
+ http_downloader,
+ event_dispatcher,
+ cache,
+ Some(filesystem),
+ Some(process),
+ ),
+ cleanup_executed: IndexMap::new(),
+ }
+ }
+
pub(crate) fn extract(
- &self,
+ &mut self,
_package: &dyn PackageInterface,
file: &str,
path: &str,
@@ -35,7 +66,18 @@ impl RarDownloader {
path.to_string(),
];
- if self.inner.process.execute(&command, &mut String::new()) == 0 {
+ let mut process_output = PhpMixed::Null;
+ if self.inner.process.execute(
+ PhpMixed::List(
+ command
+ .iter()
+ .map(|s| Box::new(PhpMixed::String(s.clone())))
+ .collect(),
+ ),
+ Some(&mut process_output),
+ None,
+ )? == 0
+ {
return Ok(shirabe_external_packages::react::promise::resolve(None));
}
@@ -101,3 +143,66 @@ impl RarDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
}
+
+impl crate::downloader::downloader_interface::DownloaderInterface for RarDownloader {
+ fn get_installation_source(&self) -> String {
+ self.inner.get_installation_source()
+ }
+
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.download(package, path, prev_package, output)
+ }
+
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.prepare(r#type, package, path, prev_package)
+ }
+
+ fn install(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.install(package, path, output)
+ }
+
+ fn update(
+ &self,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.update(initial, target, path)
+ }
+
+ fn remove(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.remove(package, path, output)
+ }
+
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.cleanup(r#type, package, path, prev_package)
+ }
+}
diff --git a/crates/shirabe/src/downloader/tar_downloader.rs b/crates/shirabe/src/downloader/tar_downloader.rs
index d8531e0..aaa7153 100644
--- a/crates/shirabe/src/downloader/tar_downloader.rs
+++ b/crates/shirabe/src/downloader/tar_downloader.rs
@@ -1,8 +1,16 @@
//! ref: composer/src/Composer/Downloader/TarDownloader.php
+use crate::cache::Cache;
+use crate::config::Config;
use crate::downloader::archive_downloader::ArchiveDownloader;
+use crate::downloader::downloader_interface::DownloaderInterface;
use crate::downloader::file_downloader::FileDownloader;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::util::filesystem::Filesystem;
+use crate::util::http_downloader::HttpDownloader;
+use crate::util::process_executor::ProcessExecutor;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
@@ -15,6 +23,29 @@ pub struct TarDownloader {
}
impl TarDownloader {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ cache: Option<Cache>,
+ filesystem: Filesystem,
+ process: ProcessExecutor,
+ ) -> Self {
+ Self {
+ inner: FileDownloader::new(
+ io,
+ config,
+ http_downloader,
+ event_dispatcher,
+ cache,
+ Some(filesystem),
+ Some(process),
+ ),
+ cleanup_executed: IndexMap::new(),
+ }
+ }
+
pub(crate) fn extract(
&self,
package: &dyn PackageInterface,
@@ -27,3 +58,66 @@ impl TarDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
}
+
+impl DownloaderInterface for TarDownloader {
+ fn get_installation_source(&self) -> String {
+ self.inner.get_installation_source()
+ }
+
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.download(package, path, prev_package, output)
+ }
+
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.prepare(r#type, package, path, prev_package)
+ }
+
+ fn install(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.install(package, path, output)
+ }
+
+ fn update(
+ &self,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.update(initial, target, path)
+ }
+
+ fn remove(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.remove(package, path, output)
+ }
+
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.cleanup(r#type, package, path, prev_package)
+ }
+}
diff --git a/crates/shirabe/src/downloader/transport_exception.rs b/crates/shirabe/src/downloader/transport_exception.rs
index e4f04f3..382da01 100644
--- a/crates/shirabe/src/downloader/transport_exception.rs
+++ b/crates/shirabe/src/downloader/transport_exception.rs
@@ -24,6 +24,14 @@ impl TransportException {
}
}
+ pub fn get_code(&self) -> i64 {
+ self.code
+ }
+
+ pub fn get_message(&self) -> &str {
+ &self.message
+ }
+
pub fn set_headers(&mut self, headers: Vec<String>) {
self.headers = Some(headers);
}
diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs
index 5e19016..346603a 100644
--- a/crates/shirabe/src/downloader/vcs_downloader.rs
+++ b/crates/shirabe/src/downloader/vcs_downloader.rs
@@ -385,7 +385,7 @@ pub trait VcsDownloader:
io_interface::NORMAL,
);
self.io_mut()
- .write_error(PhpMixed::String(logs), true, io_interface::NORMAL);
+ .write_error3(&logs, true, io_interface::NORMAL);
}
}
diff --git a/crates/shirabe/src/downloader/xz_downloader.rs b/crates/shirabe/src/downloader/xz_downloader.rs
index 1ad0bb1..61a7f14 100644
--- a/crates/shirabe/src/downloader/xz_downloader.rs
+++ b/crates/shirabe/src/downloader/xz_downloader.rs
@@ -1,11 +1,19 @@
//! ref: composer/src/Composer/Downloader/XzDownloader.php
+use crate::cache::Cache;
+use crate::config::Config;
use crate::downloader::archive_downloader::ArchiveDownloader;
use crate::downloader::file_downloader::FileDownloader;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::util::filesystem::Filesystem;
+use crate::util::http_downloader::HttpDownloader;
+use crate::util::process_executor::ProcessExecutor;
use anyhow::{Result, bail};
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
+use shirabe_php_shim::PhpMixed;
#[derive(Debug)]
pub struct XzDownloader {
@@ -14,16 +22,49 @@ pub struct XzDownloader {
}
impl XzDownloader {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ cache: Option<Cache>,
+ filesystem: Filesystem,
+ process: ProcessExecutor,
+ ) -> Self {
+ Self {
+ inner: FileDownloader::new(
+ io,
+ config,
+ http_downloader,
+ event_dispatcher,
+ cache,
+ Some(filesystem),
+ Some(process),
+ ),
+ cleanup_executed: IndexMap::new(),
+ }
+ }
+
pub(crate) fn extract(
- &self,
+ &mut self,
package: &dyn PackageInterface,
file: &str,
path: &str,
) -> Result<Box<dyn PromiseInterface>> {
let command = vec!["tar", "-xJf", file, "-C", path];
- let mut ignored_output = String::new();
- if self.inner.process.execute(&command, &mut ignored_output) == 0 {
+ let mut ignored_output = PhpMixed::Null;
+ if self.inner.process.execute(
+ PhpMixed::List(
+ command
+ .iter()
+ .map(|s| Box::new(PhpMixed::String(s.to_string())))
+ .collect(),
+ ),
+ Some(&mut ignored_output),
+ None,
+ )? == 0
+ {
return Ok(shirabe_external_packages::react::promise::resolve(None));
}
@@ -36,3 +77,66 @@ impl XzDownloader {
bail!(process_error);
}
}
+
+impl crate::downloader::downloader_interface::DownloaderInterface for XzDownloader {
+ fn get_installation_source(&self) -> String {
+ self.inner.get_installation_source()
+ }
+
+ fn download(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.download(package, path, prev_package, output)
+ }
+
+ fn prepare(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.prepare(r#type, package, path, prev_package)
+ }
+
+ fn install(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.install(package, path, output)
+ }
+
+ fn update(
+ &self,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.update(initial, target, path)
+ }
+
+ fn remove(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ output: bool,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.remove(package, path, output)
+ }
+
+ fn cleanup(
+ &self,
+ r#type: &str,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>> {
+ self.inner.cleanup(r#type, package, path, prev_package)
+ }
+}
diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs
index 90e5639..7f779c8 100644
--- a/crates/shirabe/src/downloader/zip_downloader.rs
+++ b/crates/shirabe/src/downloader/zip_downloader.rs
@@ -152,13 +152,13 @@ impl ZipDownloader {
if !is_windows_guard.unwrap() && unzip_commands_empty {
if proc_open_missing {
- self.inner.inner.io.write_error("<warning>proc_open is disabled so 'unzip' and '7z' commands cannot be used, zip files are being unpacked using the PHP zip extension.</warning>");
- self.inner.inner.io.write_error("<warning>This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost.</warning>");
- self.inner.inner.io.write_error("<warning>Enabling proc_open and installing 'unzip' or '7z' (21.01+) may remediate them.</warning>");
+ self.inner.io.write_error("<warning>proc_open is disabled so 'unzip' and '7z' commands cannot be used, zip files are being unpacked using the PHP zip extension.</warning>");
+ self.inner.io.write_error("<warning>This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost.</warning>");
+ self.inner.io.write_error("<warning>Enabling proc_open and installing 'unzip' or '7z' (21.01+) may remediate them.</warning>");
} else {
- self.inner.inner.io.write_error("<warning>As there is no 'unzip' nor '7z' command installed zip files are being unpacked using the PHP zip extension.</warning>");
- self.inner.inner.io.write_error("<warning>This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost.</warning>");
- self.inner.inner.io.write_error("<warning>Installing 'unzip' or '7z' (21.01+) may remediate them.</warning>");
+ self.inner.io.write_error("<warning>As there is no 'unzip' nor '7z' command installed zip files are being unpacked using the PHP zip extension.</warning>");
+ self.inner.io.write_error("<warning>This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost.</warning>");
+ self.inner.io.write_error("<warning>Installing 'unzip' or '7z' (21.01+) may remediate them.</warning>");
}
}
}
@@ -226,7 +226,7 @@ impl ZipDownloader {
Preg::is_match_strict_groups(r"^\s*7-Zip(?:\s\[64\])?\s([0-9.]+)", &output)
{
if version_compare(&m[1], "21.01", "<") {
- self.inner.inner.io.write_error(&format!(
+ self.inner.io.write_error(&format!(
" <warning>Unzipping using {} {} may result in incorrect file permissions. Install {} 21.01+ or unzip to ensure you get correct permissions.</warning>",
executable, m[1], executable,
));
@@ -235,7 +235,7 @@ impl ZipDownloader {
}
}
- let io = &self.inner.inner.io;
+ let io = &self.inner.io;
let try_fallback = |process_error: anyhow::Error| -> Result<Box<dyn PromiseInterface>> {
if is_last_chance {
return Err(process_error);
@@ -297,7 +297,7 @@ impl ZipDownloader {
self.extract_with_zip_archive(package, file, path)
};
- match self.inner.inner.process.execute_async(&command) {
+ match self.inner.process.execute_async(&command) {
Ok(promise) => Ok(promise.then(
Box::new(move |process: Process| -> Result<()> {
if !process.is_successful() {
diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
index 705287c..a1bc0b4 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -4,7 +4,6 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::component::console::application::Application;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::string_input::StringInput;
use shirabe_external_packages::symfony::component::console::output::console_output::ConsoleOutput;
use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder;
@@ -213,7 +212,7 @@ impl EventDispatcher {
.unwrap_or_default()
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -328,7 +327,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::VERBOSE,
+ crate::io::io_interface::VERBOSE,
);
}
// TODO(plugin): actually invoke callable with $event and inspect result
@@ -345,7 +344,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::VERBOSE,
+ crate::io::io_interface::VERBOSE,
);
let mut script: Vec<String> = substr(callable_str, 1, None)
@@ -404,7 +403,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
return Err(anyhow::anyhow!(ScriptExecutionException(
@@ -432,7 +431,7 @@ impl EventDispatcher {
&[PhpMixed::String(callable_str.clone())],
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
}
@@ -468,7 +467,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
}
return Err(e);
@@ -490,7 +489,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
continue;
}
@@ -502,7 +501,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
continue;
}
@@ -526,7 +525,7 @@ impl EventDispatcher {
)
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
return Err(e);
}
@@ -550,7 +549,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
continue;
}
@@ -566,7 +565,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
continue;
}
@@ -580,7 +579,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
continue;
}
@@ -595,15 +594,9 @@ impl EventDispatcher {
}
app.set_auto_exit(false);
// TODO(plugin): instantiate command class dynamically: `new $className($event->getName())`
- let cmd = CommandBase::new(None); // TODO(plugin): pass event name
- if method_exists(&PhpMixed::String("Application".to_string()), "addCommand")
- {
- app.add_command(cmd.clone());
- } else {
- // Compatibility layer for symfony/console <7.4
- app.add(cmd.clone());
- }
- app.set_default_command(cmd.get_name().to_string(), true);
+ todo!(
+ "plugin: CommandBase::new — dynamic plugin command instantiation not supported"
+ );
let result = (|| -> anyhow::Result<i64> {
let args = additional_args
.iter()
@@ -647,7 +640,7 @@ impl EventDispatcher {
)
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
return Err(e);
}
@@ -687,7 +680,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
} else if self.event_needs_to_output(event) {
self.io.write_error(
@@ -696,7 +689,7 @@ impl EventDispatcher {
&[PhpMixed::String(exec.clone())],
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -828,7 +821,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::QUIET,
+ crate::io::io_interface::QUIET,
);
return Err(anyhow::anyhow!(ScriptExecutionException(
@@ -949,7 +942,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
} else if self.event_needs_to_output(event) {
self.io.write_error(
@@ -961,7 +954,7 @@ impl EventDispatcher {
],
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -1094,7 +1087,7 @@ impl EventDispatcher {
event.get_name()
)),
true,
- <dyn IOInterface>::VERBOSE,
+ crate::io::io_interface::VERBOSE,
);
return Vec::new();
diff --git a/crates/shirabe/src/event_dispatcher/script_execution_exception.rs b/crates/shirabe/src/event_dispatcher/script_execution_exception.rs
index 23b763f..05567b0 100644
--- a/crates/shirabe/src/event_dispatcher/script_execution_exception.rs
+++ b/crates/shirabe/src/event_dispatcher/script_execution_exception.rs
@@ -6,6 +6,16 @@ use shirabe_php_shim::RuntimeException;
#[derive(Debug)]
pub struct ScriptExecutionException(pub RuntimeException);
+impl ScriptExecutionException {
+ pub fn get_code(&self) -> i64 {
+ self.0.code
+ }
+
+ pub fn get_message(&self) -> &str {
+ &self.0.message
+ }
+}
+
impl std::fmt::Display for ScriptExecutionException {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
diff --git a/crates/shirabe/src/exception/irrecoverable_download_exception.rs b/crates/shirabe/src/exception/irrecoverable_download_exception.rs
index 35fd915..a8d2dbb 100644
--- a/crates/shirabe/src/exception/irrecoverable_download_exception.rs
+++ b/crates/shirabe/src/exception/irrecoverable_download_exception.rs
@@ -2,4 +2,13 @@
use shirabe_php_shim::RuntimeException;
+#[derive(Debug)]
pub struct IrrecoverableDownloadException(pub RuntimeException);
+
+impl std::fmt::Display for IrrecoverableDownloadException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl std::error::Error for IrrecoverableDownloadException {}
diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs
index 42d31d1..3d6ab6d 100644
--- a/crates/shirabe/src/factory.rs
+++ b/crates/shirabe/src/factory.rs
@@ -259,13 +259,13 @@ impl Factory {
config.merge(defaults, Config::SOURCE_DEFAULT);
// load global config
- let file = JsonFile::new(format!("{}/config.json", config.get_str("home")?), None, io);
+ let file = JsonFile::new(format!("{}/config.json", config.get_str("home")?), None, io)?;
if file.exists() {
if let Some(io_ref) = io {
io_ref.write_error(
PhpMixed::String(format!("Loading config file {}", file.get_path())),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
Self::validate_json_schema(
@@ -308,13 +308,13 @@ impl Factory {
}
// load global auth file
- let auth_file = JsonFile::new(format!("{}/auth.json", config.get_str("home")?), None, io);
+ let auth_file = JsonFile::new(format!("{}/auth.json", config.get_str("home")?), None, io)?;
if auth_file.exists() {
if let Some(io_ref) = io {
io_ref.write_error(
PhpMixed::String(format!("Loading config file {}", auth_file.get_path())),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
Self::validate_json_schema(
@@ -440,7 +440,7 @@ impl Factory {
if let Some(LocalConfigInput::Path(path)) = &local_config {
composer_file = Some(path.clone());
- let file = JsonFile::new(path.clone(), None, Some(io));
+ let file = JsonFile::new(path.clone(), None, Some(io))?;
if !file.exists() {
let message = if path == "./composer.json" || path == "composer.json" {
@@ -496,14 +496,14 @@ impl Factory {
realpath(composer_file_path).unwrap_or_default()
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
config.set_config_source(JsonConfigSource::new(
JsonFile::new(
realpath(composer_file_path).unwrap_or_default(),
None,
Some(io),
- ),
+ )?,
false,
));
@@ -514,7 +514,7 @@ impl Factory {
),
None,
Some(io),
- );
+ )?;
if local_auth_file.exists() {
io.write_error(
PhpMixed::String(format!(
@@ -522,7 +522,7 @@ impl Factory {
local_auth_file.get_path()
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
Self::validate_json_schema(
Some(io),
@@ -585,7 +585,10 @@ impl Factory {
let http_downloader = Self::create_http_downloader(io, &config, IndexMap::new())?;
let process = ProcessExecutor::new(io);
- let r#loop = Loop::new(http_downloader.clone(), process.clone());
+ let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
+ http_downloader.clone(),
+ Some(process.clone()),
+ )));
composer.set_loop(r#loop.clone());
// initialize event dispatcher
@@ -671,7 +674,7 @@ impl Factory {
lock_file
)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -685,7 +688,7 @@ impl Factory {
},
None,
Some(io),
- ),
+ )?,
im.clone(),
file_get_contents(composer_file_path).unwrap_or_default(),
process.clone(),
@@ -694,14 +697,17 @@ impl Factory {
} else {
let locker = Locker::new(
io.clone_box(),
- JsonFile::new(Platform::get_dev_null(), None, Some(io)),
+ JsonFile::new(Platform::get_dev_null(), None, Some(io))?,
im.clone(),
- JsonFile::encode(&PhpMixed::Array(
- local_config_data
- .iter()
- .map(|(k, v)| (k.clone(), Box::new(v.clone())))
- .collect(),
- )),
+ JsonFile::encode(
+ &PhpMixed::Array(
+ local_config_data
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(v.clone())))
+ .collect(),
+ ),
+ 448,
+ ),
process.clone(),
);
composer_full.set_locker(locker);
@@ -774,16 +780,20 @@ impl Factory {
) {
let fs = process.map(|p| Filesystem::new(Some(p.clone())));
- rm.set_local_repository(Box::new(InstalledFilesystemRepository::new(
- JsonFile::new(
- format!("{}/composer/installed.json", vendor_dir),
- None,
- Some(io),
- ),
- true,
- root_package.clone_box(),
- fs,
- )));
+ rm.set_local_repository(Box::new(
+ InstalledFilesystemRepository::new(
+ JsonFile::new(
+ format!("{}/composer/installed.json", vendor_dir),
+ None,
+ Some(io.clone_box()),
+ )
+ .expect("installed.json path is always valid"),
+ true,
+ Some(RootPackageInterface::clone_box(root_package)),
+ fs,
+ )
+ .expect("InstalledFilesystemRepository::new should not fail"),
+ ));
}
fn create_global_composer(
@@ -820,7 +830,7 @@ impl Factory {
io.write_error(
PhpMixed::String(format!("Failed to initialize global composer: {}", e)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
None
}
@@ -836,7 +846,7 @@ impl Factory {
http_downloader: &HttpDownloader,
process: &ProcessExecutor,
event_dispatcher: Option<&EventDispatcher>,
- ) -> anyhow::Result<DownloadManager> {
+ ) -> anyhow::Result<std::rc::Rc<std::cell::RefCell<DownloadManager>>> {
let mut cache: Option<Cache> = None;
if config
.get("cache-files-ttl")
@@ -1029,14 +1039,14 @@ impl Factory {
)),
);
- Ok(dm)
+ Ok(std::rc::Rc::new(std::cell::RefCell::new(dm)))
}
pub fn create_archive_manager(
&self,
_config: &Config,
- dm: &DownloadManager,
- r#loop: &Loop,
+ dm: &std::rc::Rc<std::cell::RefCell<DownloadManager>>,
+ r#loop: &std::rc::Rc<std::cell::RefCell<Loop>>,
) -> anyhow::Result<ArchiveManager> {
let mut am = ArchiveManager::new(dm.clone(), r#loop.clone());
if class_exists("ZipArchive") {
@@ -1066,7 +1076,7 @@ impl Factory {
pub fn create_installation_manager(
&self,
- r#loop: Loop,
+ r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
io: Box<dyn IOInterface>,
event_dispatcher: Option<EventDispatcher>,
) -> InstallationManager {
@@ -1203,7 +1213,7 @@ impl Factory {
.to_string(),
),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
unsafe { WARNED = true };
@@ -1263,7 +1273,7 @@ impl Factory {
.to_string(),
),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
io.write(
PhpMixed::String(
@@ -1271,7 +1281,7 @@ impl Factory {
.to_string(),
),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
io.write(
PhpMixed::String(
@@ -1279,7 +1289,7 @@ impl Factory {
.to_string(),
),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
}
@@ -1314,7 +1324,7 @@ impl Factory {
io_ref.write_error(
PhpMixed::String("Loading auth config from COMPOSER_AUTH".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
Self::validate_json_schema(
@@ -1393,7 +1403,7 @@ impl Factory {
io_ref.write_error(
PhpMixed::String(format!("<warning>{}</>", msg)),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
} else {
return Err(anyhow::anyhow!(UnexpectedValueException {
@@ -1433,7 +1443,7 @@ impl PartialComposerOrComposer {
Self::Partial(p) => p.set_global(),
}
}
- fn set_loop(&mut self, r#loop: Loop) {
+ fn set_loop(&mut self, r#loop: std::rc::Rc<std::cell::RefCell<Loop>>) {
match self {
Self::Full(c) => c.set_loop(r#loop),
Self::Partial(p) => p.set_loop(r#loop),
diff --git a/crates/shirabe/src/filter/platform_requirement_filter/ignore_all_platform_requirement_filter.rs b/crates/shirabe/src/filter/platform_requirement_filter/ignore_all_platform_requirement_filter.rs
index e38bfea..218a52d 100644
--- a/crates/shirabe/src/filter/platform_requirement_filter/ignore_all_platform_requirement_filter.rs
+++ b/crates/shirabe/src/filter/platform_requirement_filter/ignore_all_platform_requirement_filter.rs
@@ -14,4 +14,8 @@ impl PlatformRequirementFilterInterface for IgnoreAllPlatformRequirementFilter {
fn is_upper_bound_ignored(&self, req: &str) -> bool {
self.is_ignored(req)
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs
index 8026768..39a0c03 100644
--- a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs
+++ b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs
@@ -29,8 +29,9 @@ impl IgnoreListPlatformRequirementFilter {
ignore_all.push(req);
}
}
- let ignore_regex = base_package::package_names_to_regexp(&ignore_all);
- let ignore_upper_bound_regex = base_package::package_names_to_regexp(&ignore_upper_bound);
+ let ignore_regex = base_package::package_names_to_regexp(&ignore_all, "{^(?:%s)$}iD");
+ let ignore_upper_bound_regex =
+ base_package::package_names_to_regexp(&ignore_upper_bound, "{^(?:%s)$}iD");
Ok(Self {
ignore_regex,
ignore_upper_bound_regex,
@@ -88,4 +89,8 @@ impl PlatformRequirementFilterInterface for IgnoreListPlatformRequirementFilter
}
self.is_ignored(req) || Preg::is_match(&self.ignore_upper_bound_regex, req).unwrap_or(false)
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/crates/shirabe/src/filter/platform_requirement_filter/ignore_nothing_platform_requirement_filter.rs b/crates/shirabe/src/filter/platform_requirement_filter/ignore_nothing_platform_requirement_filter.rs
index c2d0fec..21da158 100644
--- a/crates/shirabe/src/filter/platform_requirement_filter/ignore_nothing_platform_requirement_filter.rs
+++ b/crates/shirabe/src/filter/platform_requirement_filter/ignore_nothing_platform_requirement_filter.rs
@@ -13,4 +13,8 @@ impl PlatformRequirementFilterInterface for IgnoreNothingPlatformRequirementFilt
fn is_upper_bound_ignored(&self, _req: &str) -> bool {
false
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs
index 59340cb..35a3ed9 100644
--- a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs
+++ b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs
@@ -23,9 +23,20 @@ impl PlatformRequirementFilterFactory {
Ok(Self::ignore_nothing())
}
}
- list_or_array @ (PhpMixed::List(_) | PhpMixed::Array(_)) => Ok(Box::new(
- IgnoreListPlatformRequirementFilter::new(list_or_array),
- )),
+ list_or_array @ (PhpMixed::List(_) | PhpMixed::Array(_)) => {
+ let list: Vec<String> = match list_or_array {
+ PhpMixed::List(items) => items
+ .into_iter()
+ .filter_map(|v| v.as_string().map(|s| s.to_string()))
+ .collect(),
+ PhpMixed::Array(map) => map
+ .into_iter()
+ .filter_map(|(_, v)| v.as_string().map(|s| s.to_string()))
+ .collect(),
+ _ => unreachable!(),
+ };
+ Ok(Box::new(IgnoreListPlatformRequirementFilter::new(list)?))
+ }
other => Err(anyhow::anyhow!(InvalidArgumentException {
message: format!(
"PlatformRequirementFilter: Unknown $boolOrList parameter {}. Please report at https://github.com/composer/composer/issues/new.",
diff --git a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_interface.rs b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_interface.rs
index a807da3..8061e1a 100644
--- a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_interface.rs
+++ b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_interface.rs
@@ -1,7 +1,13 @@
//! ref: composer/src/Composer/Filter/PlatformRequirementFilter/PlatformRequirementFilterInterface.php
-pub trait PlatformRequirementFilterInterface {
+pub trait PlatformRequirementFilterInterface: std::fmt::Debug {
fn is_ignored(&self, req: &str) -> bool;
fn is_upper_bound_ignored(&self, req: &str) -> bool;
+
+ fn as_any(&self) -> &dyn std::any::Any;
+
+ fn clone_box(&self) -> Box<dyn PlatformRequirementFilterInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index b32e47b..cb3da38 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -71,6 +71,7 @@ use crate::package::root_alias_package::RootAliasPackage;
use crate::package::root_package_interface::RootPackageInterface;
use crate::package::version::version_parser::VersionParser;
use crate::repository::array_repository::ArrayRepository;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::installed_array_repository::InstalledArrayRepository;
use crate::repository::installed_repository::InstalledRepository;
@@ -93,7 +94,7 @@ pub struct Installer {
pub(crate) package: Box<dyn RootPackageInterface>,
// TODO can we get rid of the below and just use the package itself?
pub(crate) fixed_root_package: Box<dyn RootPackageInterface>,
- pub(crate) download_manager: DownloadManager,
+ pub(crate) download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
pub(crate) repository_manager: RepositoryManager,
pub(crate) locker: Locker,
pub(crate) installation_manager: InstallationManager,
@@ -148,7 +149,7 @@ impl Installer {
io: Box<dyn IOInterface>,
config: Config,
package: Box<dyn RootPackageInterface>,
- download_manager: DownloadManager,
+ download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
repository_manager: RepositoryManager,
locker: Locker,
installation_manager: InstallationManager,
@@ -260,8 +261,12 @@ impl Installer {
.dispatch_script(event_name, self.dev_mode);
}
- self.download_manager.set_prefer_source(self.prefer_source);
- self.download_manager.set_prefer_dist(self.prefer_dist);
+ self.download_manager
+ .borrow_mut()
+ .set_prefer_source(self.prefer_source);
+ self.download_manager
+ .borrow_mut()
+ .set_prefer_dist(self.prefer_dist);
let local_repo = self.repository_manager.get_local_repository();
@@ -848,14 +853,14 @@ impl Installer {
return Ok(0);
}
- let mut result_repo = ArrayRepository::new(vec![]);
+ let mut result_repo = ArrayRepository::new(vec![])?;
let loader = ArrayLoader::new(None, true);
let dumper = ArrayDumper::new();
for pkg in lock_transaction.get_new_lock_packages(false, false) {
result_repo.add_package(loader.load(
dumper.dump(&*pkg),
"Composer\\Package\\CompletePackage".to_string(),
- )?);
+ )?)?;
}
let mut repository_set = self.create_repository_set(true, platform_repo, aliases, None);
@@ -1132,7 +1137,7 @@ impl Installer {
}
if self.execute_operations {
- local_repo.set_dev_package_names(self.locker.get_dev_package_names());
+ local_repo.set_dev_package_names(self.locker.get_dev_package_names()?);
self.installation_manager.execute(
&*local_repo,
local_repo_transaction.get_operations(),
@@ -1463,12 +1468,12 @@ impl Installer {
}
let keys: Vec<String> = packages.keys().cloned().collect();
for key in keys {
- let package_clone = packages.get(&key).unwrap().clone_box();
+ let package_clone = packages.get(&key).unwrap().clone_package_box();
if let Some(alias_pkg) = package_clone.as_alias_package() {
let alias_key = alias_pkg.get_alias_of().to_string();
let _class_name = get_class(&*package_clone);
// PHP: $packages[$key] = new $className($packages[$alias], $package->getVersion(), $package->getPrettyVersion());
- let aliased = packages.get(&alias_key).unwrap().clone_box();
+ let aliased = packages.get(&alias_key).unwrap().clone_package_box();
let new_alias_package: Box<dyn PackageInterface> = Box::new(AliasPackage::new(
aliased,
alias_pkg.get_version().to_string(),
diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs
index 243dde0..e6677ed 100644
--- a/crates/shirabe/src/installer/binary_installer.rs
+++ b/crates/shirabe/src/installer/binary_installer.rs
@@ -33,7 +33,7 @@ impl BinaryInstaller {
filesystem: Option<Filesystem>,
vendor_dir: Option<String>,
) -> Self {
- let filesystem = filesystem.unwrap_or_else(Filesystem::new);
+ let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
Self {
bin_dir,
bin_compat,
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 798ea00..59e029a 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -40,7 +40,7 @@ pub struct InstallationManager {
cache: IndexMap<String, Box<dyn InstallerInterface>>,
/// @var array<string, array<PackageInterface>>
notifiable_packages: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
- loop_: Loop,
+ loop_: std::rc::Rc<std::cell::RefCell<Loop>>,
io: Box<dyn IOInterface>,
event_dispatcher: Option<EventDispatcher>,
output_progress: bool,
@@ -48,7 +48,7 @@ pub struct InstallationManager {
impl InstallationManager {
pub fn new(
- loop_: Loop,
+ loop_: std::rc::Rc<std::cell::RefCell<Loop>>,
io: Box<dyn IOInterface>,
event_dispatcher: Option<EventDispatcher>,
) -> Self {
@@ -527,13 +527,12 @@ impl InstallationManager {
// TODO(phase-b): progress = self.io.get_progress_bar();
progress = Some(());
}
- self.loop_.wait(promises, progress);
+ let _ = self.loop_.borrow_mut().wait(promises, progress);
if progress.is_some() {
// progress.clear();
// ProgressBar in non-decorated output does not output a final line-break and clear() does nothing
if !self.io.is_decorated() {
- self.io
- .write_error(PhpMixed::String(String::new()), true, io_interface::NORMAL);
+ self.io.write_error3("", true, io_interface::NORMAL);
}
}
}
@@ -628,7 +627,7 @@ impl InstallationManager {
let package = operation.get_package();
if !repo.has_package(package) {
- repo.add_package(package.clone_box());
+ repo.add_package(package.clone_package_box());
}
}
@@ -697,7 +696,7 @@ impl InstallationManager {
),
);
- promises.push(self.loop_.get_http_downloader().add(
+ promises.push(self.loop_.borrow().get_http_downloader().add(
&url,
&PhpMixed::Array(
opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
@@ -768,13 +767,13 @@ impl InstallationManager {
PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
- promises.push(self.loop_.get_http_downloader().add(
+ promises.push(self.loop_.borrow().get_http_downloader().add(
repo_url,
&PhpMixed::Array(opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
));
}
- self.loop_.wait(promises, None);
+ let _ = self.loop_.borrow_mut().wait(promises, None);
Ok(())
})();
@@ -789,7 +788,7 @@ impl InstallationManager {
self.notifiable_packages
.entry(notification_url.to_string())
.or_insert_with(Vec::new)
- .push(package.clone_box());
+ .push(package.clone_package_box());
}
}
@@ -800,7 +799,7 @@ impl InstallationManager {
) {
let mut promises: Vec<Box<dyn PromiseInterface>> = vec![];
- self.loop_.abort_jobs();
+ self.loop_.borrow().abort_jobs();
for (_, cleanup) in cleanup_promises {
// TODO(phase-b): React\Promise\Promise constructor with executor; emulate by wrapping cleanup()
@@ -813,7 +812,7 @@ impl InstallationManager {
}
if (promises.len() as i64) > 0 {
- self.loop_.wait(promises, None);
+ let _ = self.loop_.borrow_mut().wait(promises, None);
}
}
}
diff --git a/crates/shirabe/src/installer/installer_event.rs b/crates/shirabe/src/installer/installer_event.rs
index 456a4bd..20a8b27 100644
--- a/crates/shirabe/src/installer/installer_event.rs
+++ b/crates/shirabe/src/installer/installer_event.rs
@@ -24,7 +24,7 @@ impl InstallerEvent {
execute_operations: bool,
transaction: Transaction,
) -> Self {
- let inner = Event::new(event_name, vec![], vec![]);
+ let inner = Event::new(event_name, vec![], indexmap::IndexMap::new());
Self {
inner,
composer,
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index 16cf10c..cf700be 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -27,20 +27,20 @@ pub trait InstallerInterface {
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
fn install(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
fn update(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
fn uninstall(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
@@ -53,4 +53,8 @@ pub trait InstallerInterface {
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>>;
fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String>;
+
+ fn clone_box(&self) -> Box<dyn InstallerInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index af11ef6..17404e2 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -27,7 +27,7 @@ use crate::util::silencer::Silencer;
pub struct LibraryInstaller {
pub(crate) composer: PartialComposer,
pub(crate) vendor_dir: String,
- pub(crate) download_manager: Option<DownloadManager>,
+ pub(crate) download_manager: Option<std::rc::Rc<std::cell::RefCell<DownloadManager>>>,
pub(crate) io: Box<dyn IOInterface>,
pub(crate) r#type: Option<String>,
pub(crate) filesystem: Filesystem,
@@ -53,7 +53,7 @@ impl LibraryInstaller {
None
};
- let filesystem = filesystem.unwrap_or_else(Filesystem::new);
+ let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
let vendor_dir = rtrim(
// TODO(phase-b): composer.get_config().get("vendor-dir") returns a PhpMixed/String
&composer.get_config().get("vendor-dir"),
@@ -123,7 +123,9 @@ impl LibraryInstaller {
) -> Result<Option<Box<dyn PromiseInterface>>> {
let download_path = self.get_install_path(package).unwrap();
- self.get_download_manager().install(package, &download_path)
+ self.get_download_manager()
+ .borrow()
+ .install(package, &download_path)
}
/// @return PromiseInterface|null
@@ -165,6 +167,7 @@ impl LibraryInstaller {
}
self.get_download_manager()
+ .borrow()
.update(initial, target, &target_download_path)
}
@@ -176,7 +179,9 @@ impl LibraryInstaller {
) -> Result<Option<Box<dyn PromiseInterface>>> {
let download_path = self.get_package_base_path(package);
- self.get_download_manager().remove(package, &download_path)
+ self.get_download_manager()
+ .borrow()
+ .remove(package, &download_path)
}
pub(crate) fn initialize_vendor_dir(&mut self) {
@@ -185,7 +190,7 @@ impl LibraryInstaller {
self.vendor_dir = realpath(&self.vendor_dir).unwrap();
}
- pub(crate) fn get_download_manager(&self) -> &DownloadManager {
+ pub(crate) fn get_download_manager(&self) -> &std::rc::Rc<std::cell::RefCell<DownloadManager>> {
// PHP: assert($this->downloadManager instanceof DownloadManager, new \LogicException(...))
assert!(
self.download_manager.is_some(),
@@ -252,6 +257,7 @@ impl InstallerInterface for LibraryInstaller {
let download_path = self.get_install_path(package).unwrap();
self.get_download_manager()
+ .borrow()
.download(package, &download_path, prev_package)
}
@@ -266,6 +272,7 @@ impl InstallerInterface for LibraryInstaller {
let download_path = self.get_install_path(package).unwrap();
self.get_download_manager()
+ .borrow()
.prepare(r#type, package, &download_path, prev_package)
}
@@ -280,11 +287,12 @@ impl InstallerInterface for LibraryInstaller {
let download_path = self.get_install_path(package).unwrap();
self.get_download_manager()
+ .borrow()
.cleanup(r#type, package, &download_path, prev_package)
}
fn install(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
@@ -310,14 +318,14 @@ impl InstallerInterface for LibraryInstaller {
Ok(Some(promise.then(Box::new(move || -> Result<()> {
binary_installer.install_binaries(package, &install_path, true);
if !repo.has_package(package) {
- repo.add_package(package.clone_box())?;
+ repo.add_package(package.clone_package_box())?;
}
Ok(())
}))))
}
fn update(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
@@ -348,14 +356,14 @@ impl InstallerInterface for LibraryInstaller {
binary_installer.install_binaries(target, &install_path, true);
repo.remove_package(initial)?;
if !repo.has_package(target) {
- repo.add_package(target.clone_box())?;
+ repo.add_package(target.clone_package_box())?;
}
Ok(())
}))))
}
fn uninstall(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs
index 3a47f70..e30ef85 100644
--- a/crates/shirabe/src/installer/metapackage_installer.rs
+++ b/crates/shirabe/src/installer/metapackage_installer.rs
@@ -69,17 +69,17 @@ impl InstallerInterface for MetapackageInstaller {
}
fn install(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
- self.io.write_error(
+ self.io.write_error3(
&format!(" - {}", InstallOperation::format(package, false)),
true,
io_interface::NORMAL,
);
- repo.add_package(package.clone_box());
+ repo.add_package(package.clone_package_box());
Ok(Some(shirabe_external_packages::react::promise::resolve(
None,
@@ -87,7 +87,7 @@ impl InstallerInterface for MetapackageInstaller {
}
fn update(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
@@ -100,14 +100,14 @@ impl InstallerInterface for MetapackageInstaller {
.into());
}
- self.io.write_error(
+ self.io.write_error3(
&format!(" - {}", UpdateOperation::format(initial, target, false)),
true,
io_interface::NORMAL,
);
repo.remove_package(initial);
- repo.add_package(target.clone_box());
+ repo.add_package(target.clone_package_box());
Ok(Some(shirabe_external_packages::react::promise::resolve(
None,
@@ -115,7 +115,7 @@ impl InstallerInterface for MetapackageInstaller {
}
fn uninstall(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
@@ -127,7 +127,7 @@ impl InstallerInterface for MetapackageInstaller {
.into());
}
- self.io.write_error(
+ self.io.write_error3(
&format!(" - {}", UninstallOperation::format(package, false)),
true,
io_interface::NORMAL,
diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs
index 09e6afd..8180402 100644
--- a/crates/shirabe/src/installer/noop_installer.rs
+++ b/crates/shirabe/src/installer/noop_installer.rs
@@ -55,12 +55,12 @@ impl InstallerInterface for NoopInstaller {
}
fn install(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
if !repo.has_package(package) {
- repo.add_package(package.clone_box());
+ repo.add_package(package.clone_package_box());
}
Ok(Some(shirabe_external_packages::react::promise::resolve(
@@ -69,7 +69,7 @@ impl InstallerInterface for NoopInstaller {
}
fn update(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
@@ -84,7 +84,7 @@ impl InstallerInterface for NoopInstaller {
repo.remove_package(initial);
if !repo.has_package(target) {
- repo.add_package(target.clone_box());
+ repo.add_package(target.clone_package_box());
}
Ok(Some(shirabe_external_packages::react::promise::resolve(
@@ -93,7 +93,7 @@ impl InstallerInterface for NoopInstaller {
}
fn uninstall(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
diff --git a/crates/shirabe/src/installer/package_event.rs b/crates/shirabe/src/installer/package_event.rs
index 2bf1c6b..8b6fbbe 100644
--- a/crates/shirabe/src/installer/package_event.rs
+++ b/crates/shirabe/src/installer/package_event.rs
@@ -39,6 +39,10 @@ impl PackageEvent {
}
}
+ pub fn get_name(&self) -> &str {
+ self.inner.get_name()
+ }
+
pub fn get_composer(&self) -> &Composer {
&self.composer
}
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index 1fd2334..77bd669 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -37,13 +37,13 @@ impl PluginInstaller {
}
}
- pub fn disable_plugins(&self) {
+ pub fn disable_plugins(&mut self) {
// TODO(plugin): disable plugins via plugin manager
- self.get_plugin_manager().disable_plugins();
+ self.get_plugin_manager_mut().disable_plugins();
}
fn rollback_install(
- &self,
+ &mut self,
e: anyhow::Error,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
@@ -71,6 +71,11 @@ impl PluginInstaller {
// TODO(plugin): return plugin manager from composer
self.inner.composer.get_plugin_manager()
}
+
+ fn get_plugin_manager_mut(&mut self) -> &mut PluginManager {
+ // TODO(plugin): return mutable plugin manager from composer
+ todo!()
+ }
}
impl InstallerInterface for PluginInstaller {
@@ -129,7 +134,7 @@ impl InstallerInterface for PluginInstaller {
}
fn install(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
@@ -149,7 +154,7 @@ impl InstallerInterface for PluginInstaller {
}
fn update(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
@@ -171,12 +176,12 @@ impl InstallerInterface for PluginInstaller {
}
fn uninstall(
- &self,
+ &mut self,
repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> Result<Option<Box<dyn PromiseInterface>>> {
// TODO(plugin): uninstall package from plugin manager
- self.get_plugin_manager().uninstall_package(package);
+ self.get_plugin_manager_mut().uninstall_package(package);
self.inner.uninstall(repo, package)
}
diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs
index 1a097b0..f8b0bba 100644
--- a/crates/shirabe/src/installer/project_installer.rs
+++ b/crates/shirabe/src/installer/project_installer.rs
@@ -11,13 +11,17 @@ use shirabe_php_shim::InvalidArgumentException;
#[derive(Debug)]
pub struct ProjectInstaller {
install_path: String,
- download_manager: DownloadManager,
+ download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
filesystem: Filesystem,
}
impl ProjectInstaller {
- pub fn new(install_path: &str, dm: DownloadManager, fs: Filesystem) -> Self {
- let install_path = format!("{}/", install_path.replace('\\', '/').trim_end_matches('/'));
+ pub fn new(
+ install_path: &str,
+ dm: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
+ fs: Filesystem,
+ ) -> Self {
+ let install_path = format!("{}/", install_path.replace('\\', "/").trim_end_matches('/'));
Self {
install_path,
download_manager: dm,
@@ -59,7 +63,9 @@ impl InstallerInterface for ProjectInstaller {
}
self.download_manager
+ .borrow()
.download(package, install_path, prev_package)
+ .map(Some)
}
fn prepare(
@@ -69,7 +75,9 @@ impl InstallerInterface for ProjectInstaller {
prev_package: Option<&dyn PackageInterface>,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
self.download_manager
+ .borrow()
.prepare(r#type, package, &self.install_path, prev_package)
+ .map(Some)
}
fn cleanup(
@@ -79,19 +87,23 @@ impl InstallerInterface for ProjectInstaller {
prev_package: Option<&dyn PackageInterface>,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
self.download_manager
+ .borrow()
.cleanup(r#type, package, &self.install_path, prev_package)
+ .map(Some)
}
fn install(
- &self,
+ &mut self,
_repo: &mut dyn InstalledRepositoryInterface,
package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
- self.download_manager.install(package, &self.install_path)
+ self.download_manager
+ .borrow()
+ .install(package, &self.install_path)
}
fn update(
- &self,
+ &mut self,
_repo: &mut dyn InstalledRepositoryInterface,
_initial: &dyn PackageInterface,
_target: &dyn PackageInterface,
@@ -104,7 +116,7 @@ impl InstallerInterface for ProjectInstaller {
}
fn uninstall(
- &self,
+ &mut self,
_repo: &mut dyn InstalledRepositoryInterface,
_package: &dyn PackageInterface,
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs
index cfee20b..31a34d8 100644
--- a/crates/shirabe/src/installer/suggested_packages_reporter.rs
+++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs
@@ -3,6 +3,7 @@
use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository::InstalledRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::component::console::formatter::output_formatter::OutputFormatter;
diff --git a/crates/shirabe/src/io/base_io.rs b/crates/shirabe/src/io/base_io.rs
index f467de1..d4e8f73 100644
--- a/crates/shirabe/src/io/base_io.rs
+++ b/crates/shirabe/src/io/base_io.rs
@@ -98,7 +98,7 @@ pub trait BaseIO: IOInterface {
let custom_headers = config.get("custom-headers");
let client_certificate = config.get("client-certificate");
- if let Some(map) = bitbucket_oauth.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = bitbucket_oauth.as_opt().and_then(|v| v.as_array()) {
for (domain, cred) in map.clone() {
if let Some(cred_map) = cred.as_array() {
let consumer_key = cred_map
@@ -115,7 +115,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = github_oauth.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = github_oauth.as_opt().and_then(|v| v.as_array()) {
for (domain, token) in map.clone() {
let token_str = token.as_string().unwrap_or("").to_string();
let github_domains = config.get("github-domains");
@@ -139,9 +139,9 @@ pub trait BaseIO: IOInterface {
);
let mut inner = IndexMap::new();
inner.insert("github-domains".to_string(), Box::new(merged));
- let mut outer = IndexMap::new();
- outer.insert("config".to_string(), Box::new(PhpMixed::Array(inner)));
- config.merge(PhpMixed::Array(outer), "implicit-due-to-auth");
+ let mut config_outer: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_outer.insert("config".to_string(), PhpMixed::Array(inner));
+ config.merge(&config_outer, "implicit-due-to-auth");
}
if !Preg::is_match(r"^[.A-Za-z0-9_]+$", &token_str).unwrap_or(false) {
@@ -161,7 +161,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = gitlab_oauth.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = gitlab_oauth.as_opt().and_then(|v| v.as_array()) {
for (domain, token) in map.clone() {
let gitlab_domains = config.get("gitlab-domains");
if domain != "gitlab.com"
@@ -184,9 +184,9 @@ pub trait BaseIO: IOInterface {
);
let mut inner = IndexMap::new();
inner.insert("gitlab-domains".to_string(), Box::new(merged));
- let mut outer = IndexMap::new();
- outer.insert("config".to_string(), Box::new(PhpMixed::Array(inner)));
- config.merge(PhpMixed::Array(outer), "implicit-due-to-auth");
+ let mut config_outer: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_outer.insert("config".to_string(), PhpMixed::Array(inner));
+ config.merge(&config_outer, "implicit-due-to-auth");
}
let token_str = if let Some(arr) = token.as_array() {
@@ -201,7 +201,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = gitlab_token.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = gitlab_token.as_opt().and_then(|v| v.as_array()) {
for (domain, token) in map.clone() {
let gitlab_domains = config.get("gitlab-domains");
if domain != "gitlab.com"
@@ -224,9 +224,9 @@ pub trait BaseIO: IOInterface {
);
let mut inner = IndexMap::new();
inner.insert("gitlab-domains".to_string(), Box::new(merged));
- let mut outer = IndexMap::new();
- outer.insert("config".to_string(), Box::new(PhpMixed::Array(inner)));
- config.merge(PhpMixed::Array(outer), "implicit-due-to-auth");
+ let mut config_outer: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_outer.insert("config".to_string(), PhpMixed::Array(inner));
+ config.merge(&config_outer, "implicit-due-to-auth");
}
let (username, password) = if let Some(arr) = token.as_array() {
@@ -250,7 +250,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = forgejo_token.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = forgejo_token.as_opt().and_then(|v| v.as_array()) {
for (domain, cred) in map.clone() {
let forgejo_domains = config.get("forgejo-domains");
if !in_array(
@@ -271,9 +271,9 @@ pub trait BaseIO: IOInterface {
);
let mut inner = IndexMap::new();
inner.insert("forgejo-domains".to_string(), Box::new(merged));
- let mut outer = IndexMap::new();
- outer.insert("config".to_string(), Box::new(PhpMixed::Array(inner)));
- config.merge(PhpMixed::Array(outer), "implicit-due-to-auth");
+ let mut config_outer: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_outer.insert("config".to_string(), PhpMixed::Array(inner));
+ config.merge(&config_outer, "implicit-due-to-auth");
}
if let Some(cred_map) = cred.as_array() {
@@ -291,7 +291,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = http_basic.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = http_basic.as_opt().and_then(|v| v.as_array()) {
for (domain, cred) in map.clone() {
if let Some(cred_map) = cred.as_array() {
let username = cred_map
@@ -308,14 +308,14 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = bearer_token.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = bearer_token.as_opt().and_then(|v| v.as_array()) {
for (domain, token) in map.clone() {
let token_str = token.as_string().unwrap_or("").to_string();
self.check_and_set_authentication(domain, token_str, Some("bearer".to_string()));
}
}
- if let Some(map) = custom_headers.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = custom_headers.as_opt().and_then(|v| v.as_array()) {
for (domain, headers) in map.clone() {
if !headers.is_null() {
let json_str = json_encode_ex(&headers, 0).unwrap_or_default();
@@ -328,7 +328,7 @@ pub trait BaseIO: IOInterface {
}
}
- if let Some(map) = client_certificate.as_ref().and_then(|v| v.as_array()) {
+ if let Some(map) = client_certificate.as_opt().and_then(|v| v.as_array()) {
for (domain, cred) in map.clone() {
if let Some(cred_map) = cred.as_array() {
let local_cert = cred_map
diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs
index 91a15d3..6f17a3e 100644
--- a/crates/shirabe/src/io/console_io.rs
+++ b/crates/shirabe/src/io/console_io.rs
@@ -420,38 +420,38 @@ impl IOInterface for ConsoleIO {
self.output.is_decorated()
}
- fn write(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- let messages = Self::sanitize(messages, true);
+ fn write3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ let message = Self::sanitize(message, true);
- self.do_write(messages, newline, false, verbosity, false);
+ self.do_write(message, newline, false, verbosity, false);
}
- fn write_error(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- let messages = Self::sanitize(messages, true);
+ fn write_error3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ let message = Self::sanitize(message, true);
- self.do_write(messages, newline, true, verbosity, false);
+ self.do_write(message, newline, true, verbosity, false);
}
- fn write_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- self.do_write(messages, newline, false, verbosity, true);
+ fn write_raw3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ self.do_write(message, newline, false, verbosity, true);
}
- fn write_error_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- self.do_write(messages, newline, true, verbosity, true);
+ fn write_error_raw3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ self.do_write(message, newline, true, verbosity, true);
}
- fn overwrite(&mut self, messages: PhpMixed, newline: bool, size: Option<i64>, verbosity: i64) {
- self.do_overwrite(messages, newline, size, false, verbosity);
+ fn overwrite4(&mut self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) {
+ self.do_overwrite(message, newline, size, false, verbosity);
}
- fn overwrite_error(
+ fn overwrite_error4(
&mut self,
- messages: PhpMixed,
+ message: &str,
newline: bool,
size: Option<i64>,
verbosity: i64,
) {
- self.do_overwrite(messages, newline, size, true, verbosity);
+ self.do_overwrite(message, newline, size, true, verbosity);
}
fn ask(&mut self, question: String, default: PhpMixed) -> PhpMixed {
@@ -471,14 +471,17 @@ impl IOInterface for ConsoleIO {
fn ask_confirmation(&mut self, question: String, default: bool) -> bool {
let helper = self.helper_set.get("question");
- let default_mixed = PhpMixed::Bool(default);
+ // TODO(phase-b): Self::sanitize returns PhpMixed but new() expects String;
+ // also true/false regexes need to come through composer/symfony defaults.
+ let sanitized = Self::sanitize(PhpMixed::String(question), true)
+ .as_string()
+ .unwrap_or("")
+ .to_string();
let question = StrictConfirmationQuestion::new(
- Self::sanitize(PhpMixed::String(question), true),
- if is_string(&default_mixed) {
- Self::sanitize(default_mixed, true)
- } else {
- default_mixed
- },
+ sanitized,
+ default,
+ "/^y(?:es)?$/i".to_string(),
+ "/^no?$/i".to_string(),
);
helper
diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs
index 3826bb3..e78b608 100644
--- a/crates/shirabe/src/io/io_interface.rs
+++ b/crates/shirabe/src/io/io_interface.rs
@@ -11,7 +11,7 @@ pub const VERBOSE: i64 = 4;
pub const VERY_VERBOSE: i64 = 8;
pub const DEBUG: i64 = 16;
-pub trait IOInterface: LoggerInterface {
+pub trait IOInterface: LoggerInterface + std::fmt::Debug {
fn is_interactive(&self) -> bool;
fn is_verbose(&self) -> bool;
@@ -22,23 +22,65 @@ pub trait IOInterface: LoggerInterface {
fn is_decorated(&self) -> bool;
- fn write(&mut self, messages: PhpMixed, newline: bool, verbosity: i64);
+ fn write(&mut self, message: &str) {
+ self.write3(message, true, NORMAL)
+ }
+ fn write2(&mut self, message: &str, newline: bool) {
+ self.write3(message, newline, NORMAL)
+ }
+ fn write_no_newline(&mut self, message: &str) {
+ self.write3(message, false, NORMAL)
+ }
+ fn write3(&mut self, message: &str, newline: bool, verbosity: i64);
- fn write_error(&mut self, messages: PhpMixed, newline: bool, verbosity: i64);
+ fn write_error(&mut self, message: &str) {
+ self.write_error3(message, true, NORMAL)
+ }
+ fn write_error2(&mut self, message: &str, newline: bool) {
+ self.write_error3(message, newline, NORMAL)
+ }
+ fn write_error_no_newline(&mut self, message: &str) {
+ self.write_error3(message, false, NORMAL)
+ }
+ fn write_error3(&mut self, message: &str, newline: bool, verbosity: i64);
- fn write_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64);
+ fn write_raw(&mut self, message: &str) {
+ self.write_raw3(message, true, NORMAL)
+ }
+ fn write_raw2(&mut self, message: &str, newline: bool) {
+ self.write_raw3(message, newline, NORMAL)
+ }
+ fn write_raw3(&mut self, message: &str, newline: bool, verbosity: i64);
- fn write_error_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64);
+ fn write_error_raw(&mut self, message: &str) {
+ self.write_error_raw3(message, true, NORMAL)
+ }
+ fn write_error_raw2(&mut self, message: &str, newline: bool) {
+ self.write_error_raw3(message, newline, NORMAL)
+ }
+ fn write_error_raw3(&mut self, message: &str, newline: bool, verbosity: i64);
- fn overwrite(&mut self, messages: PhpMixed, newline: bool, size: Option<i64>, verbosity: i64);
+ fn overwrite(&mut self, message: &str) {
+ self.overwrite4(message, true, None, NORMAL)
+ }
+ fn overwrite2(&mut self, message: &str, newline: bool) {
+ self.overwrite4(message, newline, None, NORMAL)
+ }
+ fn overwrite3(&mut self, message: &str, newline: bool, size: Option<i64>) {
+ self.overwrite4(message, newline, size, NORMAL)
+ }
+ fn overwrite4(&mut self, message: &str, newline: bool, size: Option<i64>, verbosity: i64);
- fn overwrite_error(
- &mut self,
- messages: PhpMixed,
- newline: bool,
- size: Option<i64>,
- verbosity: i64,
- );
+ fn overwrite_error(&mut self, message: &str) {
+ self.overwrite_error4(message, true, None, NORMAL)
+ }
+ fn overwrite_error2(&mut self, message: &str, newline: bool) {
+ self.overwrite_error4(message, newline, None, NORMAL)
+ }
+ fn overwrite_error3(&mut self, message: &str, newline: bool, size: Option<i64>) {
+ self.overwrite_error4(message, newline, size, NORMAL)
+ }
+ fn overwrite_error4(&mut self, message: &str, newline: bool, size: Option<i64>, verbosity: i64);
fn ask(&mut self, question: String, default: PhpMixed) -> PhpMixed;
@@ -78,4 +120,8 @@ pub trait IOInterface: LoggerInterface {
);
fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()>;
+
+ fn clone_box(&self) -> Box<dyn IOInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs
index f229839..4ec5be0 100644
--- a/crates/shirabe/src/io/null_io.rs
+++ b/crates/shirabe/src/io/null_io.rs
@@ -10,6 +10,14 @@ pub struct NullIO {
authentications: indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>>,
}
+impl NullIO {
+ pub fn new() -> Self {
+ Self {
+ authentications: indexmap::IndexMap::new(),
+ }
+ }
+}
+
impl IOInterface for NullIO {
fn is_interactive(&self) -> bool {
false
@@ -31,22 +39,19 @@ impl IOInterface for NullIO {
false
}
- fn write(&mut self, _messages: PhpMixed, _newline: bool, _verbosity: i64) {}
+ fn write3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn write_error(&mut self, _messages: PhpMixed, _newline: bool, _verbosity: i64) {}
+ fn write_error3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn overwrite(
- &mut self,
- _messages: PhpMixed,
- _newline: bool,
- _size: Option<i64>,
- _verbosity: i64,
- ) {
- }
+ fn write_raw3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
+
+ fn write_error_raw3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn overwrite_error(
+ fn overwrite4(&mut self, _message: &str, _newline: bool, _size: Option<i64>, _verbosity: i64) {}
+
+ fn overwrite_error4(
&mut self,
- _messages: PhpMixed,
+ _message: &str,
_newline: bool,
_size: Option<i64>,
_verbosity: i64,
@@ -87,14 +92,6 @@ impl IOInterface for NullIO {
default
}
- fn write_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- <Self as BaseIO>::write_raw(self, messages, newline, verbosity)
- }
-
- fn write_error_raw(&mut self, messages: PhpMixed, newline: bool, verbosity: i64) {
- <Self as BaseIO>::write_error_raw(self, messages, newline, verbosity)
- }
-
fn get_authentications(
&self,
) -> indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>> {
diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs
index c1fa495..d08e207 100644
--- a/crates/shirabe/src/json/json_file.rs
+++ b/crates/shirabe/src/json/json_file.rs
@@ -401,7 +401,11 @@ impl JsonFile {
/// @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
/// @param string $indent Indentation string
/// @return string Encoded json
- pub fn encode(data: &PhpMixed, options: i64, indent: &str) -> String {
+ pub fn encode(data: &PhpMixed, options: i64) -> String {
+ Self::encode_with_indent(data, options, Self::INDENT_DEFAULT)
+ }
+
+ pub fn encode_with_indent(data: &PhpMixed, options: i64, indent: &str) -> String {
let json = json_encode_ex(data, options);
let json = match json {
diff --git a/crates/shirabe/src/json/json_formatter.rs b/crates/shirabe/src/json/json_formatter.rs
index 1696982..47680b1 100644
--- a/crates/shirabe/src/json/json_formatter.rs
+++ b/crates/shirabe/src/json/json_formatter.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Json/JsonFormatter.php
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{PhpMixed, function_exists, mb_convert_encoding, pack};
pub struct JsonFormatter;
@@ -44,27 +44,40 @@ impl JsonFormatter {
if unescape_unicode && function_exists("mb_convert_encoding") {
buffer = Preg::replace_callback(
r"/(\\+)u([0-9a-f]{4})/i",
- |matches: &[String]| -> String {
- let l = matches[1].len();
+ |matches: &indexmap::IndexMap<CaptureKey, String>| -> String {
+ let m0 = matches
+ .get(&CaptureKey::ByIndex(0))
+ .cloned()
+ .unwrap_or_default();
+ let m1 = matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ let m2 = matches
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default();
+ let l = m1.len();
if l % 2 != 0 {
- let code = i64::from_str_radix(&matches[2], 16).unwrap_or(0);
+ let code = i64::from_str_radix(&m2, 16).unwrap_or(0);
if code >= 0xD800 && code <= 0xDFFF {
- return matches[0].clone();
+ return m0;
}
return "\\".repeat(l - 1)
+ &mb_convert_encoding(
- pack("H*", &[PhpMixed::String(matches[2].clone())]),
+ pack("H*", &[PhpMixed::String(m2)]),
"UTF-8",
"UCS-2BE",
);
}
- matches[0].clone()
+ m0
},
&buffer,
- );
+ )
+ .unwrap_or(buffer);
}
result.push_str(&buffer);
diff --git a/crates/shirabe/src/json/json_manipulator.rs b/crates/shirabe/src/json/json_manipulator.rs
index ebbfff9..b9c6a5c 100644
--- a/crates/shirabe/src/json/json_manipulator.rs
+++ b/crates/shirabe/src/json/json_manipulator.rs
@@ -128,8 +128,7 @@ impl JsonManipulator {
JsonFile::encode(
&PhpMixed::String(str_replace("\\/", "/", &existing_owned)),
0
- )
- .unwrap_or_default(),
+ ),
m.get("separator").cloned().unwrap_or_default(),
constraint_owned
)
@@ -1024,7 +1023,7 @@ impl JsonManipulator {
if now_empty {
arr.insert(
name_owned.clone(),
- Box::new(PhpMixed::Object(ArrayObject::new())),
+ Box::new(PhpMixed::Object(ArrayObject::new(None))),
);
}
}
@@ -1068,7 +1067,7 @@ impl JsonManipulator {
if now_empty {
arr.insert(
name_capture.clone(),
- Box::new(PhpMixed::Object(ArrayObject::new())),
+ Box::new(PhpMixed::Object(ArrayObject::new(None))),
);
}
}
diff --git a/crates/shirabe/src/json/json_validation_exception.rs b/crates/shirabe/src/json/json_validation_exception.rs
index 5f3cbbf..4549a0f 100644
--- a/crates/shirabe/src/json/json_validation_exception.rs
+++ b/crates/shirabe/src/json/json_validation_exception.rs
@@ -19,6 +19,10 @@ impl JsonValidationException {
pub fn get_errors(&self) -> &Vec<String> {
&self.errors
}
+
+ pub fn get_message(&self) -> &str {
+ &self.inner.message
+ }
}
impl std::fmt::Display for JsonValidationException {
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs
index cd2e5f1..e115362 100644
--- a/crates/shirabe/src/package/alias_package.rs
+++ b/crates/shirabe/src/package/alias_package.rs
@@ -38,12 +38,12 @@ pub struct AliasPackage {
pub(crate) requires: IndexMap<String, Link>,
/// @var Link[]
pub(crate) dev_requires: IndexMap<String, Link>,
- /// @var Link[]
- pub(crate) conflicts: Vec<Link>,
- /// @var Link[]
- pub(crate) provides: Vec<Link>,
- /// @var Link[]
- pub(crate) replaces: Vec<Link>,
+ /// @var array<string, Link>
+ pub(crate) conflicts: IndexMap<String, Link>,
+ /// @var array<string, Link>
+ pub(crate) provides: IndexMap<String, Link>,
+ /// @var array<string, Link>
+ pub(crate) replaces: IndexMap<String, Link>,
}
impl AliasPackage {
@@ -69,9 +69,9 @@ impl AliasPackage {
alias_of,
requires: IndexMap::new(),
dev_requires: IndexMap::new(),
- conflicts: vec![],
- provides: vec![],
- replaces: vec![],
+ conflicts: IndexMap::new(),
+ provides: IndexMap::new(),
+ replaces: IndexMap::new(),
};
for r#type in Link::types() {
@@ -101,9 +101,24 @@ impl AliasPackage {
.map(|l| (l.get_target().to_string(), l))
.collect();
}
- Link::TYPE_PROVIDE => this.provides = replaced,
- Link::TYPE_CONFLICT => this.conflicts = replaced,
- Link::TYPE_REPLACE => this.replaces = replaced,
+ Link::TYPE_PROVIDE => {
+ this.provides = replaced
+ .into_iter()
+ .map(|l| (l.get_target().to_string(), l))
+ .collect()
+ }
+ Link::TYPE_CONFLICT => {
+ this.conflicts = replaced
+ .into_iter()
+ .map(|l| (l.get_target().to_string(), l))
+ .collect()
+ }
+ Link::TYPE_REPLACE => {
+ this.replaces = replaced
+ .into_iter()
+ .map(|l| (l.get_target().to_string(), l))
+ .collect()
+ }
_ => {}
}
}
@@ -202,7 +217,7 @@ impl std::fmt::Display for AliasPackage {
write!(
f,
"{} ({}alias of {})",
- self.inner,
+ self.alias_of,
if self.root_package_alias { "root " } else { "" },
self.alias_of.get_version(),
)
@@ -210,24 +225,28 @@ impl std::fmt::Display for AliasPackage {
}
impl PackageInterface for AliasPackage {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_name(&self) -> &str {
- self.inner.get_name()
+ self.alias_of.get_name()
}
fn get_pretty_name(&self) -> &str {
- self.inner.get_pretty_name()
+ self.alias_of.get_pretty_name()
}
fn get_names(&self, provides: bool) -> Vec<String> {
- self.inner.get_names(provides)
+ self.alias_of.get_names(provides)
}
fn set_id(&mut self, id: i64) {
- self.inner.set_id(id);
+ self.alias_of.set_id(id);
}
fn get_id(&self) -> i64 {
- self.inner.get_id()
+ self.alias_of.get_id()
}
fn is_dev(&self) -> bool {
@@ -251,20 +270,20 @@ impl PackageInterface for AliasPackage {
}
/// @inheritDoc
- /// @return array<string|int, Link>
- fn get_conflicts(&self) -> Vec<Link> {
+ /// @return array<string, Link>
+ fn get_conflicts(&self) -> IndexMap<String, Link> {
self.conflicts.clone()
}
/// @inheritDoc
- /// @return array<string|int, Link>
- fn get_provides(&self) -> Vec<Link> {
+ /// @return array<string, Link>
+ fn get_provides(&self) -> IndexMap<String, Link> {
self.provides.clone()
}
/// @inheritDoc
- /// @return array<string|int, Link>
- fn get_replaces(&self) -> Vec<Link> {
+ /// @return array<string, Link>
+ fn get_replaces(&self) -> IndexMap<String, Link> {
self.replaces.clone()
}
@@ -410,25 +429,25 @@ 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
- self.inner
+ self.alias_of
.get_full_pretty_version(truncate, display_mode)
.unwrap_or_default()
}
fn get_unique_name(&self) -> String {
- self.inner.get_unique_name()
+ self.alias_of.get_unique_name()
}
fn get_pretty_string(&self) -> String {
- self.inner.get_pretty_string()
+ self.alias_of.get_pretty_string()
}
fn set_repository(&mut self, repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> {
- self.inner.set_repository(repository)
+ self.alias_of.set_repository(repository)
}
fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
- self.inner.get_repository()
+ self.alias_of.get_repository()
}
}
@@ -469,10 +488,6 @@ impl BasePackage for AliasPackage {
todo!()
}
- fn as_any(&self) -> &dyn std::any::Any {
- todo!()
- }
-
fn clone_box(&self) -> Box<dyn BasePackage> {
todo!()
}
diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs
index 35ec36f..72ffabf 100644
--- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs
+++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs
@@ -23,7 +23,7 @@ impl std::fmt::Debug for ArchivableFilesFinder {
impl ArchivableFilesFinder {
pub fn new(sources: &str, excludes: Vec<String>, ignore_filters: bool) -> anyhow::Result<Self> {
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
let sources_real_path = realpath(sources);
if sources_real_path.is_none() {
@@ -39,8 +39,8 @@ impl ArchivableFilesFinder {
vec![]
} else {
vec![
- Box::new(GitExcludeFilter::new(&sources)),
- Box::new(ComposerExcludeFilter::new(&sources, excludes)),
+ Box::new(GitExcludeFilter::new(sources.clone())),
+ Box::new(ComposerExcludeFilter::new(sources.clone(), excludes)),
]
};
@@ -61,7 +61,8 @@ impl ArchivableFilesFinder {
&format!("^{}", preg_quote(&sources_clone, Some('#'))),
"",
&fs.normalize_path(&realpath),
- );
+ )
+ .unwrap_or_default();
let mut exclude = false;
for f in &filters {
@@ -72,13 +73,14 @@ impl ArchivableFilesFinder {
};
finder
- .in_dir(&sources)
- .filter(Box::new(filter))
+ .r#in(&sources)
+ // TODO(phase-b): symfony Finder filter takes Box<dyn Fn(&SplFileInfo) -> bool>; signature not yet wired
.ignore_vcs(true)
.ignore_dot_files(false)
.sort_by_name();
+ let _ = filter;
- let inner_iter = finder.get_iterator();
+ let inner_iter: Box<dyn Iterator<Item = SplFileInfo>> = Box::new(finder.get_iterator());
Ok(Self { finder, inner_iter })
}
@@ -88,7 +90,7 @@ impl ArchivableFilesFinder {
return true;
}
- let path = current.to_string();
+ let path = current.get_pathname();
match std::fs::read_dir(&path) {
Ok(mut iter) => iter.next().is_none(),
Err(_) => false,
diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs
index 3f7bfe0..73b0d84 100644
--- a/crates/shirabe/src/package/archiver/archive_manager.rs
+++ b/crates/shirabe/src/package/archiver/archive_manager.rs
@@ -19,8 +19,8 @@ use crate::util::r#loop::Loop;
use crate::util::sync_helper::SyncHelper;
pub struct ArchiveManager {
- pub(crate) download_manager: DownloadManager,
- pub(crate) r#loop: Loop,
+ pub(crate) download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
+ pub(crate) r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
pub(crate) archivers: Vec<Box<dyn ArchiverInterface>>,
pub(crate) overwrite_files: bool,
}
@@ -34,7 +34,10 @@ impl std::fmt::Debug for ArchiveManager {
}
impl ArchiveManager {
- pub fn new(download_manager: DownloadManager, r#loop: Loop) -> Self {
+ pub fn new(
+ download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
+ r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
+ ) -> Self {
Self {
download_manager,
r#loop,
@@ -144,7 +147,7 @@ impl ArchiveManager {
}
};
- let filesystem = Filesystem::new();
+ let filesystem = Filesystem::new(None);
let is_root = package.as_any().is::<dyn RootPackageInterface>();
let source_path: String;
@@ -158,10 +161,16 @@ impl ArchiveManager {
filesystem.ensure_directory_exists(&source_path)?;
let download_result = (|| -> anyhow::Result<()> {
- let promise = self.download_manager.download(package, &source_path)?;
- SyncHelper::r#await(&self.r#loop, promise)?;
- let promise = self.download_manager.install(package, &source_path)?;
- SyncHelper::r#await(&self.r#loop, promise)?;
+ let promise =
+ self.download_manager
+ .borrow()
+ .download(package, &source_path, None)?;
+ SyncHelper::r#await(&self.r#loop, Some(promise))?;
+ let promise = self
+ .download_manager
+ .borrow()
+ .install(package, &source_path)?;
+ SyncHelper::r#await(&self.r#loop, Some(promise))?;
Ok(())
})();
@@ -172,7 +181,7 @@ impl ArchiveManager {
let composer_json_path = format!("{}/composer.json", source_path);
if file_exists(&composer_json_path) {
- let json_file = JsonFile::new(composer_json_path, None, None);
+ let json_file = JsonFile::new(composer_json_path, None, None)?;
let json_data = json_file.read()?;
if let Some(archive) = json_data.get("archive") {
if let Some(name) = archive.get("name").and_then(|v| v.as_str()) {
diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs
index c7d2557..1a716d0 100644
--- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs
+++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs
@@ -59,7 +59,7 @@ impl BaseExcludeFilterBase {
let rule = rule.trim_matches('/');
- let glob_regex = Glob::to_regex(rule);
+ let glob_regex = Glob::to_regex(rule, true, true);
let rule_regex = &glob_regex[2..glob_regex.len() - 2];
(
@@ -143,7 +143,7 @@ pub trait BaseExcludeFilter {
let rule = rule.trim_matches('/');
// remove delimiters as well as caret (^) and dollar sign ($) from the regex
- let glob_regex = Glob::to_regex(rule);
+ let glob_regex = Glob::to_regex(rule, true, true);
let rule_regex = &glob_regex[2..glob_regex.len() - 2];
(
diff --git a/crates/shirabe/src/package/archiver/git_exclude_filter.rs b/crates/shirabe/src/package/archiver/git_exclude_filter.rs
index dddad12..1a0ec4d 100644
--- a/crates/shirabe/src/package/archiver/git_exclude_filter.rs
+++ b/crates/shirabe/src/package/archiver/git_exclude_filter.rs
@@ -34,14 +34,17 @@ impl GitExcludeFilter {
}
fn parse_git_attributes_line_static(line: &str) -> Option<(String, bool, bool)> {
- let parts = Preg::split(r"\s+", line);
+ let parts = Preg::split(r"\s+", line).unwrap_or_default();
if parts.len() == 2 && parts[1] == "export-ignore" {
return Some(BaseExcludeFilterBase::generate_pattern(&parts[0]));
}
if parts.len() == 2 && parts[1] == "-export-ignore" {
- return BaseExcludeFilterBase::generate_pattern(&format!("!{}", parts[0]));
+ return Some(BaseExcludeFilterBase::generate_pattern(&format!(
+ "!{}",
+ parts[0]
+ )));
}
None
diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs
index 2e9d96e..7b5142b 100644
--- a/crates/shirabe/src/package/archiver/phar_archiver.rs
+++ b/crates/shirabe/src/package/archiver/phar_archiver.rs
@@ -29,6 +29,12 @@ fn compress_formats() -> IndexMap<&'static str, i64> {
#[derive(Debug)]
pub struct PharArchiver;
+impl PharArchiver {
+ pub fn new() -> Self {
+ Self
+ }
+}
+
impl ArchiverInterface for PharArchiver {
fn archive(
&self,
@@ -46,6 +52,7 @@ impl ArchiverInterface for PharArchiver {
unlink(&target);
}
+ let target_outer = target.clone();
let inner = (|| -> anyhow::Result<String> {
let pos = strrpos(&target, &format).unwrap_or(target.len());
let filename = target[..pos.saturating_sub(1)].to_string();
@@ -63,7 +70,10 @@ impl ArchiverInterface for PharArchiver {
*formats.get(format.as_str()).unwrap_or(&Phar::TAR),
);
let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?;
- let mut files_only = ArchivableFilesFilter::new(files);
+ // TODO(phase-b): unify iterator types (ArchivableFilesFinder yields SplFileInfo,
+ // ArchivableFilesFilter expects PathBuf).
+ let mut files_only =
+ ArchivableFilesFilter::new(Box::new(files.map(|f| f.get_pathname().into())));
phar.build_from_iterator(&mut files_only, &sources);
files_only.add_empty_dir(&phar, &sources);
@@ -137,7 +147,7 @@ impl ArchiverInterface for PharArchiver {
inner.map_err(|e| {
let message = format!(
"Could not create archive '{}' from '{}': {}",
- target, sources, e
+ target_outer, sources, e
);
anyhow::anyhow!(RuntimeException { message, code: 0 })
})
diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs
index ef5b40a..471352f 100644
--- a/crates/shirabe/src/package/archiver/zip_archiver.rs
+++ b/crates/shirabe/src/package/archiver/zip_archiver.rs
@@ -13,6 +13,10 @@ use shirabe_php_shim::{
pub struct ZipArchiver;
impl ZipArchiver {
+ pub fn new() -> Self {
+ Self
+ }
+
fn formats() -> IndexMap<String, bool> {
let mut map = IndexMap::new();
map.insert("zip".to_string(), true);
@@ -33,7 +37,7 @@ impl ArchiverInterface for ZipArchiver {
excludes: Vec<String>,
ignore_filters: bool,
) -> anyhow::Result<String> {
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
let sources_realpath = realpath(&sources);
let sources = if let Some(p) = sources_realpath {
p
@@ -47,7 +51,7 @@ impl ArchiverInterface for ZipArchiver {
let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?;
for file in files {
let filepath = file.get_pathname();
- let mut relative_path = file.get_relative_pathname();
+ let mut relative_path = file.get_relative_path_name();
if Platform::is_windows() {
relative_path = shirabe_php_shim::strtr(&relative_path, "\\", "/");
diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs
index 2e50c44..fb906d1 100644
--- a/crates/shirabe/src/package/base_package.rs
+++ b/crates/shirabe/src/package/base_package.rs
@@ -83,62 +83,20 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>);
fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>>;
- fn as_any(&self) -> &dyn std::any::Any;
fn clone_box(&self) -> Box<dyn BasePackage>;
- fn get_name(&self) -> &str {
- self.name()
- }
-
- fn get_pretty_name(&self) -> &str {
- self.pretty_name()
- }
-
- fn get_names(&self, provides: bool) -> Vec<String> {
- let mut names: IndexMap<String, bool> = IndexMap::new();
- names.insert(self.get_name().to_string(), true);
-
- if provides {
- for link in self.get_provides().values() {
- names.insert(link.get_target().to_string(), true);
- }
- }
-
- for link in self.get_replaces().values() {
- names.insert(link.get_target().to_string(), true);
- }
-
- names.into_keys().collect()
- }
-
- fn set_id(&mut self, id: i64) {
- *self.id_mut() = id;
- }
+ // as_alias_package / as_complete_package_interface inherited from PackageInterface.
- fn get_id(&self) -> i64 {
- self.id()
+ fn as_alias_package_mut(&mut self) -> Option<&mut crate::package::alias_package::AliasPackage> {
+ None
}
- fn set_repository(&mut self, repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> {
- if let Some(existing) = self.repository_opt() {
- // TODO(phase-b): proper reference identity check before raising error
- return Err(anyhow::anyhow!(LogicException {
- message: format!(
- "Package \"{}\" cannot be added to repository \"{}\" as it is already in repository \"{}\".",
- self.get_pretty_name(),
- repository.get_repo_name(),
- existing.get_repo_name(),
- ),
- code: 0,
- }));
- }
- self.set_repository_box(repository);
- Ok(())
- }
+ // get_name / get_pretty_name / get_names live on PackageInterface; the BasePackage
+ // duplicates were causing ambiguity at every call site (`pkg.get_name()` with
+ // pkg: &dyn BasePackage). Concrete impls already forward to name()/pretty_name().
- fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
- self.repository_opt()
- }
+ // set_id, get_id, get_repository, get_unique_name, set_repository are inherited
+ // from PackageInterface; do not redeclare here to avoid trait-method ambiguity.
fn is_platform(&self) -> bool {
self.repository_opt()
@@ -146,24 +104,18 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
.is_some()
}
- fn get_unique_name(&self) -> String {
- format!("{}-{}", self.get_name(), self.get_version())
- }
-
fn equals(&self, _package: &dyn PackageInterface) -> bool {
// TODO(phase-b): implement via reference identity (requires Rc/Arc)
// PHP uses === which is reference equality; unwraps AliasPackage on both sides
todo!("equals requires reference identity which needs Rc/Arc")
}
- fn get_pretty_string(&self) -> String {
- format!("{} {}", self.get_pretty_name(), self.get_pretty_version())
- }
+ // get_pretty_string is inherited from PackageInterface.
fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> anyhow::Result<String> {
- const DISPLAY_SOURCE_REF_IF_DEV: i64 = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV;
- const DISPLAY_SOURCE_REF: i64 = PackageInterface::DISPLAY_SOURCE_REF;
- const DISPLAY_DIST_REF: i64 = PackageInterface::DISPLAY_DIST_REF;
+ const DISPLAY_SOURCE_REF_IF_DEV: i64 = <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV;
+ const DISPLAY_SOURCE_REF: i64 = <dyn PackageInterface>::DISPLAY_SOURCE_REF;
+ const DISPLAY_DIST_REF: i64 = <dyn PackageInterface>::DISPLAY_DIST_REF;
if display_mode == DISPLAY_SOURCE_REF_IF_DEV
&& (!self.is_dev()
@@ -207,7 +159,7 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
fn get_stability_priority(&self) -> i64 {
*STABILITIES
.get(self.get_stability())
- .unwrap_or(&Self::STABILITY_STABLE)
+ .unwrap_or(&STABILITY_STABLE)
}
fn php_clone(&mut self) {
diff --git a/crates/shirabe/src/package/comparer/comparer.rs b/crates/shirabe/src/package/comparer/comparer.rs
index 0e0d295..8f406a5 100644
--- a/crates/shirabe/src/package/comparer/comparer.rs
+++ b/crates/shirabe/src/package/comparer/comparer.rs
@@ -65,7 +65,7 @@ impl Comparer {
let mut source: IndexMap<String, IndexMap<String, Option<String>>> = IndexMap::new();
let mut destination: IndexMap<String, IndexMap<String, Option<String>>> = IndexMap::new();
self.changed = IndexMap::new();
- let current_directory = Platform::get_cwd();
+ let current_directory = Platform::get_cwd(false).unwrap_or_default();
shirabe_php_shim::chdir(&self.source);
if !Self::do_tree(".", &mut source) {
return;
diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs
index 134ed3a..d187eea 100644
--- a/crates/shirabe/src/package/complete_alias_package.rs
+++ b/crates/shirabe/src/package/complete_alias_package.rs
@@ -13,7 +13,13 @@ pub struct CompleteAliasPackage {
impl CompleteAliasPackage {
pub fn new(alias_of: CompletePackage, version: String, pretty_version: String) -> Self {
- let inner = AliasPackage::new(alias_of.clone(), version, pretty_version);
+ // 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>.
+ let inner = AliasPackage::new(
+ todo!("share CompletePackage via Rc"),
+ version,
+ pretty_version,
+ );
Self { inner, alias_of }
}
@@ -61,7 +67,8 @@ impl CompleteAliasPackage {
}
pub fn set_description(&mut self, description: Option<String>) {
- self.alias_of.set_description(description);
+ self.alias_of
+ .set_description(description.unwrap_or_default());
}
pub fn get_homepage(&self) -> Option<&str> {
@@ -69,7 +76,7 @@ impl CompleteAliasPackage {
}
pub fn set_homepage(&mut self, homepage: Option<String>) {
- self.alias_of.set_homepage(homepage);
+ self.alias_of.set_homepage(homepage.unwrap_or_default());
}
pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> {
@@ -116,7 +123,7 @@ impl CompleteAliasPackage {
}
pub fn set_archive_name(&mut self, name: Option<String>) {
- self.alias_of.set_archive_name(name);
+ self.alias_of.set_archive_name(name.unwrap_or_default());
}
pub fn get_archive_excludes(&self) -> Vec<String> {
diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs
index a3db372..27c49c6 100644
--- a/crates/shirabe/src/package/complete_package.rs
+++ b/crates/shirabe/src/package/complete_package.rs
@@ -133,6 +133,10 @@ impl CompletePackageInterface for CompletePackage {
}
impl PackageInterface for CompletePackage {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_name(&self) -> &str {
todo!()
}
@@ -253,15 +257,15 @@ impl PackageInterface for CompletePackage {
todo!()
}
- fn get_conflicts(&self) -> Vec<super::link::Link> {
+ fn get_conflicts(&self) -> IndexMap<String, super::link::Link> {
todo!()
}
- fn get_provides(&self) -> Vec<super::link::Link> {
+ fn get_provides(&self) -> IndexMap<String, super::link::Link> {
todo!()
}
- fn get_replaces(&self) -> Vec<super::link::Link> {
+ fn get_replaces(&self) -> IndexMap<String, super::link::Link> {
todo!()
}
diff --git a/crates/shirabe/src/package/complete_package_interface.rs b/crates/shirabe/src/package/complete_package_interface.rs
index 31780a7..d1120ac 100644
--- a/crates/shirabe/src/package/complete_package_interface.rs
+++ b/crates/shirabe/src/package/complete_package_interface.rs
@@ -55,4 +55,8 @@ pub trait CompletePackageInterface: PackageInterface {
fn get_archive_excludes(&self) -> Vec<String>;
fn set_archive_excludes(&mut self, excludes: Vec<String>);
+
+ fn as_package_interface(&self) -> &dyn crate::package::package_interface::PackageInterface {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs
index 02f1092..d81fd6c 100644
--- a/crates/shirabe/src/package/dumper/array_dumper.rs
+++ b/crates/shirabe/src/package/dumper/array_dumper.rs
@@ -131,8 +131,10 @@ impl ArrayDumper {
}
// corresponds to: foreach (BasePackage::$supportedLinkTypes as $type => $opts) { $links = $package->{'get'.ucfirst($opts['method'])}(); ... }
- for (type_name, method_name) in BasePackage::supported_link_types() {
- let links = package.get_links_by_method(&method_name);
+ for (type_name, method_name) in <dyn BasePackage>::supported_link_types() {
+ // TODO(phase-b): PackageInterface needs get_links_by_method to mimic PHP magic call
+ let links: Vec<crate::package::link::Link> = Vec::new();
+ let _ = (&method_name, package);
if links.is_empty() {
continue;
}
@@ -186,10 +188,9 @@ impl ArrayDumper {
),
);
}
- if let Some(pkg_type) = package.get_type() {
- if !pkg_type.is_empty() {
- data.insert("type".to_string(), PhpMixed::String(pkg_type.to_string()));
- }
+ let pkg_type = package.get_type();
+ if !pkg_type.is_empty() {
+ data.insert("type".to_string(), PhpMixed::String(pkg_type.to_string()));
}
let extra = package.get_extra();
if !extra.is_empty() {
@@ -247,10 +248,15 @@ impl ArrayDumper {
);
}
let php_ext = package.get_php_ext();
- if !php_ext.is_empty() {
+ if let Some(php_ext) = php_ext.as_ref().filter(|m| !m.is_empty()) {
data.insert(
"php-ext".to_string(),
- PhpMixed::Array(php_ext.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
+ PhpMixed::Array(
+ php_ext
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(v.clone())))
+ .collect(),
+ ),
);
}
diff --git a/crates/shirabe/src/package/link.rs b/crates/shirabe/src/package/link.rs
index 2233eb1..e41d431 100644
--- a/crates/shirabe/src/package/link.rs
+++ b/crates/shirabe/src/package/link.rs
@@ -13,6 +13,20 @@ pub struct Link {
pub(crate) pretty_constraint: Option<String>,
}
+impl Clone for Link {
+ fn clone(&self) -> Self {
+ // TODO(phase-b): Link is a PHP class; this clone is a shallow placeholder until
+ // Link is shared via Rc<Link>.
+ Self {
+ source: self.source.clone(),
+ target: self.target.clone(),
+ constraint: self.constraint.clone_box(),
+ description: self.description.clone(),
+ pretty_constraint: self.pretty_constraint.clone(),
+ }
+ }
+}
+
impl std::fmt::Debug for Link {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Link")
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 28e13af..275d718 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -588,7 +588,7 @@ impl ArrayLoader {
let alias_normalized = self.get_branch_alias(config)?;
if let Some(alias_normalized) = alias_normalized {
if !alias_normalized.is_empty() {
- let pretty_alias = Preg::replace(r"{(\.9{7})+}", ".x", &alias_normalized);
+ let pretty_alias = Preg::replace(r"{(\.9{7})+}", ".x", &alias_normalized)?;
// TODO(phase-b): `$package instanceof RootPackage` downcast from CompletePackage
let package_as_root: Option<RootPackage> = None;
diff --git a/crates/shirabe/src/package/loader/invalid_package_exception.rs b/crates/shirabe/src/package/loader/invalid_package_exception.rs
index 96725ba..764b31f 100644
--- a/crates/shirabe/src/package/loader/invalid_package_exception.rs
+++ b/crates/shirabe/src/package/loader/invalid_package_exception.rs
@@ -1,5 +1,6 @@
//! ref: composer/src/Composer/Package/Loader/InvalidPackageException.php
+use indexmap::IndexMap;
use shirabe_php_shim::{Exception, PhpMixed};
#[derive(Debug)]
@@ -7,11 +8,15 @@ pub struct InvalidPackageException {
inner: Exception,
errors: Vec<String>,
warnings: Vec<String>,
- data: Vec<PhpMixed>,
+ data: IndexMap<String, PhpMixed>,
}
impl InvalidPackageException {
- pub fn new(errors: Vec<String>, warnings: Vec<String>, data: Vec<PhpMixed>) -> Self {
+ pub fn new(
+ errors: Vec<String>,
+ warnings: Vec<String>,
+ data: IndexMap<String, PhpMixed>,
+ ) -> Self {
let message = format!(
"Invalid package information: \n{}",
errors
@@ -29,7 +34,7 @@ impl InvalidPackageException {
}
}
- pub fn get_data(&self) -> &[PhpMixed] {
+ pub fn get_data(&self) -> &IndexMap<String, PhpMixed> {
&self.data
}
diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs
index 978bc28..cb3cbeb 100644
--- a/crates/shirabe/src/package/loader/json_loader.rs
+++ b/crates/shirabe/src/package/loader/json_loader.rs
@@ -22,13 +22,16 @@ impl JsonLoader {
pub fn load(&self, json: JsonLoaderInput) -> Result<Box<dyn BasePackage>> {
let config = match json {
- JsonLoaderInput::File(json_file) => json_file.read()?,
+ JsonLoaderInput::File(mut json_file) => json_file.read()?,
JsonLoaderInput::String(ref s) if Path::new(s).exists() => {
- JsonFile::parse_json(&std::fs::read_to_string(s)?, Some(s))?
+ let contents = std::fs::read_to_string(s)?;
+ JsonFile::parse_json(Some(&contents), Some(s))?
}
- JsonLoaderInput::String(ref s) => JsonFile::parse_json(s, None)?,
+ JsonLoaderInput::String(ref s) => JsonFile::parse_json(Some(s), None)?,
};
- self.loader.load(config, None)
+ // TODO(phase-b): JsonFile::parse_json returns PhpMixed; loader::load expects IndexMap
+ let _ = config;
+ self.loader.load(indexmap::IndexMap::new(), None)
}
}
diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs
index 3ba9212..5009a0d 100644
--- a/crates/shirabe/src/package/loader/validating_array_loader.rs
+++ b/crates/shirabe/src/package/loader/validating_array_loader.rs
@@ -1288,7 +1288,10 @@ impl ValidatingArrayLoader {
return Err(anyhow::anyhow!(InvalidPackageException::new(
self.errors.clone(),
self.warnings.clone(),
- config.values().map(|v| (**v).clone()).collect(),
+ config
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect(),
)));
}
diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs
index 5950437..397af08 100644
--- a/crates/shirabe/src/package/locker.rs
+++ b/crates/shirabe/src/package/locker.rs
@@ -134,7 +134,6 @@ impl Locker {
.collect(),
),
0,
- JsonFile::INDENT_DEFAULT,
),
))
}
@@ -616,13 +615,13 @@ impl Locker {
} else {
self.virtual_file_written = true;
self.lock_data_cache = Some(JsonFile::parse_json(
- &JsonFile::encode(
+ Some(&JsonFile::encode_with_indent(
&PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
shirabe_php_shim::JSON_UNESCAPED_SLASHES
| shirabe_php_shim::JSON_PRETTY_PRINT
| shirabe_php_shim::JSON_UNESCAPED_UNICODE,
JsonFile::INDENT_DEFAULT,
- ),
+ )),
None,
)?);
}
diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs
index b8b6770..74286a0 100644
--- a/crates/shirabe/src/package/package.rs
+++ b/crates/shirabe/src/package/package.rs
@@ -71,7 +71,10 @@ impl Package {
let stability = VersionParser::parse_stability(&version).to_string();
let dev = stability == "dev";
Self {
- inner: BasePackage::new(name),
+ id: -1,
+ name: name.to_lowercase(),
+ pretty_name: name,
+ repository: None,
r#type: None,
target_dir: None,
installation_source: None,
@@ -458,7 +461,7 @@ impl Package {
let url = if url_type == "dist" && strpos(url, "%").is_some() {
ComposerMirror::process_url(
url,
- &self.inner.name,
+ &self.name,
&self.version,
r#ref.unwrap_or(""),
r#type.unwrap_or(""),
@@ -474,7 +477,7 @@ impl Package {
let mirror_url = if url_type == "dist" {
ComposerMirror::process_url(
&mirror.url,
- &self.inner.name,
+ &self.name,
&self.version,
r#ref.unwrap_or(""),
r#type.unwrap_or(""),
@@ -483,14 +486,14 @@ impl Package {
} else if url_type == "source" && r#type == Some("git") {
ComposerMirror::process_git_url(
&mirror.url,
- &self.inner.name,
+ &self.name,
&url,
r#type.unwrap_or(""),
)
} else if url_type == "source" && r#type == Some("hg") {
ComposerMirror::process_hg_url(
&mirror.url,
- &self.inner.name,
+ &self.name,
&url,
r#type.unwrap_or(""),
)
@@ -568,10 +571,6 @@ impl BasePackage for Package {
todo!()
}
- fn as_any(&self) -> &dyn std::any::Any {
- todo!()
- }
-
fn clone_box(&self) -> Box<dyn BasePackage> {
todo!()
}
@@ -584,6 +583,9 @@ impl std::fmt::Display for Package {
}
impl PackageInterface for Package {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
fn get_name(&self) -> &str {
todo!()
}
@@ -674,13 +676,13 @@ impl PackageInterface for Package {
fn get_requires(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_conflicts(&self) -> Vec<Link> {
+ fn get_conflicts(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_provides(&self) -> Vec<Link> {
+ fn get_provides(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_replaces(&self) -> Vec<Link> {
+ fn get_replaces(&self) -> IndexMap<String, Link> {
todo!()
}
fn get_dev_requires(&self) -> IndexMap<String, Link> {
diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs
index 6a07556..c6ccce8 100644
--- a/crates/shirabe/src/package/package_interface.rs
+++ b/crates/shirabe/src/package/package_interface.rs
@@ -14,7 +14,9 @@ use crate::repository::repository_interface::RepositoryInterface;
/// @phpstan-type AutoloadRules array{psr-0?: array<string, string|string[]>, psr-4?: array<string, string|string[]>, classmap?: list<string>, files?: list<string>, exclude-from-classmap?: list<string>}
/// @phpstan-type DevAutoloadRules array{psr-0?: array<string, string|string[]>, psr-4?: array<string, string|string[]>, classmap?: list<string>, files?: list<string>}
/// @phpstan-type PhpExtConfig array{extension-name?: string, priority?: int, support-zts?: bool, support-nts?: bool, build-path?: string|null, download-url-method?: string|list<string>, os-families?: non-empty-list<non-empty-string>, os-families-exclude?: non-empty-list<non-empty-string>, configure-options?: list<array{name: string, description?: string}>}
-pub trait PackageInterface: std::fmt::Display {
+pub trait PackageInterface: std::fmt::Display + std::fmt::Debug {
+ fn as_any(&self) -> &dyn std::any::Any;
+
/// Returns the package's name without version info, thus not a unique identifier
///
/// @return string package name
@@ -170,20 +172,20 @@ pub trait PackageInterface: std::fmt::Display {
/// Returns a set of links to packages which must not be installed at the
/// same time as this package
///
- /// @return Link[] An array of package links defining conflicting packages
- fn get_conflicts(&self) -> Vec<Link>;
+ /// @return array<string, Link> A map of package links defining conflicting packages
+ fn get_conflicts(&self) -> IndexMap<String, Link>;
/// Returns a set of links to virtual packages that are provided through
/// this package
///
- /// @return Link[] An array of package links defining provided packages
- fn get_provides(&self) -> Vec<Link>;
+ /// @return array<string, Link> A map of package links defining provided packages
+ fn get_provides(&self) -> IndexMap<String, Link>;
/// Returns a set of links to packages which can alternatively be
/// satisfied by installing this package
///
- /// @return Link[] An array of package links defining replaced packages
- fn get_replaces(&self) -> Vec<Link>;
+ /// @return array<string, Link> A map of package links defining replaced packages
+ fn get_replaces(&self) -> IndexMap<String, Link>;
/// Returns a set of links to packages which are required to develop
/// this package. These are installed if in dev mode.
@@ -275,6 +277,30 @@ pub trait PackageInterface: std::fmt::Display {
/// 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::alias_package::AliasPackage> {
+ None
+ }
+
+ fn as_complete_package_interface(
+ &self,
+ ) -> Option<&dyn crate::package::complete_package_interface::CompletePackageInterface> {
+ None
+ }
+
+ fn as_complete_package(
+ &self,
+ ) -> Option<&dyn crate::package::complete_package_interface::CompletePackageInterface> {
+ None
+ }
}
impl dyn PackageInterface {
diff --git a/crates/shirabe/src/package/root_alias_package.rs b/crates/shirabe/src/package/root_alias_package.rs
index 703c1f9..cb458f3 100644
--- a/crates/shirabe/src/package/root_alias_package.rs
+++ b/crates/shirabe/src/package/root_alias_package.rs
@@ -231,6 +231,10 @@ impl std::fmt::Display for RootAliasPackage {
}
impl PackageInterface for RootAliasPackage {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_name(&self) -> &str {
todo!()
}
@@ -321,13 +325,13 @@ impl PackageInterface for RootAliasPackage {
fn get_requires(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_conflicts(&self) -> Vec<Link> {
+ fn get_conflicts(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_provides(&self) -> Vec<Link> {
+ fn get_provides(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_replaces(&self) -> Vec<Link> {
+ fn get_replaces(&self) -> IndexMap<String, Link> {
todo!()
}
fn get_dev_requires(&self) -> IndexMap<String, Link> {
diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs
index 2557c8a..af8d1f5 100644
--- a/crates/shirabe/src/package/root_package.rs
+++ b/crates/shirabe/src/package/root_package.rs
@@ -24,6 +24,21 @@ pub struct RootPackage {
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);
+ Self {
+ inner,
+ minimum_stability: "stable".to_string(),
+ prefer_stable: false,
+ stability_flags: IndexMap::new(),
+ config: IndexMap::new(),
+ references: IndexMap::new(),
+ aliases: Vec::new(),
+ }
+ }
}
impl RootPackageInterface for RootPackage {
@@ -221,6 +236,10 @@ impl std::fmt::Display for RootPackage {
}
impl PackageInterface for RootPackage {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_name(&self) -> &str {
todo!()
}
@@ -311,13 +330,13 @@ impl PackageInterface for RootPackage {
fn get_requires(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_conflicts(&self) -> Vec<Link> {
+ fn get_conflicts(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_provides(&self) -> Vec<Link> {
+ fn get_provides(&self) -> IndexMap<String, Link> {
todo!()
}
- fn get_replaces(&self) -> Vec<Link> {
+ fn get_replaces(&self) -> IndexMap<String, Link> {
todo!()
}
fn get_dev_requires(&self) -> IndexMap<String, Link> {
diff --git a/crates/shirabe/src/package/root_package_interface.rs b/crates/shirabe/src/package/root_package_interface.rs
index a8634af..a053e28 100644
--- a/crates/shirabe/src/package/root_package_interface.rs
+++ b/crates/shirabe/src/package/root_package_interface.rs
@@ -3,6 +3,8 @@
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
+use crate::package::package_interface::PackageInterface;
+
use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::link::Link;
@@ -48,4 +50,16 @@ pub trait RootPackageInterface: CompletePackageInterface {
fn set_suggests(&mut self, suggests: IndexMap<String, String>);
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/stability_filter.rs b/crates/shirabe/src/package/version/stability_filter.rs
index d08492c..5053e9d 100644
--- a/crates/shirabe/src/package/version/stability_filter.rs
+++ b/crates/shirabe/src/package/version/stability_filter.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Package/Version/StabilityFilter.php
-use crate::package::base_package::BasePackage;
+use crate::package::base_package::STABILITIES;
use indexmap::IndexMap;
pub struct StabilityFilter;
@@ -15,7 +15,7 @@ impl StabilityFilter {
for name in names {
// allow if package matches the package-specific stability flag
if let Some(&flag) = stability_flags.get(name) {
- if let Some(&stability_value) = BasePackage::STABILITIES.get(stability) {
+ if let Some(&stability_value) = STABILITIES.get(stability) {
if stability_value <= flag {
return true;
}
diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs
index 5911458..8963fd6 100644
--- a/crates/shirabe/src/package/version/version_bumper.rs
+++ b/crates/shirabe/src/package/version/version_bumper.rs
@@ -26,11 +26,15 @@ impl VersionBumper {
return Ok(pretty_constraint);
}
- let mut version = package.get_version();
- if package.get_version().starts_with("dev-") {
- let loader = ArrayLoader::new(&parser);
+ let mut version = package.get_version().to_string();
+ if version.starts_with("dev-") {
+ // TODO(phase-b): ArrayLoader::new takes Option<VersionParser> by value; pass None until
+ // VersionParser sharing is reconciled.
+ let _ = &parser;
+ let loader = ArrayLoader::new(None, false);
let dumper = ArrayDumper::new();
- let extra = loader.get_branch_alias(dumper.dump(package));
+ let dumped = dumper.dump(package);
+ let extra = loader.get_branch_alias(&dumped)?;
if extra.is_none() || extra.as_deref() == Some(VersionParser::DEFAULT_BRANCH_ALIAS) {
return Ok(pretty_constraint);
diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs
index 6bb1004..6dbf425 100644
--- a/crates/shirabe/src/package/version/version_parser.rs
+++ b/crates/shirabe/src/package/version/version_parser.rs
@@ -74,6 +74,28 @@ impl VersionParser {
Ok(result)
}
+ pub fn new() -> Self {
+ Self {
+ inner: SemverVersionParser,
+ }
+ }
+
+ pub fn normalize(&self, version: &str, full_version: Option<&str>) -> anyhow::Result<String> {
+ self.inner.normalize(version, full_version)
+ }
+
+ pub fn normalize_stability(stability: &str) -> anyhow::Result<String> {
+ SemverVersionParser::normalize_stability(stability)
+ }
+
+ pub fn normalize_branch(&self, name: &str) -> anyhow::Result<String> {
+ self.inner.normalize_branch(name)
+ }
+
+ pub fn parse_stability(version: &str) -> String {
+ SemverVersionParser::parse_stability(version)
+ }
+
pub fn is_upgrade(normalized_from: &str, normalized_to: &str) -> anyhow::Result<bool> {
if normalized_from == normalized_to {
return Ok(true);
diff --git a/crates/shirabe/src/partial_composer.rs b/crates/shirabe/src/partial_composer.rs
index a08e272..5547b5a 100644
--- a/crates/shirabe/src/partial_composer.rs
+++ b/crates/shirabe/src/partial_composer.rs
@@ -11,7 +11,7 @@ use crate::util::r#loop::Loop;
pub struct PartialComposer {
global: bool,
package: Option<Box<dyn RootPackageInterface>>,
- r#loop: Option<Loop>,
+ r#loop: Option<std::rc::Rc<std::cell::RefCell<Loop>>>,
repository_manager: Option<RepositoryManager>,
installation_manager: Option<InstallationManager>,
config: Option<Config>,
@@ -35,11 +35,11 @@ impl PartialComposer {
self.config.as_ref().unwrap()
}
- pub fn set_loop(&mut self, r#loop: Loop) {
+ pub fn set_loop(&mut self, r#loop: std::rc::Rc<std::cell::RefCell<Loop>>) {
self.r#loop = Some(r#loop);
}
- pub fn get_loop(&self) -> &Loop {
+ pub fn get_loop(&self) -> &std::rc::Rc<std::cell::RefCell<Loop>> {
self.r#loop.as_ref().unwrap()
}
diff --git a/crates/shirabe/src/platform/hhvm_detector.rs b/crates/shirabe/src/platform/hhvm_detector.rs
index c6a53cb..b425bfe 100644
--- a/crates/shirabe/src/platform/hhvm_detector.rs
+++ b/crates/shirabe/src/platform/hhvm_detector.rs
@@ -9,6 +9,7 @@ use std::sync::Mutex;
// None = null (uninitialized), Some(None) = false (not found), Some(Some(v)) = version
static HHVM_VERSION_CACHE: Mutex<Option<Option<String>>> = Mutex::new(None);
+#[derive(Debug)]
pub struct HhvmDetector {
executable_finder: Option<ExecutableFinder>,
process_executor: Option<ProcessExecutor>,
@@ -47,25 +48,30 @@ impl HhvmDetector {
let finder = self
.executable_finder
.get_or_insert_with(ExecutableFinder::new);
- let hhvm_path = finder.find("hhvm");
+ let hhvm_path = finder.find("hhvm", None, &[]);
if let Some(hhvm_path) = hhvm_path {
let executor = self
.process_executor
- .get_or_insert_with(ProcessExecutor::new);
- let mut version_output = String::new();
- let exit_code = executor.execute(
- &[
- &hhvm_path,
+ .get_or_insert_with(|| ProcessExecutor::new(None, None));
+ let mut version_output = shirabe_php_shim::PhpMixed::Null;
+ let cmd = shirabe_php_shim::PhpMixed::List(
+ [
+ hhvm_path.as_str(),
"--php",
"-d",
"hhvm.jit=0",
"-r",
"echo HHVM_VERSION;",
- ],
- &mut version_output,
+ ]
+ .into_iter()
+ .map(|s| Box::new(shirabe_php_shim::PhpMixed::String(s.to_string())))
+ .collect(),
);
+ let exit_code = executor
+ .execute(cmd, Some(&mut version_output), None)
+ .unwrap_or(1);
if exit_code == 0 {
- *cache = Some(Some(version_output));
+ *cache = Some(version_output.as_string().map(|s| s.to_string()));
}
}
}
diff --git a/crates/shirabe/src/plugin/command_event.rs b/crates/shirabe/src/plugin/command_event.rs
index aa5b366..715f29a 100644
--- a/crates/shirabe/src/plugin/command_event.rs
+++ b/crates/shirabe/src/plugin/command_event.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Plugin/CommandEvent.php
use crate::event_dispatcher::event::Event;
+use indexmap::IndexMap;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
@@ -9,34 +10,28 @@ use shirabe_php_shim::PhpMixed;
pub struct CommandEvent {
inner: Event,
command_name: String,
- input: Box<dyn InputInterface>,
- output: Box<dyn OutputInterface>,
}
impl CommandEvent {
+ // TODO(phase-b): input/output dropped because storing &dyn references in an event would
+ // require lifetime parameters; restore once Plugin API needs them.
pub fn new(
name: String,
command_name: String,
- input: Box<dyn InputInterface>,
- output: Box<dyn OutputInterface>,
- args: Vec<PhpMixed>,
- flags: Vec<PhpMixed>,
+ _input: &dyn InputInterface,
+ _output: &dyn OutputInterface,
+ args: Vec<String>,
+ flags: IndexMap<String, PhpMixed>,
) -> Self {
let inner = Event::new(name, args, flags);
Self {
inner,
command_name,
- input,
- output,
}
}
- pub fn get_input(&self) -> &dyn InputInterface {
- self.input.as_ref()
- }
-
- pub fn get_output(&self) -> &dyn OutputInterface {
- self.output.as_ref()
+ pub fn get_name(&self) -> &str {
+ self.inner.get_name()
}
pub fn get_command_name(&self) -> &str {
diff --git a/crates/shirabe/src/plugin/plugin_blocked_exception.rs b/crates/shirabe/src/plugin/plugin_blocked_exception.rs
index cf3c7ce..fcf52ac 100644
--- a/crates/shirabe/src/plugin/plugin_blocked_exception.rs
+++ b/crates/shirabe/src/plugin/plugin_blocked_exception.rs
@@ -6,6 +6,12 @@ use shirabe_php_shim::UnexpectedValueException;
#[derive(Debug)]
pub struct PluginBlockedException(pub UnexpectedValueException);
+impl PluginBlockedException {
+ pub fn new(message: String) -> Self {
+ Self(UnexpectedValueException { message, code: 0 })
+ }
+}
+
impl std::fmt::Display for PluginBlockedException {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
diff --git a/crates/shirabe/src/plugin/plugin_interface.rs b/crates/shirabe/src/plugin/plugin_interface.rs
index d536380..a7bb384 100644
--- a/crates/shirabe/src/plugin/plugin_interface.rs
+++ b/crates/shirabe/src/plugin/plugin_interface.rs
@@ -5,10 +5,14 @@ use crate::io::io_interface::IOInterface;
pub const PLUGIN_API_VERSION: &'static str = "2.9.0";
-pub trait PluginInterface {
+pub trait PluginInterface: std::fmt::Debug {
fn activate(&mut self, composer: &Composer, io: &dyn IOInterface);
fn deactivate(&mut self, composer: &Composer, io: &dyn IOInterface);
fn uninstall(&mut self, composer: &Composer, io: &dyn IOInterface);
+
+ fn clone_box(&self) -> Box<dyn PluginInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs
index adf5954..24b8e0b 100644
--- a/crates/shirabe/src/plugin/plugin_manager.rs
+++ b/crates/shirabe/src/plugin/plugin_manager.rs
@@ -832,14 +832,15 @@ impl PluginManager {
"allow-plugins",
PhpMixed::Array(allow_plugins.clone()),
);
- let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
+ // TODO(phase-b): get_config() returns &Config, but merge needs &mut Config; ownership needs refactoring
let mut inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
inner.insert(
"allow-plugins".to_string(),
Box::new(PhpMixed::Array(allow_plugins)),
);
- wrap.insert("config".to_string(), Box::new(PhpMixed::Array(inner)));
- composer_ref.get_config().merge(PhpMixed::Array(wrap), "");
+ let mut wrap: IndexMap<String, PhpMixed> = IndexMap::new();
+ wrap.insert("config".to_string(), PhpMixed::Array(inner));
+ todo!("see TODO(phase-b) above");
}
}
diff --git a/crates/shirabe/src/plugin/post_file_download_event.rs b/crates/shirabe/src/plugin/post_file_download_event.rs
index f0e2569..549807a 100644
--- a/crates/shirabe/src/plugin/post_file_download_event.rs
+++ b/crates/shirabe/src/plugin/post_file_download_event.rs
@@ -33,6 +33,10 @@ impl PostFileDownloadEvent {
}
}
+ pub fn get_name(&self) -> &str {
+ self.inner.get_name()
+ }
+
pub fn get_file_name(&self) -> Option<&str> {
self.file_name.as_deref()
}
diff --git a/crates/shirabe/src/plugin/pre_command_run_event.rs b/crates/shirabe/src/plugin/pre_command_run_event.rs
index 40672c0..7a8b2f1 100644
--- a/crates/shirabe/src/plugin/pre_command_run_event.rs
+++ b/crates/shirabe/src/plugin/pre_command_run_event.rs
@@ -4,24 +4,21 @@
use crate::event_dispatcher::event::Event;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
+#[derive(Debug)]
pub struct PreCommandRunEvent {
inner: Event,
- input: Box<dyn InputInterface>,
command: String,
}
impl PreCommandRunEvent {
- pub fn new(name: String, input: Box<dyn InputInterface>, command: String) -> Self {
- let inner = Event::new(name);
- Self {
- inner,
- input,
- command,
- }
+ // TODO(phase-b): input dropped because storing a &dyn reference would need lifetime params.
+ pub fn new(name: String, _input: &dyn InputInterface, command: String) -> Self {
+ let inner = Event::new(name, vec![], indexmap::IndexMap::new());
+ Self { inner, command }
}
- pub fn get_input(&self) -> &dyn InputInterface {
- self.input.as_ref()
+ pub fn get_name(&self) -> &str {
+ self.inner.get_name()
}
pub fn get_command(&self) -> &str {
diff --git a/crates/shirabe/src/plugin/pre_pool_create_event.rs b/crates/shirabe/src/plugin/pre_pool_create_event.rs
index 5d1a4e9..a1a6bc6 100644
--- a/crates/shirabe/src/plugin/pre_pool_create_event.rs
+++ b/crates/shirabe/src/plugin/pre_pool_create_event.rs
@@ -21,6 +21,10 @@ pub struct PrePoolCreateEvent {
}
impl PrePoolCreateEvent {
+ pub fn get_name(&self) -> &str {
+ self.inner.get_name()
+ }
+
#[allow(clippy::too_many_arguments)]
pub fn new(
name: String,
diff --git a/crates/shirabe/src/question/strict_confirmation_question.rs b/crates/shirabe/src/question/strict_confirmation_question.rs
index eea6629..b3a7196 100644
--- a/crates/shirabe/src/question/strict_confirmation_question.rs
+++ b/crates/shirabe/src/question/strict_confirmation_question.rs
@@ -19,7 +19,7 @@ impl StrictConfirmationQuestion {
true_answer_regex: String,
false_answer_regex: String,
) -> Self {
- let inner = Question::new(question, PhpMixed::Bool(default));
+ let inner = Question::new(&question, Some(PhpMixed::Bool(default)));
let mut this = Self {
inner,
true_answer_regex,
@@ -28,7 +28,7 @@ impl StrictConfirmationQuestion {
let normalizer = this.get_default_normalizer();
let validator = this.get_default_validator();
this.inner.set_normalizer(normalizer);
- this.inner.set_validator(validator);
+ this.inner.set_validator(Some(validator));
this
}
@@ -41,14 +41,14 @@ impl StrictConfirmationQuestion {
if is_bool(answer) {
return answer.clone();
}
- if empty(answer) && !empty(&default) {
- return default.clone();
+ if empty(answer) && default.as_ref().is_some_and(|d| !empty(d)) {
+ return default.clone().unwrap_or(PhpMixed::Null);
}
if let PhpMixed::String(s) = answer {
- if Preg::is_match(&true_regex, s) {
+ if Preg::is_match(&true_regex, s).unwrap_or(false) {
return PhpMixed::Bool(true);
}
- if Preg::is_match(&false_regex, s) {
+ if Preg::is_match(&false_regex, s).unwrap_or(false) {
return PhpMixed::Bool(false);
}
}
@@ -56,16 +56,17 @@ impl StrictConfirmationQuestion {
})
}
- fn get_default_validator(&self) -> Box<dyn Fn(&PhpMixed) -> Result<PhpMixed>> {
- Box::new(|answer: &PhpMixed| {
- if !is_bool(answer) {
+ fn get_default_validator(&self) -> Box<dyn Fn(Option<PhpMixed>) -> Result<PhpMixed>> {
+ Box::new(|answer: Option<PhpMixed>| {
+ let answer = answer.unwrap_or(PhpMixed::Null);
+ if !is_bool(&answer) {
return Err(InvalidArgumentException {
message: "Please answer yes, y, no, or n.".to_string(),
code: 0,
}
.into());
}
- Ok(answer.clone())
+ Ok(answer)
})
}
}
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index 5262b5e..5652dad 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -176,8 +176,10 @@ impl RepositoryInterface for ArrayRepository {
let mut result: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
let mut names_found: IndexMap<String, bool> = IndexMap::new();
for package in &packages {
- if package_name_map.contains_key(package.get_name()) {
- let constraint_opt = package_name_map.get(package.get_name()).unwrap();
+ 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();
let constraint_matches = match constraint_opt {
None => true,
Some(c) => c.matches(&Constraint::new("==", package.get_version())),
@@ -186,11 +188,11 @@ impl RepositoryInterface for ArrayRepository {
&& StabilityFilter::is_package_acceptable(
&acceptable_stabilities,
&stability_flags,
- &package.get_names(true),
+ &PackageInterface::get_names(package.as_ref(), true),
package.get_stability(),
)
&& !already_loaded
- .get(package.get_name())
+ .get(PackageInterface::get_name(package.as_ref()))
.map(|v| v.contains_key(package.get_version()))
.unwrap_or(false)
{
@@ -207,7 +209,10 @@ impl RepositoryInterface for ArrayRepository {
}
}
- names_found.insert(package.get_name().to_string(), true);
+ names_found.insert(
+ PackageInterface::get_name(package.as_ref()).to_string(),
+ true,
+ );
}
}
@@ -244,7 +249,7 @@ impl RepositoryInterface for ArrayRepository {
};
for package in self.get_packages() {
- if name == package.get_name() {
+ if name == PackageInterface::get_name(package.as_ref()) {
let pkg_constraint = Constraint::new("==", package.get_version());
if constraint.matches(&pkg_constraint) {
return Some(package);
@@ -275,7 +280,7 @@ impl RepositoryInterface for ArrayRepository {
};
for package in self.get_packages() {
- if name == package.get_name() {
+ if name == PackageInterface::get_name(package.as_ref()) {
if constraint.is_none()
|| constraint
.as_ref()
@@ -303,7 +308,7 @@ impl RepositoryInterface for ArrayRepository {
let mut matches: IndexMap<String, SearchResult> = IndexMap::new();
for package in self.get_packages() {
- let mut name = package.get_name().to_string();
+ let mut name = PackageInterface::get_name(package.as_ref()).to_string();
if mode == Self::SEARCH_VENDOR {
// PHP: [$name] = explode('/', $name);
let parts: Vec<&str> = name.splitn(2, '/').collect();
@@ -395,7 +400,7 @@ impl RepositoryInterface for ArrayRepository {
let mut result: IndexMap<String, ProviderInfo> = IndexMap::new();
'candidates: for candidate in self.get_packages() {
- if result.contains_key(candidate.get_name()) {
+ if result.contains_key(PackageInterface::get_name(candidate.as_ref())) {
continue;
}
for link in candidate.get_provides().values() {
@@ -404,9 +409,9 @@ impl RepositoryInterface for ArrayRepository {
(candidate.as_any() as &dyn Any).downcast_ref::<CompletePackage>();
let description = complete.and_then(|c| c.get_description().map(String::from));
result.insert(
- candidate.get_name().to_string(),
+ PackageInterface::get_name(candidate.as_ref()).to_string(),
ProviderInfo {
- name: candidate.get_name().to_string(),
+ name: PackageInterface::get_name(candidate.as_ref()).to_string(),
description,
r#type: candidate.get_type().to_string(),
},
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 9acebe0..dfbb0b5 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -37,6 +37,7 @@ use crate::repository::advisory_provider_interface::{
use crate::repository::array_repository::ArrayRepository;
use crate::repository::configurable_repository_interface::ConfigurableRepositoryInterface;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::repository_security_exception::RepositorySecurityException;
use crate::util::http::response::Response;
use crate::util::http_downloader::HttpDownloader;
@@ -84,7 +85,7 @@ pub struct ComposerRepository {
base_url: String,
io: Box<dyn IOInterface>,
http_downloader: HttpDownloader,
- r#loop: Loop,
+ r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
pub(crate) cache: Cache,
pub(crate) notify_url: Option<String>,
pub(crate) search_url: Option<String>,
@@ -270,7 +271,10 @@ impl ComposerRepository {
let version_parser = VersionParser::new();
let loader = ArrayLoader::new_with_parser(version_parser.clone());
- let r#loop = Loop::new(http_downloader.clone(), None);
+ let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
+ http_downloader.clone(),
+ None,
+ )));
let mut this = Self {
inner,
@@ -1010,13 +1014,13 @@ impl ComposerRepository {
}
let parser = VersionParser::new();
+ let semver_parser = shirabe_semver::version_parser::VersionParser;
let repo_name = self.get_repo_name();
let create = |data: &IndexMap<String, PhpMixed>,
name: &str,
package_constraint_map: &IndexMap<String, Box<dyn ConstraintInterface>>|
-> anyhow::Result<Option<PartialOrSecurityAdvisory>> {
- let advisory =
- PartialSecurityAdvisory::create(name.to_string(), data.clone(), &parser)?;
+ let advisory = PartialSecurityAdvisory::create(name, data, &semver_parser)?;
let is_full = matches!(advisory, PartialOrSecurityAdvisory::Full(_));
if !allow_partial_advisories && !is_full {
let data_mixed = PhpMixed::Array(
@@ -1036,13 +1040,13 @@ impl ComposerRepository {
}
.into());
}
- let affected_versions = match &advisory {
- PartialOrSecurityAdvisory::Partial(p) => &p.affected_versions,
- PartialOrSecurityAdvisory::Full(p) => &p.inner.affected_versions,
+ let affected_versions: &dyn ConstraintInterface = match &advisory {
+ PartialOrSecurityAdvisory::Partial(p) => &*p.affected_versions,
+ PartialOrSecurityAdvisory::Full(p) => p.affected_versions(),
};
let constraint = package_constraint_map.get(name).map(|c| &**c);
if let Some(c) = constraint {
- if !affected_versions.matches_constraint(c) {
+ if !affected_versions.matches(c) {
return Ok(None);
}
} else {
@@ -1126,7 +1130,7 @@ impl ComposerRepository {
promises.push(promise);
}
- self.r#loop.wait(promises, None)?;
+ self.r#loop.borrow_mut().wait(promises, None)?;
}
if let Some(api_url) = api_url {
@@ -1980,7 +1984,7 @@ impl ComposerRepository {
promises.push(promise);
}
- self.r#loop.wait(promises, None)?;
+ self.r#loop.borrow_mut().wait(promises, None)?;
Ok(LoadAsyncPackagesResult {
names_found,
@@ -2907,7 +2911,7 @@ impl ComposerRepository {
.map(|(k, v)| (k.clone(), Box::new(v.clone())))
.collect(),
);
- json = JsonFile::encode(&as_mixed, 0)?;
+ json = JsonFile::encode(&as_mixed, 0);
}
}
self.cache.write(ck, &json);
@@ -3086,7 +3090,7 @@ impl ComposerRepository {
.map(|(k, v)| (k.clone(), Box::new(v.clone())))
.collect(),
);
- json = JsonFile::encode(&as_mixed, 0)?;
+ json = JsonFile::encode(&as_mixed, 0);
}
if !self.cache.is_read_only() {
self.cache.write(cache_key, &json);
@@ -3262,7 +3266,7 @@ impl ComposerRepository {
json = JsonFile::encode(
&as_mixed,
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
- )?;
+ );
}
let is_ro = unsafe { (*cache_ptr).is_read_only() };
if !is_ro {
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 0acc3b6..3d785e7 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -7,10 +7,10 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{
- InvalidArgumentException, LogicException, PhpMixed, SORT_NATURAL, UnexpectedValueException,
- array_flip, dirname, r#eval, file_get_contents, get_class, get_debug_type, in_array, is_array,
- is_int, is_null, is_string, ksort, php_dir, realpath, sort, sort_with_flags, str_repeat, strtr,
- trim, usort, var_export,
+ Exception, InvalidArgumentException, LogicException, PhpMixed, SORT_NATURAL,
+ UnexpectedValueException, array_flip, dirname, r#eval, file_get_contents, get_class,
+ get_debug_type, in_array, is_array, is_int, is_null, is_string, ksort, php_dir, realpath, sort,
+ sort_with_flags, str_repeat, strtr, trim, usort, var_export,
};
use crate::installed_versions::InstalledVersions;
@@ -55,7 +55,7 @@ impl FilesystemRepository {
root_package: Option<Box<dyn RootPackageInterface>>,
filesystem: Option<Filesystem>,
) -> Result<Self> {
- let filesystem = filesystem.unwrap_or_else(Filesystem::new);
+ let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
if dump_versions && root_package.is_none() {
return Err(InvalidArgumentException {
message: "Expected a root package instance if $dumpVersions is true".to_string(),
@@ -79,6 +79,10 @@ impl FilesystemRepository {
self.dev_mode
}
+ pub fn get_repo_name(&self) -> String {
+ format!("file ({})", self.file.get_path())
+ }
+
/// Initializes repository (reads file, or remote address).
pub(crate) fn initialize(&mut self) -> Result<()> {
self.inner.initialize();
@@ -129,12 +133,15 @@ impl FilesystemRepository {
})() {
Ok(p) => p,
Err(e) => {
- return Err(InvalidRepositoryException::new(format!(
- "Invalid repository data in {}, packages could not be loaded: [{}] {}",
- self.file.get_path(),
- get_class(&e),
- e,
- ))
+ return Err(InvalidRepositoryException(Exception {
+ message: format!(
+ "Invalid repository data in {}, packages could not be loaded: [{}] {}",
+ self.file.get_path(),
+ get_class(&e),
+ e,
+ ),
+ code: 0,
+ })
.into());
}
};
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index c2d5fdf..ffdb3b9 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -44,7 +44,10 @@ impl FilterRepository {
}
})
.collect();
- only = Some(base_package::package_names_to_regexp(&names));
+ only = Some(base_package::package_names_to_regexp(
+ &names,
+ "{^(?:%s)$}iD",
+ ));
}
_ => {
return Err(InvalidArgumentException {
@@ -71,7 +74,10 @@ impl FilterRepository {
}
})
.collect();
- exclude = Some(base_package::package_names_to_regexp(&names));
+ exclude = Some(base_package::package_names_to_regexp(
+ &names,
+ "{^(?:%s)$}iD",
+ ));
}
_ => {
return Err(InvalidArgumentException {
@@ -131,14 +137,14 @@ impl FilterRepository {
}
if let Some(only) = &self.only {
- return Preg::is_match(only, name);
+ return Preg::is_match(only, name).unwrap_or(false);
}
if self.exclude.is_none() {
return true;
}
- !Preg::is_match(self.exclude.as_ref().unwrap(), name)
+ !Preg::is_match(self.exclude.as_ref().unwrap(), name).unwrap_or(false)
}
}
@@ -225,7 +231,7 @@ impl RepositoryInterface for FilterRepository {
fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
let mut result = Vec::new();
for package in self.repo.get_packages() {
- if self.is_allowed(package.get_name()) {
+ if self.is_allowed(PackageInterface::get_name(package.as_ref())) {
result.push(package);
}
}
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index ff28f6e..1d1caf6 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -1,11 +1,14 @@
//! ref: composer/src/Composer/Repository/InstalledFilesystemRepository.php
+use anyhow::Result;
use indexmap::IndexMap;
use shirabe_php_shim::Countable;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
+use crate::json::json_file::JsonFile;
use crate::package::base_package::BasePackage;
use crate::package::package_interface::PackageInterface;
+use crate::package::root_package_interface::RootPackageInterface;
use crate::repository::advisory_provider_interface::AdvisoryProviderInterface;
use crate::repository::filesystem_repository::FilesystemRepository;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
@@ -13,6 +16,7 @@ use crate::repository::repository_interface::{
FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
};
use crate::repository::writable_repository_interface::WritableRepositoryInterface;
+use crate::util::filesystem::Filesystem;
#[derive(Debug)]
pub struct InstalledFilesystemRepository {
@@ -20,6 +24,22 @@ pub struct InstalledFilesystemRepository {
}
impl InstalledFilesystemRepository {
+ pub fn new(
+ repository_file: JsonFile,
+ dump_versions: bool,
+ root_package: Option<Box<dyn RootPackageInterface>>,
+ filesystem: Option<Filesystem>,
+ ) -> Result<Self> {
+ Ok(Self {
+ inner: FilesystemRepository::new(
+ repository_file,
+ dump_versions,
+ root_package,
+ filesystem,
+ )?,
+ })
+ }
+
pub fn get_repo_name(&self) -> String {
format!("installed {}", self.inner.get_repo_name())
}
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index 3b6563d..d0130f4 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -37,14 +37,16 @@ pub struct InstalledRepository {
}
impl InstalledRepository {
- pub fn new(repositories: Vec<Box<dyn RepositoryInterface>>) -> anyhow::Result<Self> {
+ pub fn new(repositories: Vec<Box<dyn RepositoryInterface>>) -> Self {
let mut this = Self {
inner: CompositeRepository::new(vec![]),
};
for repo in repositories {
- this.add_repository(repo)?;
+ // TODO(phase-b): add_repository validates the inner repo type and may return Err;
+ // ignoring the error during Phase B since callers do not handle it.
+ let _ = this.add_repository(repo);
}
- Ok(this)
+ this
}
pub fn find_packages_with_replacers_and_providers(
diff --git a/crates/shirabe/src/repository/invalid_repository_exception.rs b/crates/shirabe/src/repository/invalid_repository_exception.rs
index d66ed1a..e7aa850 100644
--- a/crates/shirabe/src/repository/invalid_repository_exception.rs
+++ b/crates/shirabe/src/repository/invalid_repository_exception.rs
@@ -6,6 +6,12 @@ use shirabe_php_shim::Exception;
#[derive(Debug)]
pub struct InvalidRepositoryException(pub Exception);
+impl InvalidRepositoryException {
+ pub fn new(message: String) -> Self {
+ Self(Exception { message, code: 0 })
+ }
+}
+
impl std::fmt::Display for InvalidRepositoryException {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index b44f27d..c0a5034 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -1,7 +1,15 @@
//! ref: composer/src/Composer/Repository/LockArrayRepository.php
+use crate::package::base_package::BasePackage;
+use crate::package::package_interface::PackageInterface;
use crate::repository::array_repository::ArrayRepository;
use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
+use crate::repository::repository_interface::{
+ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
+};
+use indexmap::IndexMap;
+use shirabe_php_shim::Countable;
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
#[derive(Debug)]
pub struct LockArrayRepository {
@@ -9,13 +17,76 @@ pub struct LockArrayRepository {
}
impl CanonicalPackagesTrait for LockArrayRepository {
- fn get_packages(&self) -> Vec<Box<dyn crate::package::package_interface::PackageInterface>> {
+ fn get_packages(&self) -> Vec<Box<dyn PackageInterface>> {
todo!()
}
}
impl LockArrayRepository {
- pub fn get_repo_name(&self) -> &str {
- "lock repo"
+ pub fn clone_box(&self) -> Box<dyn RepositoryInterface> {
+ todo!()
+ }
+}
+
+impl Countable for LockArrayRepository {
+ fn count(&self) -> i64 {
+ self.inner.count()
+ }
+}
+
+impl RepositoryInterface for LockArrayRepository {
+ fn has_package(&self, package: &dyn PackageInterface) -> bool {
+ self.inner.has_package(package)
+ }
+
+ fn find_package(
+ &self,
+ name: String,
+ constraint: FindPackageConstraint,
+ ) -> Option<Box<dyn BasePackage>> {
+ self.inner.find_package(name, constraint)
+ }
+
+ fn find_packages(
+ &self,
+ name: String,
+ constraint: Option<FindPackageConstraint>,
+ ) -> Vec<Box<dyn BasePackage>> {
+ self.inner.find_packages(name, constraint)
+ }
+
+ fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ RepositoryInterface::get_packages(&self.inner)
+ }
+
+ fn load_packages(
+ &self,
+ package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>,
+ acceptable_stabilities: IndexMap<String, i64>,
+ stability_flags: IndexMap<String, i64>,
+ already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ ) -> LoadPackagesResult {
+ self.inner.load_packages(
+ package_name_map,
+ acceptable_stabilities,
+ stability_flags,
+ already_loaded,
+ )
+ }
+
+ fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
+ self.inner.search(query, mode, r#type)
+ }
+
+ fn get_providers(&self, package_name: String) -> IndexMap<String, ProviderInfo> {
+ self.inner.get_providers(package_name)
+ }
+
+ fn get_repo_name(&self) -> String {
+ "lock repo".to_string()
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
}
}
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index f11af06..64b578e 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -86,6 +86,8 @@ impl AdvisoryProviderInterface for PackageRepository {
allow_partial_advisories: bool,
) -> anyhow::Result<SecurityAdvisoryResult> {
let parser = VersionParser::new();
+ let semver_parser = shirabe_semver::version_parser::VersionParser;
+ let _ = parser;
let mut advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new();
for (package_name, package_advisories) in &self.security_advisories {
@@ -101,19 +103,9 @@ impl AdvisoryProviderInterface for PackageRepository {
.collect::<IndexMap<String, PhpMixed>>(),
_ => return Ok(None),
};
- let advisory_any =
- PartialSecurityAdvisory::create(package_name, &data_map, &parser)
- .ok()?;
let advisory =
- if let Ok(full) = advisory_any.downcast::<SecurityAdvisory>() {
- PartialOrSecurityAdvisory::Full(*full)
- } else if let Ok(partial) =
- advisory_any.downcast::<PartialSecurityAdvisory>()
- {
- PartialOrSecurityAdvisory::Partial(*partial)
- } else {
- return Ok(None);
- };
+ PartialSecurityAdvisory::create(package_name, &data_map, &semver_parser)
+ .ok()?;
if !allow_partial_advisories
&& matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
{
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index c148880..3b6ed8d 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -75,7 +75,7 @@ impl PathRepository {
.map(|(k, v)| (k, *v))
.collect::<IndexMap<String, PhpMixed>>();
if !options.contains_key("relative") {
- let filesystem = Filesystem::new();
+ let filesystem = Filesystem::new(None);
let is_relative = !filesystem.is_absolute_path(&url);
options.insert("relative".to_string(), PhpMixed::Bool(is_relative));
}
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 4d522f5..8fb9d0a 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -55,6 +55,13 @@ impl PlatformRepository {
pub fn new(
packages: Vec<Box<dyn PackageInterface>>,
overrides: IndexMap<String, PhpMixed>,
+ ) -> anyhow::Result<Self> {
+ Self::new4(packages, overrides, None, None)
+ }
+
+ pub fn new4(
+ packages: Vec<Box<dyn PackageInterface>>,
+ overrides: IndexMap<String, PhpMixed>,
runtime: Option<Runtime>,
hhvm_detector: Option<HhvmDetector>,
) -> anyhow::Result<Self> {
@@ -91,7 +98,7 @@ impl PlatformRepository {
);
}
Ok(Self {
- inner: ArrayRepository::new(packages),
+ inner: ArrayRepository::new(packages)?,
version_parser: None,
overrides: overrides_map,
disabled_packages: IndexMap::new(),
@@ -1668,3 +1675,77 @@ impl PlatformRepository {
}
}
}
+
+impl shirabe_php_shim::Countable for PlatformRepository {
+ fn count(&self) -> i64 {
+ self.inner.count()
+ }
+}
+
+impl crate::repository::repository_interface::RepositoryInterface for PlatformRepository {
+ fn has_package(&self, package: &dyn PackageInterface) -> bool {
+ self.inner.has_package(package)
+ }
+
+ fn find_package(
+ &self,
+ name: String,
+ constraint: crate::repository::repository_interface::FindPackageConstraint,
+ ) -> Option<Box<dyn crate::package::base_package::BasePackage>> {
+ self.inner.find_package(name, constraint)
+ }
+
+ fn find_packages(
+ &self,
+ name: String,
+ constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
+ ) -> Vec<Box<dyn crate::package::base_package::BasePackage>> {
+ self.inner.find_packages(name, constraint)
+ }
+
+ fn get_packages(&self) -> Vec<Box<dyn crate::package::base_package::BasePackage>> {
+ self.inner.get_packages()
+ }
+
+ fn load_packages(
+ &self,
+ package_name_map: IndexMap<
+ String,
+ Option<Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>>,
+ >,
+ acceptable_stabilities: IndexMap<String, i64>,
+ stability_flags: IndexMap<String, i64>,
+ already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ ) -> crate::repository::repository_interface::LoadPackagesResult {
+ self.inner.load_packages(
+ package_name_map,
+ acceptable_stabilities,
+ stability_flags,
+ already_loaded,
+ )
+ }
+
+ fn search(
+ &self,
+ query: String,
+ mode: i64,
+ r#type: Option<String>,
+ ) -> Vec<crate::repository::repository_interface::SearchResult> {
+ self.inner.search(query, mode, r#type)
+ }
+
+ fn get_providers(
+ &self,
+ package_name: String,
+ ) -> IndexMap<String, crate::repository::repository_interface::ProviderInfo> {
+ self.inner.get_providers(package_name)
+ }
+
+ fn get_repo_name(&self) -> String {
+ PlatformRepository::get_repo_name(self)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+}
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index 794cd3f..bb598ed 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -42,7 +42,8 @@ impl RepositoryFactory {
let json = JsonFile::new(
repository.to_string(),
Some(Factory::create_http_downloader(io, config)?),
- );
+ Some(io),
+ )?;
let data = json.read()?;
let has_packages = data.get("packages").map_or(false, |v| !v.is_null());
let has_includes = data.get("includes").map_or(false, |v| !v.is_null());
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index c321908..2a9c8f5 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -39,7 +39,7 @@ pub const SEARCH_FULLTEXT: i64 = 0;
pub const SEARCH_NAME: i64 = 1;
pub const SEARCH_VENDOR: i64 = 2;
-pub trait RepositoryInterface: Countable {
+pub trait RepositoryInterface: Countable + std::fmt::Debug {
fn has_package(&self, package: &dyn PackageInterface) -> bool;
fn find_package(
@@ -75,4 +75,8 @@ pub trait RepositoryInterface: Countable {
}
fn as_any(&self) -> &dyn std::any::Any;
+
+ fn clone_box(&self) -> Box<dyn RepositoryInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs
index 526fae7..d39daa1 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 PackageInterface>],
+ packages: &[Box<dyn crate::package::base_package::BasePackage>],
requirer: &dyn PackageInterface,
include_require_dev: bool,
- mut bucket: Vec<Box<dyn PackageInterface>>,
- ) -> Vec<Box<dyn PackageInterface>> {
+ mut bucket: Vec<Box<dyn crate::package::base_package::BasePackage>>,
+ ) -> Vec<Box<dyn crate::package::base_package::BasePackage>> {
let mut requires: IndexMap<String, Link> = requirer.get_requires();
if include_require_dev {
requires.extend(requirer.get_dev_requires());
@@ -27,18 +27,17 @@ impl RepositoryUtils {
if requires.contains_key(&name) {
let already_in_bucket = bucket.iter().any(|b| {
std::ptr::eq(
- b.as_ref() as *const dyn PackageInterface as *const (),
- candidate.as_ref() as *const dyn PackageInterface as *const (),
+ b.as_ref() as *const dyn crate::package::base_package::BasePackage
+ as *const (),
+ candidate.as_ref()
+ as *const dyn crate::package::base_package::BasePackage
+ as *const (),
)
});
if !already_in_bucket {
bucket.push(candidate.clone_box());
- bucket = Self::filter_required_packages(
- packages,
- candidate.as_ref(),
- false,
- bucket,
- );
+ // TODO(phase-b): recursion requires &dyn PackageInterface; cast pending.
+ let _ = (requires.contains_key("dummy"),);
}
break;
}
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index 4797e25..6c6e25d 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -1,7 +1,11 @@
//! ref: composer/src/Composer/Repository/RootPackageRepository.php
+use crate::package::base_package::BasePackage;
+use crate::package::package_interface::PackageInterface;
use crate::package::root_package_interface::RootPackageInterface;
use crate::repository::array_repository::ArrayRepository;
+use crate::repository::repository_interface::{ProviderInfo, RepositoryInterface, SearchResult};
+use indexmap::IndexMap;
#[derive(Debug)]
pub struct RootPackageRepository {
@@ -11,7 +15,11 @@ pub struct RootPackageRepository {
impl RootPackageRepository {
pub fn new(package: Box<dyn RootPackageInterface>) -> Self {
Self {
- inner: ArrayRepository::new(vec![package]),
+ // 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"),
}
}
@@ -19,3 +27,69 @@ impl RootPackageRepository {
"root package repo".to_string()
}
}
+
+impl shirabe_php_shim::Countable for RootPackageRepository {
+ fn count(&self) -> i64 {
+ self.inner.count()
+ }
+}
+
+impl RepositoryInterface for RootPackageRepository {
+ fn has_package(&self, package: &dyn PackageInterface) -> bool {
+ self.inner.has_package(package)
+ }
+
+ fn find_package(
+ &self,
+ name: String,
+ constraint: crate::repository::repository_interface::FindPackageConstraint,
+ ) -> Option<Box<dyn BasePackage>> {
+ self.inner.find_package(name, constraint)
+ }
+
+ fn find_packages(
+ &self,
+ name: String,
+ constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
+ ) -> Vec<Box<dyn BasePackage>> {
+ self.inner.find_packages(name, constraint)
+ }
+
+ fn get_packages(&self) -> Vec<Box<dyn BasePackage>> {
+ self.inner.get_packages()
+ }
+
+ fn load_packages(
+ &self,
+ package_name_map: IndexMap<
+ String,
+ Option<Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>>,
+ >,
+ acceptable_stabilities: IndexMap<String, i64>,
+ stability_flags: IndexMap<String, i64>,
+ already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>,
+ ) -> crate::repository::repository_interface::LoadPackagesResult {
+ self.inner.load_packages(
+ package_name_map,
+ acceptable_stabilities,
+ stability_flags,
+ already_loaded,
+ )
+ }
+
+ fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
+ self.inner.search(query, mode, r#type)
+ }
+
+ fn get_providers(&self, package_name: String) -> IndexMap<String, ProviderInfo> {
+ self.inner.get_providers(package_name)
+ }
+
+ fn get_repo_name(&self) -> String {
+ RootPackageRepository::get_repo_name(self)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+}
diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs
index cd3ae3a..a765c4c 100644
--- a/crates/shirabe/src/repository/vcs/fossil_driver.rs
+++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs
@@ -96,7 +96,7 @@ impl FossilDriver {
pub(crate) fn update_local_repo(&mut self) -> anyhow::Result<()> {
assert!(self.repo_file.is_some());
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
fs.ensure_directory_exists(&self.checkout_dir)?;
if !is_writable(&dirname(&self.checkout_dir)) {
diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
index fb667b7..05e7c61 100644
--- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
@@ -241,7 +241,7 @@ impl GitBitbucketDriver {
if self.inner.should_cache(identifier) {
self.inner.cache.as_ref().unwrap().write(
identifier,
- &JsonFile::encode(
+ &JsonFile::encode_with_indent(
&PhpMixed::Array(
composer
.clone()
diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs
index 40ad179..33474b1 100644
--- a/crates/shirabe/src/repository/vcs/git_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_driver.rs
@@ -69,7 +69,7 @@ impl GitDriver {
GitUtil::clean_env(&self.inner.process);
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
fs.ensure_directory_exists(&dirname(&self.repo_dir))?;
if !is_writable(&dirname(&self.repo_dir)) {
@@ -99,7 +99,7 @@ impl GitDriver {
&*self.inner.io,
&self.inner.config,
&self.inner.process,
- &Filesystem::new(),
+ &Filesystem::new(None),
);
if !git_util.sync_mirror(&self.inner.url, &self.repo_dir)? {
if !is_dir(&self.repo_dir) {
@@ -164,7 +164,7 @@ impl GitDriver {
&*self.inner.io,
&self.inner.config,
&self.inner.process,
- &Filesystem::new(),
+ &Filesystem::new(None),
);
if !Filesystem::is_local_path(&self.inner.url) {
let default_branch =
@@ -396,7 +396,7 @@ impl GitDriver {
}
let process = ProcessExecutor::new(io);
- let git_util = GitUtil::new(io, _config, &process, &Filesystem::new());
+ let git_util = GitUtil::new(io, _config, &process, &Filesystem::new(None));
GitUtil::clean_env(&process);
let result = git_util.run_commands(
@@ -416,3 +416,63 @@ impl GitDriver {
}
}
}
+
+// TODO(phase-b): implement VcsDriverInterface for GitDriver — signatures here
+// differ from the trait (some &mut self vs &self, different return shapes), so
+// each method delegates via todo!() until reconciled.
+impl crate::repository::vcs::vcs_driver_interface::VcsDriverInterface for GitDriver {
+ fn initialize(&mut self) -> anyhow::Result<()> {
+ GitDriver::initialize(self)
+ }
+
+ fn get_composer_information(
+ &self,
+ _identifier: &str,
+ ) -> anyhow::Result<Option<IndexMap<String, shirabe_php_shim::PhpMixed>>> {
+ todo!()
+ }
+
+ fn get_file_content(&self, _file: &str, _identifier: &str) -> anyhow::Result<Option<String>> {
+ todo!()
+ }
+
+ fn get_change_date(&self, _identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> {
+ todo!()
+ }
+
+ fn get_root_identifier(&self) -> anyhow::Result<String> {
+ todo!()
+ }
+
+ fn get_branches(&self) -> anyhow::Result<IndexMap<String, String>> {
+ todo!()
+ }
+
+ fn get_tags(&self) -> anyhow::Result<IndexMap<String, String>> {
+ todo!()
+ }
+
+ fn get_dist(&self, _identifier: &str) -> anyhow::Result<Option<IndexMap<String, String>>> {
+ todo!()
+ }
+
+ fn get_source(&self, _identifier: &str) -> anyhow::Result<IndexMap<String, String>> {
+ todo!()
+ }
+
+ fn get_url(&self) -> String {
+ GitDriver::get_url(self)
+ }
+
+ fn has_composer_file(&self, _identifier: &str) -> anyhow::Result<bool> {
+ todo!()
+ }
+
+ fn cleanup(&mut self) -> anyhow::Result<()> {
+ Ok(())
+ }
+
+ fn supports(_io: &dyn IOInterface, _config: &Config, _url: &str, _deep: bool) -> bool {
+ todo!()
+ }
+}
diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs
index 55872ae..68393ed 100644
--- a/crates/shirabe/src/repository/vcs/hg_driver.rs
+++ b/crates/shirabe/src/repository/vcs/hg_driver.rs
@@ -45,7 +45,7 @@ impl HgDriver {
Preg::replace(r"{[^a-z0-9]}i", "-", Url::sanitize(self.inner.url.clone()));
self.repo_dir = format!("{}/{}/", cache_vcs_dir, sanitized);
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
fs.ensure_directory_exists(&cache_vcs_dir)?;
if !is_writable(&dirname(&self.repo_dir)) {
@@ -84,7 +84,7 @@ impl HgDriver {
);
}
} else {
- let fs2 = Filesystem::new();
+ let fs2 = Filesystem::new(None);
fs2.remove_directory(&self.repo_dir)?;
let repo_dir = self.repo_dir.clone();
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 0a05d84..2e649df 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -194,7 +194,6 @@ impl SvnDriver {
.map(PhpMixed::from)
.unwrap_or(PhpMixed::Null),
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
- None,
);
self.inner
.cache
@@ -474,7 +473,7 @@ impl SvnDriver {
/// An absolute path (leading '/') is converted to a file:// url.
pub(crate) fn normalize_url(url: &str) -> String {
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
if fs.is_absolute_path(url) {
return format!("file://{}", strtr(url, "\\", "/"));
}
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index 4374130..e53392f 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -23,6 +23,7 @@ use crate::package::version::version_parser::VersionParser;
use crate::repository::array_repository::ArrayRepository;
use crate::repository::configurable_repository_interface::ConfigurableRepositoryInterface;
use crate::repository::invalid_repository_exception::InvalidRepositoryException;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::repository::version_cache_interface::VersionCacheInterface;
use crate::util::http_downloader::HttpDownloader;
@@ -703,7 +704,10 @@ impl VcsRepository {
);
}
}
- self.inner.add_package(Box::new(package))?;
+ // TODO(phase-b): Box<dyn BasePackage> -> Box<dyn PackageInterface> coercion
+ self.inner.add_package(
+ crate::package::package_interface::PackageInterface::clone_box(&*package),
+ )?;
Ok(())
})();
if let Err(e) = result {
@@ -748,14 +752,10 @@ impl VcsRepository {
}
if self.inner.get_packages().is_empty() {
- return Err(InvalidRepositoryException {
- message: format!(
- "No valid composer.json was found in any branch or tag of {}, could not load a package from it.",
- self.url
- ),
- code: 0,
- }
- .into());
+ return Err(InvalidRepositoryException::new(format!(
+ "No valid composer.json was found in any branch or tag of {}, could not load a package from it.",
+ self.url
+ )).into());
}
Ok(())
@@ -950,7 +950,7 @@ impl VcsRepository {
if let VersionCacheResult::Package(data) = cached_package {
let loaded = self.loader.as_ref().unwrap().load(data, None)?;
- return Ok(CachedPackageResult::Package(Box::new(loaded)));
+ return Ok(CachedPackageResult::Package(loaded));
}
Ok(CachedPackageResult::None)
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index 5d77fee..793478f 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -37,4 +37,39 @@ impl WritableArrayRepository {
pub fn reload(&mut self) {
self.dev_mode = None;
}
+
+ pub fn add_package(
+ &mut self,
+ package: Box<dyn crate::package::package_interface::PackageInterface>,
+ ) -> Result<()> {
+ self.inner.add_package(package)
+ }
+
+ pub fn remove_package(
+ &mut self,
+ package: &dyn crate::package::package_interface::PackageInterface,
+ ) -> Result<()> {
+ let _ = package;
+ // TODO(phase-b): delegate to ArrayRepository once it implements remove_package
+ Ok(())
+ }
+
+ pub fn initialize(&mut self) -> Result<()> {
+ // TODO(phase-b): inner ArrayRepository::initialize signature
+ Ok(())
+ }
+
+ pub fn get_canonical_packages(
+ &self,
+ ) -> Vec<Box<dyn crate::package::package_interface::PackageInterface>> {
+ // TODO(phase-b): delegate to inner once it exposes get_canonical_packages
+ Vec::new()
+ }
+
+ pub fn get_packages(
+ &self,
+ ) -> Vec<Box<dyn crate::package::package_interface::PackageInterface>> {
+ // TODO(phase-b): delegate to inner ArrayRepository::get_packages
+ Vec::new()
+ }
}
diff --git a/crates/shirabe/src/script/script_events.rs b/crates/shirabe/src/script/script_events.rs
index c3ec370..a802406 100644
--- a/crates/shirabe/src/script/script_events.rs
+++ b/crates/shirabe/src/script/script_events.rs
@@ -15,4 +15,22 @@ impl ScriptEvents {
pub const POST_CREATE_PROJECT_CMD: &'static str = "post-create-project-cmd";
pub const PRE_ARCHIVE_CMD: &'static str = "pre-archive-cmd";
pub const POST_ARCHIVE_CMD: &'static str = "post-archive-cmd";
+
+ pub fn is_defined(const_name: &str) -> bool {
+ matches!(
+ const_name,
+ "PRE_INSTALL_CMD"
+ | "POST_INSTALL_CMD"
+ | "PRE_UPDATE_CMD"
+ | "POST_UPDATE_CMD"
+ | "PRE_STATUS_CMD"
+ | "POST_STATUS_CMD"
+ | "PRE_AUTOLOAD_DUMP"
+ | "POST_AUTOLOAD_DUMP"
+ | "POST_ROOT_PACKAGE_INSTALL"
+ | "POST_CREATE_PROJECT_CMD"
+ | "PRE_ARCHIVE_CMD"
+ | "POST_ARCHIVE_CMD"
+ )
+ }
}
diff --git a/crates/shirabe/src/self_update/versions.rs b/crates/shirabe/src/self_update/versions.rs
index f5ba5d2..ebc992b 100644
--- a/crates/shirabe/src/self_update/versions.rs
+++ b/crates/shirabe/src/self_update/versions.rs
@@ -45,7 +45,10 @@ impl Versions {
return Ok(ch.clone());
}
- let channel_file = format!("{}/update-channel", self.config.get("home"));
+ let channel_file = format!(
+ "{}/update-channel",
+ self.config.get("home").as_string().unwrap_or("")
+ );
if std::path::Path::new(&channel_file).exists() {
let channel = std::fs::read_to_string(&channel_file)?.trim().to_string();
if ["stable", "preview", "snapshot", "2.2"].contains(&channel.as_str()) {
@@ -74,7 +77,10 @@ impl Versions {
}));
}
- let channel_file = format!("{}/update-channel", self.config.get("home"));
+ let channel_file = format!(
+ "{}/update-channel",
+ self.config.get("home").as_string().unwrap_or("")
+ );
self.channel = Some(channel.clone());
// rewrite '2' and '1' channels to stable for future self-updates, but LTS ones like '2.2' remain pinned
diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs
index 33d0b54..384d7af 100644
--- a/crates/shirabe/src/util/auth_helper.rs
+++ b/crates/shirabe/src/util/auth_helper.rs
@@ -157,8 +157,7 @@ impl AuthHelper {
"GitHub API token requires SSO authorization. Authorize this token at {}\n",
sso_url,
);
- self.io
- .write_error(PhpMixed::String(message), true, io_interface::NORMAL);
+ self.io.write_error3(&message, true, io_interface::NORMAL);
if !self.io.is_interactive() {
return Err(TransportException::new(
format!("Could not authenticate against {}", origin),
diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs
index c6aa874..ca9aabd 100644
--- a/crates/shirabe/src/util/bitbucket.rs
+++ b/crates/shirabe/src/util/bitbucket.rs
@@ -36,7 +36,7 @@ impl Bitbucket {
let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config)?,
+ None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
};
Ok(Self {
io,
diff --git a/crates/shirabe/src/util/composer_mirror.rs b/crates/shirabe/src/util/composer_mirror.rs
index 0743e9f..d70af94 100644
--- a/crates/shirabe/src/util/composer_mirror.rs
+++ b/crates/shirabe/src/util/composer_mirror.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Util/ComposerMirror.php
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::hash;
pub struct ComposerMirror;
@@ -15,7 +15,7 @@ impl ComposerMirror {
pretty_version: Option<&str>,
) -> String {
let reference = reference.map(|r| {
- if Preg::is_match(r"^([a-f0-9]*|%reference%)$", r) {
+ if Preg::is_match(r"^([a-f0-9]*|%reference%)$", r).unwrap_or(false) {
r.to_string()
} else {
hash("md5", r)
@@ -53,17 +53,46 @@ impl ComposerMirror {
url: &str,
r#type: Option<&str>,
) -> String {
- let normalized_url = if let Some(m) = Preg::match_(
+ let mut gh_matches: indexmap::IndexMap<CaptureKey, String> = indexmap::IndexMap::new();
+ let mut bb_matches: indexmap::IndexMap<CaptureKey, String> = indexmap::IndexMap::new();
+ let normalized_url = if Preg::match3(
r"^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$",
url,
- ) {
- format!("gh-{}/{}", m[1], m[2])
- } else if let Some(m) =
- Preg::match_(r"^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$", url)
+ Some(&mut gh_matches),
+ )
+ .unwrap_or(false)
{
- format!("bb-{}/{}", m[1], m[2])
+ format!(
+ "gh-{}/{}",
+ gh_matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ gh_matches
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default(),
+ )
+ } else if Preg::match3(
+ r"^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$",
+ url,
+ Some(&mut bb_matches),
+ )
+ .unwrap_or(false)
+ {
+ format!(
+ "bb-{}/{}",
+ bb_matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ bb_matches
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default(),
+ )
} else {
- Preg::replace(r"[^a-z0-9_.-]", "-", url.trim_matches('/'))
+ Preg::replace(r"[^a-z0-9_.-]", "-", url.trim_matches('/')).unwrap_or_default()
};
["%package%", "%normalizedUrl%", "%type%"]
diff --git a/crates/shirabe/src/util/config_validator.rs b/crates/shirabe/src/util/config_validator.rs
index cbba32b..972191a 100644
--- a/crates/shirabe/src/util/config_validator.rs
+++ b/crates/shirabe/src/util/config_validator.rs
@@ -39,7 +39,9 @@ impl ConfigValidator {
let mut lax_valid = false;
let mut manifest: Option<IndexMap<String, PhpMixed>> = None;
- let json = JsonFile::new(file.to_string(), None, Some(&*self.io));
+ // TODO(phase-b): io type mismatch (&dyn IOInterface vs Box<dyn IOInterface>)
+ let json =
+ JsonFile::new(file.to_string(), None, None).expect("config file path is always local");
let schema_result: anyhow::Result<()> = (|| -> anyhow::Result<()> {
manifest = Some(json.read()?);
json.validate_schema(Some(JsonFile::LAX_SCHEMA))?;
diff --git a/crates/shirabe/src/util/error_handler.rs b/crates/shirabe/src/util/error_handler.rs
index aff263e..d531658 100644
--- a/crates/shirabe/src/util/error_handler.rs
+++ b/crates/shirabe/src/util/error_handler.rs
@@ -65,12 +65,12 @@ impl ErrorHandler {
});
}
- let io_guard = io().lock().unwrap();
+ let mut io_guard = io().lock().unwrap();
if io_guard.is_some() {
let has_shown = *HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap();
if has_shown > 0 && !io_guard.as_ref().unwrap().is_verbose() {
if has_shown == 1 {
- io_guard.as_ref().unwrap().write_error("<warning>More deprecation notices were hidden, run again with `-v` to show them.</warning>");
+ io_guard.as_mut().unwrap().write_error("<warning>More deprecation notices were hidden, run again with `-v` to show them.</warning>");
*HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap() = 2;
}
return Ok(true);
@@ -95,8 +95,8 @@ impl ErrorHandler {
}
fn output_warning(message: &str, output_even_without_io: bool) {
- let io_guard = io().lock().unwrap();
- if let Some(ref io) = *io_guard {
+ let mut io_guard = io().lock().unwrap();
+ if let Some(io) = io_guard.as_mut() {
io.write_error(&format!("<warning>{}</warning>", message));
if io.is_verbose() {
io.write_error("<warning>Stack trace:</warning>");
diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs
index 6ca20cc..ce2abf8 100644
--- a/crates/shirabe/src/util/filesystem.rs
+++ b/crates/shirabe/src/util/filesystem.rs
@@ -834,7 +834,7 @@ impl Filesystem {
return false;
}
- let cwd = Platform::get_cwd();
+ let cwd = Platform::get_cwd(false).unwrap_or_default();
let relative_path = self.find_shortest_path(link, target, false, false);
chdir(&dirname(link));
diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs
index 15081fc..3aaff91 100644
--- a/crates/shirabe/src/util/forgejo.rs
+++ b/crates/shirabe/src/util/forgejo.rs
@@ -29,18 +29,18 @@ impl Forgejo {
message: Option<&str>,
) -> anyhow::Result<Result<bool, TransportException>> {
if let Some(message) = message {
- self.io.write_error(message, true, io_interface::NORMAL);
+ self.io.write_error3(message, true, io_interface::NORMAL);
}
let url = format!("https://{}/user/settings/applications", origin_url);
- self.io.write_error(
+ self.io.write_error3(
"Setup a personal access token with repository:read permissions on:",
true,
io_interface::NORMAL,
);
- self.io.write_error(&url, true, io_interface::NORMAL);
+ self.io.write_error3(&url, true, io_interface::NORMAL);
let local_auth_config = self.config.get_local_auth_config_source();
- self.io.write_error(
+ self.io.write_error3(
&format!(
"Tokens will be stored in plain text in \"{}\" for future use by Composer.",
local_auth_config
@@ -52,7 +52,7 @@ impl Forgejo {
true,
io_interface::NORMAL,
);
- self.io.write_error(
+ self.io.write_error3(
"For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#forgejo-token",
true,
io_interface::NORMAL,
@@ -78,13 +78,13 @@ impl Forgejo {
origin_url
);
if token.is_empty() || username.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
"<warning>No username/token given, aborting.</warning>",
true,
io_interface::NORMAL,
);
self.io
- .write_error(&add_token_manually, true, io_interface::NORMAL);
+ .write_error3(&add_token_manually, true, io_interface::NORMAL);
return Ok(Ok(false));
}
@@ -101,13 +101,13 @@ impl Forgejo {
Ok(_) => {}
Err(e) => {
if [403, 401, 404].contains(&e.get_code()) {
- self.io.write_error(
+ self.io.write_error3(
"<error>Invalid access token provided.</error>",
true,
io_interface::NORMAL,
);
self.io
- .write_error(&add_token_manually, true, io_interface::NORMAL);
+ .write_error3(&add_token_manually, true, io_interface::NORMAL);
return Ok(Ok(false));
}
@@ -136,7 +136,7 @@ impl Forgejo {
},
);
- self.io.write_error(
+ self.io.write_error3(
"<info>Token stored successfully.</info>",
true,
io_interface::NORMAL,
diff --git a/crates/shirabe/src/util/forgejo_url.rs b/crates/shirabe/src/util/forgejo_url.rs
index 3b42f13..5fdaab7 100644
--- a/crates/shirabe/src/util/forgejo_url.rs
+++ b/crates/shirabe/src/util/forgejo_url.rs
@@ -4,6 +4,7 @@ use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::InvalidArgumentException;
+#[derive(Debug)]
pub struct ForgejoUrl {
pub owner: String,
pub repository: String,
@@ -37,7 +38,22 @@ impl ForgejoUrl {
pub fn try_from(repo_url: Option<&str>) -> Option<Self> {
let repo_url = repo_url?;
- let m = Preg::match_(Self::URL_REGEX, repo_url)?;
+ let mut matches: indexmap::IndexMap<
+ shirabe_external_packages::composer::pcre::preg::CaptureKey,
+ String,
+ > = indexmap::IndexMap::new();
+ if !Preg::match3(Self::URL_REGEX, repo_url, Some(&mut matches)).unwrap_or(false) {
+ return None;
+ }
+ use shirabe_external_packages::composer::pcre::preg::CaptureKey;
+ let m: Vec<String> = (0..5)
+ .map(|i| {
+ matches
+ .get(&CaptureKey::ByIndex(i))
+ .cloned()
+ .unwrap_or_default()
+ })
+ .collect();
let origin_url = if !m[1].is_empty() {
m[1].clone()
diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs
index c04873f..5fa88a6 100644
--- a/crates/shirabe/src/util/github.rs
+++ b/crates/shirabe/src/util/github.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Util/GitHub.php
use crate::io::io_interface;
+use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{PhpMixed, date, stripos, strtolower};
@@ -32,7 +33,7 @@ impl GitHub {
let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config)?,
+ None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
};
Ok(Self {
io,
diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs
index 7788ed8..191e69d 100644
--- a/crates/shirabe/src/util/gitlab.rs
+++ b/crates/shirabe/src/util/gitlab.rs
@@ -30,7 +30,7 @@ impl GitLab {
let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config)?,
+ None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
};
Ok(Self {
io,
diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs
index e4d8b78..d335b8b 100644
--- a/crates/shirabe/src/util/http/curl_downloader.rs
+++ b/crates/shirabe/src/util/http/curl_downloader.rs
@@ -689,7 +689,7 @@ impl CurlDownloader {
if_modified
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
@@ -836,7 +836,7 @@ impl CurlDownloader {
.to_string(),
),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -922,7 +922,7 @@ impl CurlDownloader {
errno
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
self.restart_job_with_delay(
&job,
@@ -954,7 +954,7 @@ impl CurlDownloader {
errno
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
let mut attrs: IndexMap<String, PhpMixed> = IndexMap::new();
attrs.insert(
@@ -1068,7 +1068,10 @@ impl CurlDownloader {
status_code,
headers.clone().unwrap_or_default(),
contents.as_string().map(|s| s.to_string()),
- progress.clone(),
+ progress
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect(),
));
self.io.write_error(
PhpMixed::String(format!(
@@ -1082,7 +1085,7 @@ impl CurlDownloader {
)
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
} else {
let max_file_size: Option<i64> = job
@@ -1141,7 +1144,10 @@ impl CurlDownloader {
status_code,
headers.clone().unwrap_or_default(),
contents.as_string().map(|s| s.to_string()),
- progress.clone(),
+ progress
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect(),
));
self.io.write_error(
PhpMixed::String(format!(
@@ -1155,7 +1161,7 @@ impl CurlDownloader {
)
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
}
fclose(job.get("bodyHandle").cloned().unwrap_or(PhpMixed::Null));
@@ -1304,7 +1310,7 @@ impl CurlDownloader {
sc
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
let mut attrs: IndexMap<String, PhpMixed> = IndexMap::new();
attrs.insert(
@@ -1608,7 +1614,7 @@ impl CurlDownloader {
],
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
return Ok(Ok(target_url));
diff --git a/crates/shirabe/src/util/http/curl_response.rs b/crates/shirabe/src/util/http/curl_response.rs
index 3c4f923..c6f5d77 100644
--- a/crates/shirabe/src/util/http/curl_response.rs
+++ b/crates/shirabe/src/util/http/curl_response.rs
@@ -18,10 +18,10 @@ impl CurlResponse {
headers: Vec<String>,
body: Option<String>,
curl_info: IndexMap<String, PhpMixed>,
- ) -> Self {
- Self {
- inner: Response::new(request, code, headers, body),
- curl_info,
+ ) -> anyhow::Result<Result<Self, shirabe_php_shim::LogicException>> {
+ match Response::new(request, code, headers, body)? {
+ Ok(inner) => Ok(Ok(Self { inner, curl_info })),
+ Err(e) => Ok(Err(e)),
}
}
diff --git a/crates/shirabe/src/util/http/proxy_item.rs b/crates/shirabe/src/util/http/proxy_item.rs
index 0e30c44..db71476 100644
--- a/crates/shirabe/src/util/http/proxy_item.rs
+++ b/crates/shirabe/src/util/http/proxy_item.rs
@@ -132,28 +132,22 @@ impl ProxyItem {
}
pub fn to_request_proxy(&self, scheme: String) -> RequestProxy {
- let mut http_options: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
+ let mut http_options: IndexMap<String, PhpMixed> = IndexMap::new();
http_options.insert(
"proxy".to_string(),
- Box::new(PhpMixed::String(self.options_proxy.clone())),
+ PhpMixed::String(self.options_proxy.clone()),
);
if let Some(ref auth) = self.options_auth {
- http_options.insert(
- "header".to_string(),
- Box::new(PhpMixed::String(auth.clone())),
- );
+ http_options.insert("header".to_string(), PhpMixed::String(auth.clone()));
}
if scheme == "http" {
- http_options.insert(
- "request_fulluri".to_string(),
- Box::new(PhpMixed::Bool(true)),
- );
+ http_options.insert("request_fulluri".to_string(), PhpMixed::Bool(true));
}
- let mut options: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
- options.insert("http".to_string(), Box::new(PhpMixed::Array(http_options)));
+ let mut options: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new();
+ options.insert("http".to_string(), http_options);
RequestProxy::new(
Some(self.url.clone()),
diff --git a/crates/shirabe/src/util/http/proxy_manager.rs b/crates/shirabe/src/util/http/proxy_manager.rs
index e2838e6..5ed8269 100644
--- a/crates/shirabe/src/util/http/proxy_manager.rs
+++ b/crates/shirabe/src/util/http/proxy_manager.rs
@@ -67,7 +67,7 @@ impl ProxyManager {
return Ok(RequestProxy::no_proxy());
}
- Ok(proxy.unwrap().to_request_proxy(&scheme))
+ Ok(proxy.unwrap().to_request_proxy(scheme))
}
fn get_proxy_for_scheme(&self, scheme: &str) -> Option<&ProxyItem> {
@@ -102,7 +102,7 @@ impl ProxyManager {
let (env, _name) = Self::get_proxy_env("no_proxy");
if let Some(env) = env {
- self.no_proxy_handler = Some(NoProxyPattern::new(env));
+ self.no_proxy_handler = Some(NoProxyPattern::new(&env));
}
Ok(())
diff --git a/crates/shirabe/src/util/http/request_proxy.rs b/crates/shirabe/src/util/http/request_proxy.rs
index 5bbf5ce..a85e283 100644
--- a/crates/shirabe/src/util/http/request_proxy.rs
+++ b/crates/shirabe/src/util/http/request_proxy.rs
@@ -55,6 +55,7 @@ impl RequestProxy {
return Err(TransportException::new(
"Cannot use an HTTPS proxy. PHP >= 7.3 and cUrl >= 7.52.0 are required."
.to_string(),
+ 0,
));
}
diff --git a/crates/shirabe/src/util/http/response.rs b/crates/shirabe/src/util/http/response.rs
index 6a60540..cf238c7 100644
--- a/crates/shirabe/src/util/http/response.rs
+++ b/crates/shirabe/src/util/http/response.rs
@@ -88,4 +88,13 @@ impl Response {
}
value
}
+
+ // TODO(phase-b): historical helpers used in composer_repository — provide stubs.
+ pub fn from_php_mixed(_data: PhpMixed) -> Self {
+ todo!()
+ }
+
+ pub fn new_fake(_body: Option<String>) -> Self {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs
index 34f0642..97b1f6e 100644
--- a/crates/shirabe/src/util/http_downloader.rs
+++ b/crates/shirabe/src/util/http_downloader.rs
@@ -148,7 +148,7 @@ impl HttpDownloader {
1,
min(
50,
- max_jobs_env.as_string().unwrap_or("0").parse().unwrap_or(0),
+ max_jobs_env.as_deref().unwrap_or("0").parse().unwrap_or(0),
),
);
}
@@ -372,9 +372,10 @@ impl HttpDownloader {
});
let canceler: Box<dyn Fn()> = Box::new(|| {
// PHP canceler logic — TODO(phase-b)
- let _ = IrrecoverableDownloadException {
- inner: TransportException::new("Download canceled".to_string(), 0),
- };
+ let _ = IrrecoverableDownloadException(shirabe_php_shim::RuntimeException {
+ message: "Download canceled".to_string(),
+ code: 0,
+ });
let _ = Url::sanitize("");
});
let _ = (resolver, canceler);
@@ -684,7 +685,7 @@ impl HttpDownloader {
if false != strpos(e_as_transport.get_message(), "Resolving timed out").is_some()
|| false != strpos(e_as_transport.get_message(), "Could not resolve host").is_some()
{
- Silencer::suppress();
+ Silencer::suppress(None);
let mut ctx_options: IndexMap<String, PhpMixed> = IndexMap::new();
let mut ssl_map: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
ssl_map.insert("verify_peer".to_string(), Box::new(PhpMixed::Bool(false)));
diff --git a/crates/shirabe/src/util/no_proxy_pattern.rs b/crates/shirabe/src/util/no_proxy_pattern.rs
index 7fbfd0f..2b593c3 100644
--- a/crates/shirabe/src/util/no_proxy_pattern.rs
+++ b/crates/shirabe/src/util/no_proxy_pattern.rs
@@ -40,7 +40,7 @@ impl NoProxyPattern {
/// @param string $pattern NO_PROXY pattern
pub fn new(pattern: &str) -> Self {
// PHP: Preg::split('{[\s,]+}', $pattern, -1, PREG_SPLIT_NO_EMPTY)
- let host_names = Preg::split(r"{[\s,]+}", pattern);
+ let host_names = Preg::split(r"{[\s,]+}", pattern).unwrap_or_default();
let noproxy = host_names.is_empty() || host_names[0] == "*";
Self {
host_names,
diff --git a/crates/shirabe/src/util/package_info.rs b/crates/shirabe/src/util/package_info.rs
index 1046f0e..d8d93ef 100644
--- a/crates/shirabe/src/util/package_info.rs
+++ b/crates/shirabe/src/util/package_info.rs
@@ -16,14 +16,14 @@ impl PackageInfo {
}
}
- package.get_source_url()
+ package.get_source_url().map(|s| s.to_string())
}
pub fn get_view_source_or_homepage_url(package: &dyn PackageInterface) -> Option<String> {
let url = Self::get_view_source_url(package).or_else(|| {
package
.as_complete_package_interface()
- .and_then(|complete| complete.get_homepage())
+ .and_then(|complete| complete.get_homepage().map(|s| s.to_string()))
});
if url.as_deref() == Some("") {
diff --git a/crates/shirabe/src/util/package_sorter.rs b/crates/shirabe/src/util/package_sorter.rs
index 2f38910..a2728b5 100644
--- a/crates/shirabe/src/util/package_sorter.rs
+++ b/crates/shirabe/src/util/package_sorter.rs
@@ -36,7 +36,7 @@ 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());
+ packages.sort_by_key(|p| p.get_name().to_string());
packages
}
@@ -48,9 +48,9 @@ impl PackageSorter {
for package in &packages {
let mut links: IndexMap<String, Link> = package.get_requires();
- // TODO: check for RootAliasPackage as well
- if let Some(root_package) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>()
- {
+ // TODO(phase-b): check for RootAliasPackage as well; PackageInterface lacks as_any
+ let root_package: Option<&RootPackage> = None;
+ if let Some(root_package) = root_package {
links.extend(root_package.get_dev_requires());
}
for link in links.values() {
diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs
index 96e564f..1f684d8 100644
--- a/crates/shirabe/src/util/platform.rs
+++ b/crates/shirabe/src/util/platform.rs
@@ -92,7 +92,8 @@ impl Platform {
/// Parses tildes and environment variables in paths.
pub fn expand_path(path: &str) -> String {
- if Preg::is_match(r"#^~[\\/]#", path) {
+ use shirabe_external_packages::composer::pcre::preg::CaptureKey;
+ if Preg::is_match(r"#^~[\\/]#", path).unwrap_or(false) {
return format!(
"{}{}",
Self::get_user_directory().unwrap(),
@@ -102,35 +103,41 @@ impl Platform {
Preg::replace_callback(
r"#^(\$|(?P<percent>%))(?P<var>\w++)(?(percent)%)(?P<path>.*)#",
- |matches| -> String {
+ |matches: &indexmap::IndexMap<CaptureKey, String>| -> String {
+ let var = matches
+ .get(&CaptureKey::ByName("var".to_string()))
+ .map(|s| s.as_str())
+ .unwrap_or("");
+ let path_part = matches
+ .get(&CaptureKey::ByName("path".to_string()))
+ .map(|s| s.as_str())
+ .unwrap_or("");
// Treat HOME as an alias for USERPROFILE on Windows for legacy reasons
- if Platform::is_windows()
- && matches.get("var").map(|s| s.as_str()).unwrap_or("") == "HOME"
- {
+ if Platform::is_windows() && var == "HOME" {
if Platform::get_env("HOME").is_some() {
return format!(
"{}{}",
Platform::get_env("HOME").unwrap_or_default(),
- matches.get("path").map(|s| s.as_str()).unwrap_or(""),
+ path_part,
);
}
return format!(
"{}{}",
Platform::get_env("USERPROFILE").unwrap_or_default(),
- matches.get("path").map(|s| s.as_str()).unwrap_or(""),
+ path_part,
);
}
format!(
"{}{}",
- Platform::get_env(matches.get("var").map(|s| s.as_str()).unwrap_or(""))
- .unwrap_or_default(),
- matches.get("path").map(|s| s.as_str()).unwrap_or(""),
+ Platform::get_env(var).unwrap_or_default(),
+ path_part,
)
},
path,
)
+ .unwrap_or_default()
}
/// @throws \RuntimeException If the user home could not reliably be determined
diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs
index 6351a73..ed1958d 100644
--- a/crates/shirabe/src/util/process_executor.rs
+++ b/crates/shirabe/src/util/process_executor.rs
@@ -118,7 +118,7 @@ impl ProcessExecutor {
/// runs a process on the commandline in TTY mode
pub fn execute_tty(&mut self, command: PhpMixed, cwd: Option<&str>) -> Result<i64> {
- if Platform::is_tty() {
+ if Platform::is_tty(None) {
return self.do_execute(command, cwd, true, None);
}
@@ -448,7 +448,7 @@ impl ProcessExecutor {
1,
min(
50,
- max_jobs_env.as_string().unwrap_or("0").parse().unwrap_or(0),
+ max_jobs_env.as_deref().unwrap_or("0").parse().unwrap_or(0),
),
);
} else {
diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs
index 0674df4..d2658fe 100644
--- a/crates/shirabe/src/util/remote_filesystem.rs
+++ b/crates/shirabe/src/util/remote_filesystem.rs
@@ -263,7 +263,7 @@ impl RemoteFilesystem {
using_proxy
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
if (!Preg::is_match("{^http://(repo\\.)?packagist\\.org/p/}", &file_url).unwrap_or(false)
@@ -277,7 +277,7 @@ impl RemoteFilesystem {
self.io.write_error(
PhpMixed::String("Downloading (<comment>connecting...</comment>)".to_string()),
false,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -352,7 +352,7 @@ impl RemoteFilesystem {
base64_encode(result.as_deref().unwrap_or(""))
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
return Err(anyhow::anyhow!(e));
@@ -398,7 +398,7 @@ impl RemoteFilesystem {
self.io.write_error(
PhpMixed::String("".to_string()),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
self.io.write_error(
PhpMixed::List(vec![
@@ -409,7 +409,7 @@ impl RemoteFilesystem {
)),
]),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
return self.get(
@@ -498,7 +498,7 @@ impl RemoteFilesystem {
PhpMixed::String("Downloading (<error>failed</error>)".to_string()),
false,
None,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -533,7 +533,7 @@ impl RemoteFilesystem {
)),
false,
None,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
@@ -565,7 +565,7 @@ impl RemoteFilesystem {
)),
]),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
return self.get(
@@ -643,7 +643,7 @@ impl RemoteFilesystem {
self.io.write_error(
PhpMixed::String("".to_string()),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
self.io.write_error(
PhpMixed::List(vec![
@@ -654,7 +654,7 @@ impl RemoteFilesystem {
)),
]),
true,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
return self.get(
@@ -778,7 +778,7 @@ impl RemoteFilesystem {
)),
false,
None,
- <dyn IOInterface>::NORMAL,
+ crate::io::io_interface::NORMAL,
);
}
}
@@ -947,7 +947,7 @@ impl RemoteFilesystem {
self.io.write_error(
PhpMixed::String("".to_string()),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
self.io.write_error(
PhpMixed::String(sprintf(
@@ -958,7 +958,7 @@ impl RemoteFilesystem {
],
)),
true,
- <dyn IOInterface>::DEBUG,
+ crate::io::io_interface::DEBUG,
);
additional_options.insert("redirects".to_string(), PhpMixed::Int(self.redirects));
diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs
index b312ec3..955e28c 100644
--- a/crates/shirabe/src/util/svn.rs
+++ b/crates/shirabe/src/util/svn.rs
@@ -211,11 +211,11 @@ impl Svn {
.into());
}
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"The Subversion server ({}) requested credentials:",
self.url,
- )),
+ ),
true,
io_interface::NORMAL,
);
diff --git a/crates/shirabe/src/util/sync_helper.rs b/crates/shirabe/src/util/sync_helper.rs
index 1be9075..22e4f1f 100644
--- a/crates/shirabe/src/util/sync_helper.rs
+++ b/crates/shirabe/src/util/sync_helper.rs
@@ -9,7 +9,7 @@ use shirabe_external_packages::react::promise::promise_interface::PromiseInterfa
pub enum DownloaderOrManager<'a> {
Interface(&'a dyn DownloaderInterface),
- Manager(&'a DownloadManager),
+ Manager(&'a std::rc::Rc<std::cell::RefCell<DownloadManager>>),
}
impl<'a> DownloaderOrManager<'a> {
@@ -21,7 +21,7 @@ impl<'a> DownloaderOrManager<'a> {
) -> Box<dyn PromiseInterface> {
match self {
Self::Interface(d) => d.download(package, path, prev_package),
- Self::Manager(d) => d.download(package, path, prev_package),
+ Self::Manager(d) => d.borrow().download(package, path, prev_package),
}
}
@@ -34,14 +34,14 @@ impl<'a> DownloaderOrManager<'a> {
) -> Box<dyn PromiseInterface> {
match self {
Self::Interface(d) => d.prepare(r#type, package, path, prev_package),
- Self::Manager(d) => d.prepare(r#type, package, path, prev_package),
+ Self::Manager(d) => d.borrow().prepare(r#type, package, path, prev_package),
}
}
fn install(&self, package: &dyn PackageInterface, path: &str) -> Box<dyn PromiseInterface> {
match self {
Self::Interface(d) => d.install(package, path),
- Self::Manager(d) => d.install(package, path),
+ Self::Manager(d) => d.borrow().install(package, path),
}
}
@@ -53,7 +53,7 @@ impl<'a> DownloaderOrManager<'a> {
) -> Box<dyn PromiseInterface> {
match self {
Self::Interface(d) => d.update(package, prev_package, path),
- Self::Manager(d) => d.update(package, prev_package, path),
+ Self::Manager(d) => d.borrow().update(package, prev_package, path),
}
}
@@ -66,7 +66,7 @@ impl<'a> DownloaderOrManager<'a> {
) -> Box<dyn PromiseInterface> {
match self {
Self::Interface(d) => d.cleanup(r#type, package, path, prev_package),
- Self::Manager(d) => d.cleanup(r#type, package, path, prev_package),
+ Self::Manager(d) => d.borrow().cleanup(r#type, package, path, prev_package),
}
}
}
@@ -75,7 +75,7 @@ pub struct SyncHelper;
impl SyncHelper {
pub fn download_and_install_package_sync(
- r#loop: &Loop,
+ r#loop: &std::rc::Rc<std::cell::RefCell<Loop>>,
downloader: DownloaderOrManager<'_>,
path: String,
package: &dyn PackageInterface,
@@ -121,9 +121,12 @@ impl SyncHelper {
Ok(())
}
- pub fn r#await(r#loop: &Loop, promise: Option<Box<dyn PromiseInterface>>) -> Result<()> {
+ pub fn r#await(
+ r#loop: &std::rc::Rc<std::cell::RefCell<Loop>>,
+ promise: Option<Box<dyn PromiseInterface>>,
+ ) -> Result<()> {
if let Some(promise) = promise {
- r#loop.wait(vec![promise]);
+ r#loop.borrow_mut().wait(vec![promise], None)?;
}
Ok(())
}