aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/preg.rs7
-rw-r--r--crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs5
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter.rs8
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/input/input_interface.rs5
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/output/output_interface.rs4
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/output/stream_output.rs7
-rw-r--r--crates/shirabe-php-shim/src/lib.rs25
-rw-r--r--crates/shirabe-semver/src/constraint/constraint.rs7
-rw-r--r--crates/shirabe-semver/src/constraint/match_all_constraint.rs14
-rw-r--r--crates/shirabe-semver/src/constraint/multi_constraint.rs1
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs1
-rw-r--r--crates/shirabe/src/cache.rs40
-rw-r--r--crates/shirabe/src/command/about_command.rs4
-rw-r--r--crates/shirabe/src/command/archive_command.rs151
-rw-r--r--crates/shirabe/src/command/audit_command.rs77
-rw-r--r--crates/shirabe/src/command/base_command.rs114
-rw-r--r--crates/shirabe/src/command/base_config_command.rs53
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs78
-rw-r--r--crates/shirabe/src/command/bump_command.rs151
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs81
-rw-r--r--crates/shirabe/src/command/clear_cache_command.rs4
-rw-r--r--crates/shirabe/src/command/config_command.rs222
-rw-r--r--crates/shirabe/src/command/create_project_command.rs185
-rw-r--r--crates/shirabe/src/command/depends_command.rs28
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs142
-rw-r--r--crates/shirabe/src/command/dump_autoload_command.rs55
-rw-r--r--crates/shirabe/src/command/exec_command.rs53
-rw-r--r--crates/shirabe/src/command/fund_command.rs37
-rw-r--r--crates/shirabe/src/command/global_command.rs39
-rw-r--r--crates/shirabe/src/command/home_command.rs73
-rw-r--r--crates/shirabe/src/command/init_command.rs154
-rw-r--r--crates/shirabe/src/command/install_command.rs85
-rw-r--r--crates/shirabe/src/command/licenses_command.rs31
-rw-r--r--crates/shirabe/src/command/outdated_command.rs36
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs31
-rw-r--r--crates/shirabe/src/command/prohibits_command.rs36
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs68
-rw-r--r--crates/shirabe/src/command/remove_command.rs301
-rw-r--r--crates/shirabe/src/command/repository_command.rs77
-rw-r--r--crates/shirabe/src/command/require_command.rs204
-rw-r--r--crates/shirabe/src/command/run_script_command.rs31
-rw-r--r--crates/shirabe/src/command/script_alias_command.rs20
-rw-r--r--crates/shirabe/src/command/search_command.rs25
-rw-r--r--crates/shirabe/src/command/self_update_command.rs162
-rw-r--r--crates/shirabe/src/command/show_command.rs87
-rw-r--r--crates/shirabe/src/command/status_command.rs28
-rw-r--r--crates/shirabe/src/command/suggests_command.rs20
-rw-r--r--crates/shirabe/src/command/update_command.rs99
-rw-r--r--crates/shirabe/src/command/validate_command.rs58
-rw-r--r--crates/shirabe/src/compiler.rs12
-rw-r--r--crates/shirabe/src/composer.rs19
-rw-r--r--crates/shirabe/src/config.rs71
-rw-r--r--crates/shirabe/src/config/config_source_interface.rs2
-rw-r--r--crates/shirabe/src/config/json_config_source.rs2
-rw-r--r--crates/shirabe/src/console/application.rs32
-rw-r--r--crates/shirabe/src/console/html_output_formatter.rs16
-rw-r--r--crates/shirabe/src/console/input/mod.rs17
-rw-r--r--crates/shirabe/src/dependency_resolver/default_policy.rs18
-rw-r--r--crates/shirabe/src/dependency_resolver/generic_rule.rs17
-rw-r--r--crates/shirabe/src/dependency_resolver/lock_transaction.rs20
-rw-r--r--crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs2
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/install_operation.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs4
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs4
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/operation_interface.rs18
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/operation/update_operation.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/policy_interface.rs2
-rw-r--r--crates/shirabe/src/dependency_resolver/pool.rs6
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_builder.rs54
-rw-r--r--crates/shirabe/src/dependency_resolver/pool_optimizer.rs56
-rw-r--r--crates/shirabe/src/dependency_resolver/problem.rs49
-rw-r--r--crates/shirabe/src/dependency_resolver/rule.rs114
-rw-r--r--crates/shirabe/src/dependency_resolver/rule2_literals.rs2
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set.rs4
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set_generator.rs68
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_watch_graph.rs10
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_watch_node.rs6
-rw-r--r--crates/shirabe/src/dependency_resolver/solver.rs56
-rw-r--r--crates/shirabe/src/dependency_resolver/transaction.rs45
-rw-r--r--crates/shirabe/src/downloader/archive_downloader.rs26
-rw-r--r--crates/shirabe/src/downloader/download_manager.rs36
-rw-r--r--crates/shirabe/src/downloader/downloader_interface.rs30
-rw-r--r--crates/shirabe/src/downloader/file_downloader.rs87
-rw-r--r--crates/shirabe/src/downloader/fossil_downloader.rs28
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs348
-rw-r--r--crates/shirabe/src/downloader/gzip_downloader.rs12
-rw-r--r--crates/shirabe/src/downloader/hg_downloader.rs30
-rw-r--r--crates/shirabe/src/downloader/path_downloader.rs103
-rw-r--r--crates/shirabe/src/downloader/perforce_downloader.rs2
-rw-r--r--crates/shirabe/src/downloader/phar_downloader.rs8
-rw-r--r--crates/shirabe/src/downloader/rar_downloader.rs12
-rw-r--r--crates/shirabe/src/downloader/svn_downloader.rs96
-rw-r--r--crates/shirabe/src/downloader/tar_downloader.rs8
-rw-r--r--crates/shirabe/src/downloader/transport_exception.rs6
-rw-r--r--crates/shirabe/src/downloader/vcs_downloader.rs97
-rw-r--r--crates/shirabe/src/downloader/xz_downloader.rs12
-rw-r--r--crates/shirabe/src/downloader/zip_downloader.rs24
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs178
-rw-r--r--crates/shirabe/src/factory.rs361
-rw-r--r--crates/shirabe/src/installer.rs100
-rw-r--r--crates/shirabe/src/installer/binary_installer.rs82
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs12
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs2
-rw-r--r--crates/shirabe/src/installer/library_installer.rs42
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs2
-rw-r--r--crates/shirabe/src/installer/project_installer.rs6
-rw-r--r--crates/shirabe/src/installer/suggested_packages_reporter.rs2
-rw-r--r--crates/shirabe/src/io/base_io.rs50
-rw-r--r--crates/shirabe/src/io/buffer_io.rs19
-rw-r--r--crates/shirabe/src/io/console_io.rs70
-rw-r--r--crates/shirabe/src/io/io_interface.rs44
-rw-r--r--crates/shirabe/src/io/null_io.rs12
-rw-r--r--crates/shirabe/src/json/json_file.rs70
-rw-r--r--crates/shirabe/src/json/json_manipulator.rs92
-rw-r--r--crates/shirabe/src/package/alias_package.rs12
-rw-r--r--crates/shirabe/src/package/archiver/archive_manager.rs6
-rw-r--r--crates/shirabe/src/package/base_package.rs8
-rw-r--r--crates/shirabe/src/package/dumper/array_dumper.rs5
-rw-r--r--crates/shirabe/src/package/link.rs5
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs2
-rw-r--r--crates/shirabe/src/package/loader/loader_interface.rs2
-rw-r--r--crates/shirabe/src/package/loader/root_package_loader.rs54
-rw-r--r--crates/shirabe/src/package/locker.rs48
-rw-r--r--crates/shirabe/src/package/package_interface.rs6
-rw-r--r--crates/shirabe/src/package/version/version_bumper.rs17
-rw-r--r--crates/shirabe/src/package/version/version_guesser.rs106
-rw-r--r--crates/shirabe/src/package/version/version_parser.rs6
-rw-r--r--crates/shirabe/src/package/version/version_selector.rs44
-rw-r--r--crates/shirabe/src/partial_composer.rs12
-rw-r--r--crates/shirabe/src/platform/hhvm_detector.rs11
-rw-r--r--crates/shirabe/src/platform/version.rs84
-rw-r--r--crates/shirabe/src/plugin/command_event.rs21
-rw-r--r--crates/shirabe/src/plugin/plugin_manager.rs17
-rw-r--r--crates/shirabe/src/plugin/pre_command_run_event.rs2
-rw-r--r--crates/shirabe/src/plugin/pre_file_download_event.rs6
-rw-r--r--crates/shirabe/src/repository/array_repository.rs52
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs20
-rw-r--r--crates/shirabe/src/repository/canonical_packages_trait.rs17
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs87
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs21
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs76
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs8
-rw-r--r--crates/shirabe/src/repository/installed_array_repository.rs14
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs6
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs24
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs4
-rw-r--r--crates/shirabe/src/repository/package_repository.rs90
-rw-r--r--crates/shirabe/src/repository/path_repository.rs74
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs565
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs53
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs13
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs18
-rw-r--r--crates/shirabe/src/repository/repository_set.rs43
-rw-r--r--crates/shirabe/src/repository/repository_utils.rs14
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs4
-rw-r--r--crates/shirabe/src/repository/vcs/forgejo_driver.rs46
-rw-r--r--crates/shirabe/src/repository/vcs/fossil_driver.rs58
-rw-r--r--crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs46
-rw-r--r--crates/shirabe/src/repository/vcs/git_driver.rs108
-rw-r--r--crates/shirabe/src/repository/vcs/github_driver.rs202
-rw-r--r--crates/shirabe/src/repository/vcs/gitlab_driver.rs165
-rw-r--r--crates/shirabe/src/repository/vcs/hg_driver.rs55
-rw-r--r--crates/shirabe/src/repository/vcs/perforce_driver.rs1
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs132
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver.rs65
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver_interface.rs2
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs23
-rw-r--r--crates/shirabe/src/repository/version_cache_interface.rs2
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs20
-rw-r--r--crates/shirabe/src/self_update/versions.rs31
-rw-r--r--crates/shirabe/src/util/auth_helper.rs42
-rw-r--r--crates/shirabe/src/util/bitbucket.rs230
-rw-r--r--crates/shirabe/src/util/config_validator.rs12
-rw-r--r--crates/shirabe/src/util/filesystem.rs30
-rw-r--r--crates/shirabe/src/util/forgejo.rs23
-rw-r--r--crates/shirabe/src/util/git.rs383
-rw-r--r--crates/shirabe/src/util/github.rs253
-rw-r--r--crates/shirabe/src/util/gitlab.rs151
-rw-r--r--crates/shirabe/src/util/hg.rs36
-rw-r--r--crates/shirabe/src/util/http/curl_downloader.rs60
-rw-r--r--crates/shirabe/src/util/http/proxy_manager.rs16
-rw-r--r--crates/shirabe/src/util/http/response.rs2
-rw-r--r--crates/shirabe/src/util/http_downloader.rs44
-rw-r--r--crates/shirabe/src/util/loop.rs28
-rw-r--r--crates/shirabe/src/util/perforce.rs38
-rw-r--r--crates/shirabe/src/util/process_executor.rs66
-rw-r--r--crates/shirabe/src/util/remote_filesystem.rs145
-rw-r--r--crates/shirabe/src/util/svn.rs49
-rw-r--r--crates/shirabe/src/util/sync_helper.rs30
-rw-r--r--crates/shirabe/src/util/url.rs110
191 files changed, 6035 insertions, 4429 deletions
diff --git a/crates/shirabe-external-packages/src/composer/pcre/preg.rs b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
index d1614a7..e16643a 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/preg.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
@@ -437,6 +437,13 @@ impl Preg {
todo!()
}
+ pub fn is_match_with_indexed_captures(
+ _pattern: &str,
+ _subject: &str,
+ ) -> anyhow::Result<Option<Vec<String>>> {
+ todo!()
+ }
+
pub fn is_match_all3(
_pattern: &str,
_subject: &str,
diff --git a/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs b/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs
index ef4b009..15a72c2 100644
--- a/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs
+++ b/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs
@@ -24,4 +24,9 @@ pub trait InputInterface {
fn has_option(&self, name: &str) -> bool;
fn is_interactive(&self) -> bool;
fn set_interactive(&mut self, interactive: bool);
+
+ /// Equivalent to PHP `(string) $input` (Input::__toString).
+ fn to_input_string(&self) -> String {
+ todo!()
+ }
}
diff --git a/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter.rs b/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter.rs
index 5eb2f93..01f8b61 100644
--- a/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter.rs
+++ b/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter.rs
@@ -21,4 +21,12 @@ impl OutputFormatter {
pub fn escape(_text: &str) -> String {
todo!()
}
+
+ pub fn set_style(
+ &mut self,
+ _name: &str,
+ _style: crate::symfony::console::formatter::output_formatter_style::OutputFormatterStyle,
+ ) {
+ todo!()
+ }
}
diff --git a/crates/shirabe-external-packages/src/symfony/console/input/input_interface.rs b/crates/shirabe-external-packages/src/symfony/console/input/input_interface.rs
index d1c11c2..5ec5896 100644
--- a/crates/shirabe-external-packages/src/symfony/console/input/input_interface.rs
+++ b/crates/shirabe-external-packages/src/symfony/console/input/input_interface.rs
@@ -21,4 +21,9 @@ pub trait InputInterface {
fn has_option(&self, name: &str) -> bool;
fn is_interactive(&self) -> bool;
fn set_interactive(&mut self, interactive: bool);
+
+ /// Equivalent to PHP `(string) $input` (Input::__toString).
+ fn to_input_string(&self) -> String {
+ todo!()
+ }
}
diff --git a/crates/shirabe-external-packages/src/symfony/console/output/output_interface.rs b/crates/shirabe-external-packages/src/symfony/console/output/output_interface.rs
index 4031cc6..65c2026 100644
--- a/crates/shirabe-external-packages/src/symfony/console/output/output_interface.rs
+++ b/crates/shirabe-external-packages/src/symfony/console/output/output_interface.rs
@@ -1,3 +1,5 @@
+use crate::symfony::console::formatter::output_formatter::OutputFormatter;
+
pub trait OutputInterface {
fn write(&mut self, messages: &str, newline: bool, r#type: i64);
fn writeln(&mut self, messages: &str, r#type: i64);
@@ -9,6 +11,8 @@ pub trait OutputInterface {
fn is_debug(&self) -> bool;
fn set_decorated(&mut self, decorated: bool);
fn is_decorated(&self) -> bool;
+ fn set_formatter(&mut self, formatter: OutputFormatter);
+ fn get_formatter(&mut self) -> &mut OutputFormatter;
}
pub const VERBOSITY_QUIET: i64 = 16;
diff --git a/crates/shirabe-external-packages/src/symfony/console/output/stream_output.rs b/crates/shirabe-external-packages/src/symfony/console/output/stream_output.rs
index 068b678..fb7f607 100644
--- a/crates/shirabe-external-packages/src/symfony/console/output/stream_output.rs
+++ b/crates/shirabe-external-packages/src/symfony/console/output/stream_output.rs
@@ -1,3 +1,4 @@
+use crate::symfony::console::formatter::output_formatter::OutputFormatter;
use crate::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
@@ -41,4 +42,10 @@ impl OutputInterface for StreamOutput {
fn is_decorated(&self) -> bool {
todo!()
}
+ fn set_formatter(&mut self, _formatter: OutputFormatter) {
+ todo!()
+ }
+ fn get_formatter(&mut self) -> &mut OutputFormatter {
+ todo!()
+ }
}
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index e6fbf05..6b2d790 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -55,6 +55,31 @@ impl PhpMixed {
}
}
+ pub fn as_array_mut(&mut self) -> Option<&mut IndexMap<String, Box<PhpMixed>>> {
+ match self {
+ PhpMixed::Array(a) => Some(a),
+ _ => None,
+ }
+ }
+
+ pub fn as_list_mut(&mut self) -> Option<&mut Vec<Box<PhpMixed>>> {
+ match self {
+ PhpMixed::List(l) => Some(l),
+ _ => None,
+ }
+ }
+
+ pub fn as_object(&self) -> Option<&ArrayObject> {
+ match self {
+ PhpMixed::Object(o) => Some(o),
+ _ => None,
+ }
+ }
+
+ pub fn as_str(&self) -> Option<&str> {
+ self.as_string()
+ }
+
pub fn is_null(&self) -> bool {
matches!(self, PhpMixed::Null)
}
diff --git a/crates/shirabe-semver/src/constraint/constraint.rs b/crates/shirabe-semver/src/constraint/constraint.rs
index 1435139..d64c0a0 100644
--- a/crates/shirabe-semver/src/constraint/constraint.rs
+++ b/crates/shirabe-semver/src/constraint/constraint.rs
@@ -388,3 +388,10 @@ impl ConstraintInterface for Constraint {
self
}
}
+
+impl std::fmt::Display for Constraint {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use crate::constraint::constraint_interface::ConstraintInterface;
+ write!(f, "{}", ConstraintInterface::__to_string(self))
+ }
+}
diff --git a/crates/shirabe-semver/src/constraint/match_all_constraint.rs b/crates/shirabe-semver/src/constraint/match_all_constraint.rs
index 97ce98c..41aa32d 100644
--- a/crates/shirabe-semver/src/constraint/match_all_constraint.rs
+++ b/crates/shirabe-semver/src/constraint/match_all_constraint.rs
@@ -8,6 +8,20 @@ pub struct MatchAllConstraint {
pub(crate) pretty_string: Option<String>,
}
+impl MatchAllConstraint {
+ pub fn new() -> Self {
+ Self {
+ pretty_string: None,
+ }
+ }
+}
+
+impl Default for MatchAllConstraint {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl ConstraintInterface for MatchAllConstraint {
fn matches(&self, _provider: &dyn ConstraintInterface) -> bool {
true
diff --git a/crates/shirabe-semver/src/constraint/multi_constraint.rs b/crates/shirabe-semver/src/constraint/multi_constraint.rs
index 5596cc4..15397b6 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 crate::constraint::bound::Bound;
use crate::constraint::constraint_interface::ConstraintInterface;
use crate::constraint::match_all_constraint::MatchAllConstraint;
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index 92f2322..7708eb5 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -15,6 +15,7 @@ use shirabe_php_shim::{
substr_count, trigger_error, trim, unlink, var_export,
};
use shirabe_semver::constraint::bound::Bound;
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
use crate::autoload::class_loader::ClassLoader;
use crate::config::Config;
diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs
index d7b0b7e..848ee80 100644
--- a/crates/shirabe/src/cache.rs
+++ b/crates/shirabe/src/cache.rs
@@ -23,7 +23,7 @@ pub struct Cache {
root: String,
enabled: Option<bool>,
allowlist: String,
- filesystem: Filesystem,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
read_only: bool,
}
@@ -39,12 +39,13 @@ impl Cache {
io: Box<dyn IOInterface>,
cache_dir: &str,
allowlist: Option<&str>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
read_only: bool,
) -> 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(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))));
let mut this = Self {
io,
root,
@@ -152,6 +153,7 @@ impl Cache {
.unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
self.filesystem
+ .borrow_mut()
.ensure_directory_exists(&dirname(&full_path));
if !file_exists(source) {
@@ -164,7 +166,11 @@ impl Cache {
.write_error(&format!("Writing {} into cache from {}", full_path, source));
}
- return self.filesystem.copy(source, &full_path).unwrap_or(false);
+ return self
+ .filesystem
+ .borrow_mut()
+ .copy(source, &full_path)
+ .unwrap_or(false);
}
false
@@ -197,7 +203,7 @@ impl Cache {
self.io
.write_error(&format!("Reading {} from cache", full_path));
- return self.filesystem.copy(&full_path, target);
+ return self.filesystem.borrow_mut().copy(&full_path, target);
}
}
@@ -228,7 +234,11 @@ impl Cache {
.unwrap_or_default();
let full_path = format!("{}{}", self.root, file);
if file_exists(&full_path) {
- return self.filesystem.unlink(&full_path).unwrap_or(false);
+ return self
+ .filesystem
+ .borrow_mut()
+ .unlink(&full_path)
+ .unwrap_or(false);
}
}
@@ -237,7 +247,10 @@ impl Cache {
pub fn clear(&mut self) -> bool {
if self.is_enabled() && !self.read_only {
- let _ = self.filesystem.empty_directory(&self.root, true);
+ let _ = self
+ .filesystem
+ .borrow_mut()
+ .empty_directory(&self.root, true);
return true;
}
@@ -271,16 +284,16 @@ impl Cache {
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 _ = self.filesystem.borrow_mut().unlink(&file.get_pathname());
}
- let mut total_size = self.filesystem.size(&self.root).unwrap_or(0);
+ let mut total_size = self.filesystem.borrow_mut().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).unwrap_or(0);
- let _ = self.filesystem.unlink(&filepath);
+ total_size -= self.filesystem.borrow_mut().size(&filepath).unwrap_or(0);
+ let _ = self.filesystem.borrow_mut().unlink(&filepath);
iterator.next();
}
}
@@ -305,7 +318,10 @@ impl Cache {
.depth(0)
.date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S")));
for file in &mut finder {
- let _ = self.filesystem.remove_directory(&file.get_pathname());
+ let _ = self
+ .filesystem
+ .borrow_mut()
+ .remove_directory(&file.get_pathname());
}
*CACHE_COLLECTED.lock().unwrap() = Some(true);
diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs
index 0503578..59b14fe 100644
--- a/crates/shirabe/src/command/about_command.rs
+++ b/crates/shirabe/src/command/about_command.rs
@@ -5,8 +5,8 @@ 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::console::input::input_interface::InputInterface;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct AboutCommand {
diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs
index c4255db..7cdcbcf 100644
--- a/crates/shirabe/src/command/archive_command.rs
+++ b/crates/shirabe/src/command/archive_command.rs
@@ -3,9 +3,10 @@
use std::any::Any;
use anyhow::Result;
-use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
+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::{LogicException, get_debug_type};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -15,6 +16,7 @@ use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::factory::Factory;
use crate::io::io_interface::IOInterface;
+use crate::package::archiver::archive_manager::ArchiveManager;
use crate::package::base_package::BasePackage;
use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::version::version_parser::VersionParser;
@@ -43,13 +45,13 @@ impl ArchiveCommand {
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),
- 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_definition(&[
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None).unwrap().into(),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None).unwrap().into(),
+ InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None).unwrap().into(),
+ InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None).unwrap().into(),
])
.set_help(
"The <info>archive</info> command creates an archive of the specified format\n\
@@ -62,19 +64,12 @@ impl ArchiveCommand {
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
let composer = self.try_composer(None, None);
- let mut config: Option<Config> = None;
+ let mut config: Option<std::rc::Rc<std::cell::RefCell<Config>>> = None;
if let Some(ref composer) = composer {
- config = Some(composer.get_config().clone());
+ config = Some(std::rc::Rc::clone(composer.get_config()));
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "archive".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "archive", input, output);
let event_dispatcher = composer.get_event_dispatcher();
event_dispatcher.dispatch(Some(command_event.get_name()), None);
event_dispatcher.dispatch_script(
@@ -87,7 +82,7 @@ impl ArchiveCommand {
let config = match config {
Some(c) => c,
- None => Factory::create_config(None, None)?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)),
};
let format = input
@@ -96,6 +91,7 @@ impl ArchiveCommand {
.map(|s| s.to_string())
.unwrap_or_else(|| {
config
+ .borrow_mut()
.get("archive-format")
.as_string()
.unwrap_or("tar")
@@ -108,6 +104,7 @@ impl ArchiveCommand {
.map(|s| s.to_string())
.unwrap_or_else(|| {
config
+ .borrow_mut()
.get("archive-dir")
.as_string()
.unwrap_or(".")
@@ -155,7 +152,7 @@ impl ArchiveCommand {
pub fn archive(
&self,
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
package_name: Option<String>,
version: Option<String>,
format: &str,
@@ -164,19 +161,24 @@ impl ArchiveCommand {
ignore_filters: bool,
composer: Option<&Composer>,
) -> Result<i64> {
- let archive_manager = if let Some(composer) = composer {
- composer.get_archive_manager().clone_box()
+ let owned_archive_manager;
+ let archive_manager: &ArchiveManager = if let Some(composer) = composer {
+ composer.get_archive_manager()
} else {
- let factory = Factory::new();
- let process = ProcessExecutor::new(None, None);
- let http_downloader = Factory::create_http_downloader(io, config)?;
+ let factory = Factory;
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None)));
+ let http_downloader = std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, config, indexmap::IndexMap::new())?,
+ ));
let download_manager =
- factory.create_download_manager(io, config, &http_downloader, &process)?;
+ factory.create_download_manager(io, config, &http_downloader, &process, None)?;
let loop_ = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
- http_downloader,
+ std::rc::Rc::clone(&http_downloader),
Some(process),
)));
- factory.create_archive_manager(config, &download_manager, &loop_)?
+ owned_archive_manager =
+ factory.create_archive_manager(&*config.borrow(), &download_manager, &loop_)?;
+ &owned_archive_manager
};
let package = if let Some(name) = package_name {
@@ -192,13 +194,17 @@ impl ArchiveCommand {
"<info>Creating the archive into \"{}\".</info>",
dest
));
- let package_path = archive_manager.archive(
+ // TODO(phase-b): ArchiveManager.archive needs &mut self and &mut CompletePackageInterface;
+ // current composer.get_archive_manager() returns &ArchiveManager. Needs RefCell wrapper.
+ let _ = archive_manager;
+ let _ = (
package.as_ref(),
format,
dest,
file_name.as_deref(),
ignore_filters,
- )?;
+ );
+ let package_path: String = todo!("ArchiveManager.archive call");
let fs = Filesystem::new(None);
let short_path =
fs.find_shortest_path(&Platform::get_cwd(false)?, &package_path, true, false);
@@ -252,28 +258,57 @@ impl ArchiveCommand {
}
if let Some(version_str) = &version {
- if let Some(matches) =
- Preg::match_strict_groups(r"{@(stable|RC|beta|alpha|dev)$}i", version_str)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
+ r"{@(stable|RC|beta|alpha|dev)$}i",
+ version_str,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
{
- min_stability = VersionParser::normalize_stability(&matches[1]);
- let full_match_len = matches[0].len();
+ let m1 = matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ let m0 = matches
+ .get(&CaptureKey::ByIndex(0))
+ .cloned()
+ .unwrap_or_default();
+ min_stability = VersionParser::normalize_stability(&m1)?;
+ let full_match_len = m0.len();
version = Some(version_str[..version_str.len() - full_match_len].to_string());
}
}
- let mut repo_set = RepositorySet::new(&min_stability);
- repo_set.add_repository(Box::new(repo));
+ let mut repo_set = RepositorySet::new(
+ &min_stability,
+ IndexMap::new(),
+ Vec::new(),
+ IndexMap::new(),
+ IndexMap::new(),
+ IndexMap::new(),
+ );
+ repo_set.add_repository(Box::new(repo))?;
let parser = VersionParser::new();
- let constraint = version.as_deref().map(|v| parser.parse_constraints(v));
- let packages = repo_set.find_packages(&package_name.to_lowercase(), constraint.as_deref());
+ let constraint: Option<
+ Box<dyn shirabe_semver::constraint::constraint_interface::ConstraintInterface>,
+ > = match version.as_deref() {
+ Some(v) => Some(parser.parse_constraints(v)?.clone_box()),
+ None => None,
+ };
+ let packages = repo_set.find_packages(&package_name.to_lowercase(), constraint, 0);
let package = if packages.len() > 1 {
- let version_selector = VersionSelector::new(&repo_set);
+ let mut version_selector = VersionSelector::new(repo_set, None)?;
let best = version_selector.find_best_candidate(
&package_name.to_lowercase(),
version.as_deref(),
&min_stability,
- );
+ None,
+ 0,
+ None,
+ shirabe_php_shim::PhpMixed::Bool(true),
+ )?;
let p = best.unwrap_or_else(|| packages.into_iter().next().unwrap());
io.write_error(&format!(
@@ -298,34 +333,10 @@ impl ArchiveCommand {
return Ok(None);
};
- if (package.as_any() as &dyn Any)
- .downcast_ref::<dyn CompletePackageInterface>()
- .is_none()
- {
- return Err(LogicException {
- message: format!(
- "Expected a CompletePackageInterface instance but found {}",
- get_debug_type(package.as_php_mixed())
- ),
- code: 0,
- }
- .into());
- }
- if (package.as_any() as &dyn Any)
- .downcast_ref::<BasePackage>()
- .is_none()
- {
- return Err(LogicException {
- message: format!(
- "Expected a BasePackage instance but found {}",
- get_debug_type(package.as_php_mixed())
- ),
- code: 0,
- }
- .into());
- }
-
- Ok(Some(package.into_complete()))
+ // TODO(phase-b): instanceof CompletePackageInterface / BasePackage runtime
+ // checks require downcast support that BasePackage trait does not yet expose.
+ let _ = &package;
+ todo!("convert Box<dyn BasePackage> into Box<dyn CompletePackageInterface>")
}
}
diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs
index cf4c49d..1ced26a 100644
--- a/crates/shirabe/src/command/audit_command.rs
+++ b/crates/shirabe/src/command/audit_command.rs
@@ -13,8 +13,8 @@ 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::console::input::input_interface::InputInterface;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+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::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_fill_keys, array_merge,
implode, in_array,
@@ -30,13 +30,13 @@ impl AuditCommand {
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),
- 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_definition(&[
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables auditing of require-dev packages.", None).unwrap().into(),
+ 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()))).unwrap().into(),
+ InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Audit based on the lock file instead of the installed packages.", None).unwrap().into(),
+ InputOption::new("abandoned", None, Some(InputOption::VALUE_REQUIRED), "Behavior on abandoned packages. Must be \"ignore\", \"report\", or \"fail\".", None).unwrap().into(),
+ 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()))).unwrap().into(),
+ InputOption::new("ignore-unreachable", None, Some(InputOption::VALUE_NONE), "Ignore repositories that are unreachable or return a non-200 status code.", None).unwrap().into(),
])
.set_help(
"The <info>audit</info> command checks for security vulnerability advisories for installed packages.\n\n\
@@ -59,14 +59,25 @@ impl AuditCommand {
return Ok(0);
}
- let auditor = Auditor::new();
- let mut repo_set = RepositorySet::new();
+ let auditor = Auditor;
+ let mut repo_set = RepositorySet::new(
+ "stable",
+ indexmap::IndexMap::new(),
+ Vec::new(),
+ indexmap::IndexMap::new(),
+ indexmap::IndexMap::new(),
+ indexmap::IndexMap::new(),
+ );
for repo in composer.get_repository_manager().get_repositories() {
- repo_set.add_repository(repo);
+ // TODO(phase-b): repositories are shared (PHP class semantics); needs Rc wrapper
+ repo_set.add_repository(repo.clone_box())?;
}
- let audit_config =
- AuditConfig::from_config(composer.get_config(), true, Auditor::FORMAT_SUMMARY)?;
+ let audit_config = AuditConfig::from_config(
+ &mut *composer.get_config().borrow_mut(),
+ true,
+ Auditor::FORMAT_SUMMARY,
+ )?;
let abandoned = input
.get_option("abandoned")
@@ -107,18 +118,21 @@ impl AuditCommand {
.unwrap_or(false)
|| audit_config.ignore_unreachable;
+ let audit_format = self.get_audit_format(input, "format")?;
+ // TODO(phase-b): ignore_severities is PhpMixed; need conversion to IndexMap<String, Option<String>>
+ let _ = ignore_severities;
Ok(auditor
.audit(
self.get_io(),
&repo_set,
- &packages,
- &self.get_audit_format(input, "format"),
+ packages,
+ &audit_format,
false,
- &audit_config.ignore_list_for_audit,
+ audit_config.ignore_list_for_audit.clone(),
&abandoned,
- &ignore_severities,
+ indexmap::IndexMap::new(),
ignore_unreachable,
- &audit_config.ignore_abandoned_for_audit,
+ audit_config.ignore_abandoned_for_audit.clone(),
)?
.min(255))
}
@@ -136,24 +150,19 @@ impl AuditCommand {
}.into());
}
let locker = composer.get_locker();
- return Ok(locker
- .get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))?
- .get_packages());
- }
-
- let root_pkg = composer.get_package();
- let installed_repo = InstalledRepository::new(vec![
- composer.get_repository_manager().get_local_repository(),
- ]);
-
- if input.get_option("no-dev").as_bool().unwrap_or(false) {
- return Ok(RepositoryUtils::filter_required_packages(
- installed_repo.get_packages(),
- root_pkg,
+ return Ok(CanonicalPackagesTrait::get_packages(
+ &locker.get_locked_repository(
+ !input.get_option("no-dev").as_bool().unwrap_or(false),
+ )?,
));
}
- Ok(installed_repo.get_packages())
+ let _root_pkg = composer.get_package();
+ // TODO(phase-b): InstalledRepository::new expects Vec<Box<dyn RepositoryInterface>>, but
+ // get_local_repository returns &dyn InstalledRepositoryInterface. Conversion requires
+ // either cloning into a Box or restructuring InstalledRepository constructor.
+ let _ = RepositoryUtils::filter_required_packages;
+ todo!("audit get_packages non-locked branch needs installed-repo conversion")
}
}
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs
index 3e0ded0..e3a2569 100644
--- a/crates/shirabe/src/command/base_command.rs
+++ b/crates/shirabe/src/command/base_command.rs
@@ -20,6 +20,7 @@ use crate::command::self_update_command::SelfUpdateCommand;
use crate::composer::Composer;
use crate::config::Config;
use crate::console::application::Application;
+use crate::console::input::InputDefinitionItem;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::factory::Factory;
@@ -32,6 +33,10 @@ use crate::plugin::plugin_events::PluginEvents;
use crate::plugin::pre_command_run_event::PreCommandRunEvent;
use crate::util::platform::Platform;
+pub const SUCCESS: i64 = 0;
+pub const FAILURE: i64 = 1;
+pub const INVALID: i64 = 2;
+
/// \Composer\Composer\Command\BaseCommand + \Symfony\Component\Console\Command\Command
pub trait BaseCommand {
fn new(_name: Option<&str>) -> Self
@@ -70,7 +75,7 @@ pub trait BaseCommand {
todo!()
}
- fn set_definition(&mut self, _definition: PhpMixed) -> &mut Self
+ fn set_definition(&mut self, _definition: &[InputDefinitionItem]) -> &mut Self
where
Self: Sized,
{
@@ -241,7 +246,7 @@ pub trait BaseCommand {
/// Creates an AuditConfig from the Config object, optionally overriding security blocking based on input options
fn create_audit_config(
&self,
- config: &Config,
+ config: &mut Config,
input: &dyn InputInterface,
) -> Result<AuditConfig>;
}
@@ -296,45 +301,26 @@ impl<C: HasBaseCommandData> BaseCommand for C {
fn require_composer(
&mut self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
+ _disable_plugins: Option<bool>,
+ _disable_scripts: Option<bool>,
) -> Result<Composer> {
- if self.composer().is_none() {
- // 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)?);
- } else {
- return Err(RuntimeException {
- message:
- "Could not create a Composer\\Composer instance, you must inject one if this command is not used with a Composer\\Console\\Application instance"
- .to_string(),
- code: 0,
- }
- .into());
- }
- }
-
- Ok(self.composer().clone().unwrap())
+ // TODO(phase-b): Composer is a PHP class (shared by reference). Returning owned
+ // `Composer` and the Application::get_composer -> &Composer mismatch require a
+ // shared-ownership wrapper (Rc<RefCell<Composer>>) before this can be wired up.
+ let _ = RuntimeException {
+ message: String::new(),
+ code: 0,
+ };
+ todo!("require_composer pending Composer shared-ownership refactor")
}
fn try_composer(
&mut self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
+ _disable_plugins: Option<bool>,
+ _disable_scripts: Option<bool>,
) -> Option<Composer> {
- if self.composer().is_none() {
- // 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();
- }
- }
-
- self.composer().clone()
+ // TODO(phase-b): same shared-ownership refactor as require_composer.
+ todo!("try_composer pending Composer shared-ownership refactor")
}
fn set_composer(&mut self, composer: Composer) {
@@ -383,12 +369,13 @@ impl<C: HasBaseCommandData> BaseCommand for C {
let io_ptr: *const dyn IOInterface = self.get_io();
let io = unsafe { &*io_ptr };
+ let disable_plugins_kind = if disable_plugins {
+ crate::factory::DisablePlugins::All
+ } else {
+ crate::factory::DisablePlugins::None
+ };
let composer = if composer.is_none() {
- Some(Factory::create_global(
- io,
- Some(disable_plugins),
- Some(disable_scripts),
- )?)
+ Factory::create_global(io, disable_plugins_kind, disable_scripts)
} else {
composer
};
@@ -400,10 +387,10 @@ impl<C: HasBaseCommandData> BaseCommand for C {
input,
command_name,
);
- composer.get_event_dispatcher().dispatch(
- pre_command_run_event.get_name(),
- Box::new(pre_command_run_event),
- );
+ // TODO(phase-b): event_dispatcher.dispatch expects Option<Event>; need wrapper from
+ // PreCommandRunEvent.
+ let _ = composer.get_event_dispatcher();
+ let _ = pre_command_run_event.get_name();
}
if input.has_parameter_option(&["--no-ansi"], false) && input.has_option("no-progress") {
@@ -432,7 +419,7 @@ impl<C: HasBaseCommandData> BaseCommand for C {
for option_name in option_names {
if true == input.has_option(option_name) {
if false == input.get_option(option_name).as_bool().unwrap_or(false)
- && Platform::get_env(env_name).as_bool().unwrap_or(false)
+ && Platform::get_env(env_name).map_or(false, |s| !s.is_empty() && s != "0")
{
input.set_option(option_name, PhpMixed::Bool(true));
}
@@ -446,8 +433,7 @@ impl<C: HasBaseCommandData> BaseCommand for C {
.as_bool()
.unwrap_or(false)
&& Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQS")
- .as_bool()
- .unwrap_or(false)
+ .map_or(false, |s| !s.is_empty() && s != "0")
{
input.set_option("ignore-platform-reqs", PhpMixed::Bool(true));
@@ -463,12 +449,9 @@ impl<C: HasBaseCommandData> BaseCommand for C {
.unwrap_or(false))
{
let ignore_platform_req_env = Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQ");
- let ignore_str = ignore_platform_req_env
- .as_string()
- .unwrap_or("")
- .to_string();
+ let ignore_str = ignore_platform_req_env.clone().unwrap_or_default();
if 0 == count(&input.get_option("ignore-platform-req"))
- && is_string(&ignore_platform_req_env)
+ && ignore_platform_req_env.is_some()
&& "" != ignore_str
{
input.set_option(
@@ -500,14 +483,20 @@ impl<C: HasBaseCommandData> BaseCommand for C {
disable_plugins: bool,
disable_scripts: Option<bool>,
) -> Result<Composer> {
- 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 disable_plugins =
+ disable_plugins || input.has_parameter_option(&["--no-plugins"], false);
+ let disable_scripts = disable_scripts.unwrap_or(false)
+ || input.has_parameter_option(&["--no-scripts"], false);
// 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)
+ let disable_plugins_kind = if disable_plugins {
+ crate::factory::DisablePlugins::All
+ } else {
+ crate::factory::DisablePlugins::None
+ };
+ // TODO(phase-b): Option<IndexMap<String, PhpMixed>> -> Option<LocalConfigInput> conversion
+ let _ = config;
+ Factory::create(io, None, disable_plugins_kind, disable_scripts)
}
fn get_preferred_install_options(
@@ -619,7 +608,9 @@ impl<C: HasBaseCommandData> BaseCommand for C {
let ignores = input.get_option("ignore-platform-req");
if count(&ignores) > 0 {
- return Ok(PlatformRequirementFilterFactory::from_bool_or_list(ignores));
+ return Ok(PlatformRequirementFilterFactory::from_bool_or_list(
+ ignores,
+ )?);
}
Ok(PlatformRequirementFilterFactory::ignore_nothing())
@@ -713,7 +704,7 @@ impl<C: HasBaseCommandData> BaseCommand for C {
fn create_audit_config(
&self,
- config: &Config,
+ config: &mut Config,
input: &dyn InputInterface,
) -> Result<AuditConfig> {
// Handle both --audit and --no-audit flags
@@ -732,8 +723,7 @@ impl<C: HasBaseCommandData> BaseCommand for C {
let audit_config = AuditConfig::from_config(config, audit, &audit_format)?;
if Platform::get_env("COMPOSER_NO_SECURITY_BLOCKING")
- .as_bool()
- .unwrap_or(false)
+ .map_or(false, |s| !s.is_empty() && s != "0")
|| (input.has_option("no-security-blocking")
&& input
.get_option("no-security-blocking")
diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs
index 8b748b4..c63f63c 100644
--- a/crates/shirabe/src/command/base_config_command.rs
+++ b/crates/shirabe/src/command/base_config_command.rs
@@ -8,17 +8,19 @@ use crate::json::json_file::JsonFile;
use crate::util::platform::Platform;
use crate::util::silencer::Silencer;
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_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, chmod, touch};
pub trait BaseConfigCommand: BaseCommand {
- fn config(&self) -> Option<&Config>;
- fn config_mut(&mut self) -> Option<&mut Config>;
+ fn config(&self) -> Option<&std::rc::Rc<std::cell::RefCell<Config>>>;
+ fn config_mut(&mut self) -> &mut Option<std::rc::Rc<std::cell::RefCell<Config>>>;
fn config_file(&self) -> Option<&JsonFile>;
fn config_file_mut(&mut self) -> Option<&mut JsonFile>;
+ fn set_config_file(&mut self, file: Option<JsonFile>);
fn config_source(&self) -> Option<&JsonConfigSource>;
fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource>;
+ fn set_config_source(&mut self, source: Option<JsonConfigSource>);
fn initialize(
&mut self,
@@ -28,38 +30,47 @@ pub trait BaseConfigCommand: BaseCommand {
// 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() {
+ if input.get_option("global").as_bool().unwrap_or(false)
+ && !input.get_option("file").is_null()
+ {
return Err(anyhow::anyhow!("--file and --global can not be combined"));
}
let io = self.get_io();
- *self.config_mut() = Some(Factory::create_config(io)?);
- let config = self.config().as_mut().unwrap();
+ *self.config_mut() = Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_config(Some(&*io), None)?,
+ )));
+ let config_rc = std::rc::Rc::clone(self.config().unwrap());
// When using --global flag, set baseDir to home directory for correct absolute path resolution
- if input.get_option("global").as_bool() {
- let home = config.get("home").to_string();
- config.set_base_dir(home);
+ if input.get_option("global").as_bool().unwrap_or(false) {
+ let home = config_rc.borrow_mut().get("home").to_string();
+ config_rc.borrow_mut().set_base_dir(Some(home));
}
- let config_file = self.get_composer_config_file(input, config);
+ let config_file = self.get_composer_config_file(input, &*config_rc.borrow());
// 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(false)?).ok()
- == std::fs::canonicalize(config.get("home").to_string()).ok()
+ == std::fs::canonicalize(config_rc.borrow_mut().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_source_mut() =
- Some(JsonConfigSource::new(self.config_file().as_ref().unwrap()));
+ self.set_config_file(Some(JsonFile::new(
+ config_file.clone(),
+ None,
+ Some(io.clone_box()),
+ )?));
+ // TODO(phase-b): JsonConfigSource::new takes owned JsonFile, but PHP shares the same
+ // instance with $this->configFile. Needs Rc<RefCell<JsonFile>> refactor on both sides.
+ self.set_config_source(None);
// Initialize the global file if it's not there, ignoring any warnings or notices
- if input.get_option("global").as_bool() && !self.config_file().as_ref().unwrap().exists() {
+ if input.get_option("global").as_bool().unwrap_or(false)
+ && !self.config_file().as_ref().unwrap().exists()
+ {
let path = self.config_file().as_ref().unwrap().get_path().to_string();
touch(&path);
self.config_file_mut()
@@ -91,21 +102,21 @@ pub trait BaseConfigCommand: BaseCommand {
/// Get the local composer.json, global config.json, or the file passed by the user
fn get_composer_config_file(&self, input: &dyn InputInterface, config: &Config) -> String {
- if input.get_option("global").as_bool() {
+ if input.get_option("global").as_bool().unwrap_or(false) {
format!("{}/config.json", config.get("home"))
} else {
input
.get_option("file")
.as_string_opt()
.map(|s| s.to_string())
- .unwrap_or_else(|| Factory::get_composer_file())
+ .unwrap_or_else(|| Factory::get_composer_file().unwrap_or_default())
}
}
/// Get the local auth.json or global auth.json, or if the user passed in a file to use,
/// the corresponding auth.json
fn get_auth_config_file(&self, input: &dyn InputInterface, config: &Config) -> String {
- if input.get_option("global").as_bool() {
+ if input.get_option("global").as_bool().unwrap_or(false) {
format!("{}/auth.json", config.get("home"))
} else {
let composer_config = self.get_composer_config_file(input, config);
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index 5ff9c4a..b0dcf98 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -1,11 +1,11 @@
//! ref: composer/src/Composer/Command/BaseDependencyCommand.php
use indexmap::IndexMap;
+use shirabe_external_packages::symfony::component::console::formatter::output_formatter_style::OutputFormatterStyle;
+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::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;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use shirabe_php_shim::{InvalidArgumentException, UnexpectedValueException};
+use shirabe_php_shim::{InvalidArgumentException, PhpMixed, UnexpectedValueException};
use shirabe_semver::constraint::bound::Bound;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
@@ -31,7 +31,7 @@ pub trait BaseDependencyCommand: BaseCommand {
const OPTION_TREE: &'static str = "tree";
fn colors(&self) -> &[String];
- fn colors_mut(&mut self) -> &mut [String];
+ fn colors_mut(&mut self) -> &mut Vec<String>;
// TODO(phase-b): these wrappers existed to forward BaseCommand setters, but they
// shadowed the BaseCommand methods and caused ambiguity. Use BaseCommand directly.
@@ -63,9 +63,14 @@ pub trait BaseDependencyCommand: BaseCommand {
}
repos.push(Box::new(locker.get_locked_repository(true)?));
+ let platform_overrides: IndexMap<String, PhpMixed> = locker
+ .get_platform_overrides()?
+ .into_iter()
+ .map(|(k, v)| (k, PhpMixed::String(v)))
+ .collect();
repos.push(Box::new(PlatformRepository::new(
vec![],
- locker.get_platform_overrides(),
+ platform_overrides,
)?));
} else {
let local_repo = composer.get_repository_manager().get_local_repository();
@@ -82,10 +87,14 @@ pub trait BaseDependencyCommand: BaseCommand {
return Ok(1);
}
- repos.push(Box::new(local_repo));
+ // TODO(phase-b): InstalledRepositoryInterface is shared by reference (PHP class
+ // semantics); Box<dyn RepositoryInterface> requires owned upcast. Skipping local
+ // repo push until clone_box is exposed on InstalledRepositoryInterface.
+ let _ = local_repo;
let platform_overrides = composer
.get_config()
+ .borrow()
.get("platform")
.as_array()
.cloned()
@@ -95,7 +104,7 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(Box::new(PlatformRepository::new(vec![], IndexMap::new())?));
}
- let mut installed_repo = InstalledRepository::new(repos)?;
+ let mut installed_repo = InstalledRepository::new(repos);
let needle = input
.get_argument(Self::ARGUMENT_PACKAGE)
@@ -112,8 +121,7 @@ pub trait BaseDependencyCommand: BaseCommand {
"*".to_string()
};
- let packages =
- installed_repo.find_packages_with_replacers_and_providers(needle.clone(), None);
+ let packages = installed_repo.find_packages_with_replacers_and_providers(&needle, None);
if packages.is_empty() {
return Err(anyhow::anyhow!(InvalidArgumentException {
message: format!("Could not find package \"{}\" in your project", needle),
@@ -122,22 +130,22 @@ pub trait BaseDependencyCommand: BaseCommand {
}
let matched_package = installed_repo.find_package(
- needle.clone(),
+ &needle,
FindPackageConstraint::String(text_constraint.clone()),
);
if matched_package.is_none() {
let default_repos = CompositeRepository::new(RepositoryFactory::default_repos(
Some(self.get_io()),
- Some(composer.get_config()),
+ Some(std::rc::Rc::clone(composer.get_config())),
Some(&mut composer.get_repository_manager()),
)?);
if let Some(r#match) = default_repos.find_package(
- needle.clone(),
+ &needle,
FindPackageConstraint::String(text_constraint.clone()),
) {
- installed_repo.add_repository(Box::new(InstalledArrayRepository::new(vec![
- r#match.clone_box(),
- ])))?;
+ installed_repo.add_repository(Box::new(
+ InstalledArrayRepository::new_with_packages(vec![r#match.clone_box()])?,
+ ))?;
} else if PlatformRepository::is_platform_package(&needle) {
let parser = VersionParser::new();
let platform_constraint = parser.parse_constraints(&text_constraint)?;
@@ -147,9 +155,11 @@ pub trait BaseDependencyCommand: BaseCommand {
.get_version()
.to_string();
let temp_platform_pkg = Package::new(needle.clone(), version.clone(), version);
- installed_repo.add_repository(Box::new(InstalledArrayRepository::new(
- vec![Box::new(temp_platform_pkg)],
- )))?;
+ installed_repo.add_repository(Box::new(
+ InstalledArrayRepository::new_with_packages(vec![Box::new(
+ temp_platform_pkg,
+ )])?,
+ ))?;
}
} else {
self.get_io().write_error(&format!(
@@ -201,9 +211,13 @@ pub trait BaseDependencyCommand: BaseCommand {
}
let has_constraint = text_constraint != "*";
- let constraint = if has_constraint {
+ let constraint: Option<Box<dyn ConstraintInterface>> = if has_constraint {
let version_parser = VersionParser::new();
- Some(version_parser.parse_constraints(&text_constraint)?)
+ Some(
+ version_parser
+ .parse_constraints(&text_constraint)?
+ .clone_box(),
+ )
} else {
None
};
@@ -339,7 +353,19 @@ pub trait BaseDependencyCommand: BaseCommand {
new_table.extend(table);
table = new_table;
}
- self.render_table(table, output);
+ // TODO(phase-b): render_table expects Vec<PhpMixed>; build PhpMixed cells once a
+ // converter exists for Vec<String> rows.
+ let table_as_mixed: Vec<PhpMixed> = table
+ .into_iter()
+ .map(|row| {
+ PhpMixed::List(
+ row.into_iter()
+ .map(|s| Box::new(PhpMixed::String(s)))
+ .collect(),
+ )
+ })
+ .collect();
+ self.render_table(table_as_mixed, output);
}
fn init_styles(&mut self, output: &dyn OutputInterface) {
@@ -350,9 +376,11 @@ pub trait BaseDependencyCommand: BaseCommand {
"magenta".to_string(),
"blue".to_string(),
];
- for color in &self.colors() {
- let style = OutputFormatterStyle::new(color.clone());
- output.get_formatter().set_style(color, style);
+ for color in self.colors() {
+ // TODO(phase-b): output.get_formatter() returns &OutputFormatter; set_style needs
+ // &mut. Need interior mutability or `get_formatter_mut`.
+ let _ = OutputFormatterStyle::new(Some(color), None, None);
+ let _ = output.get_formatter();
}
}
diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs
index 6b8ca1a..fb32979 100644
--- a/crates/shirabe/src/command/bump_command.rs
+++ b/crates/shirabe/src/command/bump_command.rs
@@ -4,8 +4,8 @@ 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::console::input::input_interface::InputInterface;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+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, file_get_contents, file_put_contents, is_writable, strtolower};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -37,16 +37,10 @@ impl BumpCommand {
self
.set_name("bump")
.set_description("Increases the lower limit of your composer.json requirements to the currently installed versions")
- .set_definition(vec![
- InputArgument::new(
- "packages",
- Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
- "Optional package name(s) to restrict which packages are bumped.",
- None,
- ),
- 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_definition (&[
+ InputArgument::new("packages",Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),"Optional package name(s) to restrict which packages are bumped.",None,).unwrap().into(),InputOption::new("dev-only", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require-dev\".", None).unwrap().into(),
+ InputOption::new("no-dev-only", Some(PhpMixed::String("R".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require\".", None).unwrap().into(),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the packages to bump, but will not execute anything.", None).unwrap().into(),
])
.set_help(
"The <info>bump</info> command increases the lower limit of your composer.json requirements\n\
@@ -136,23 +130,34 @@ impl BumpCommand {
}
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_error3(
+ let has_lock_file_disabled = !composer.get_config().borrow().has("lock")
+ || composer
+ .get_config()
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(true);
+ let repo: Box<dyn crate::repository::repository_interface::RepositoryInterface> =
+ if !has_lock_file_disabled {
+ Box::new(composer.get_locker().get_locked_repository(true)?)
+ } else if composer.get_locker().is_locked() {
+ if !composer.get_locker().is_fresh()? {
+ 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,
);
- return Ok(Self::ERROR_LOCK_OUTDATED);
- }
- composer.get_locker().get_locked_repository(true)?
- } else {
- composer.get_repository_manager().get_local_repository()
- };
+ return Ok(Self::ERROR_LOCK_OUTDATED);
+ }
+ Box::new(composer.get_locker().get_locked_repository(true)?)
+ } else {
+ // TODO(phase-b): get_local_repository returns &dyn InstalledRepositoryInterface;
+ // cloning into an owned Box requires clone_box on that trait.
+ composer
+ .get_repository_manager()
+ .get_local_repository()
+ .clone_box()
+ };
if composer.get_package().get_type() != "project" && !dev_only {
io.write_error3(
@@ -162,7 +167,10 @@ impl BumpCommand {
);
let contents_data = composer_json.read()?;
- if !contents_data.contains_key("type") {
+ if !contents_data
+ .as_array()
+ .map_or(false, |m| m.contains_key("type"))
+ {
io.write_error3(
"If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".",
true,
@@ -192,7 +200,7 @@ impl BumpCommand {
let packages_filter: Vec<String> = packages_filter
.iter()
.map(|constraint| {
- Preg::replace(r"{[:= ].+}", "", constraint.clone())
+ Preg::replace(r"{[:= ].+}", "", constraint)
.unwrap_or_else(|_| constraint.clone())
})
.collect();
@@ -218,20 +226,25 @@ impl BumpCommand {
if PlatformRepository::is_platform_package(pkg_name) {
continue;
}
- let current_constraint = link.get_pretty_constraint();
+ let current_constraint = link.get_pretty_constraint()?;
- let package_opt = repo.find_package(pkg_name, "*");
- let package = match package_opt {
+ let package_opt = repo.find_package(
+ pkg_name,
+ crate::repository::repository_interface::FindPackageConstraint::String(
+ "*".to_string(),
+ ),
+ );
+ let mut package = match package_opt {
None => continue,
Some(p) => p,
};
- let mut package = package;
while let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
- package = alias.get_alias_of();
+ // TODO(phase-b): get_alias_of returns &dyn BasePackage; cloning into Box
+ // requires clone_box on BasePackage applied to a borrowed ref.
+ package = alias.get_alias_of().clone_box();
}
- let bumped =
- bumper.bump_requirement(link.get_constraint().as_ref(), package.as_ref())?;
+ let bumped = bumper.bump_requirement(link.get_constraint(), package.as_ref())?;
if bumped == current_constraint {
continue;
@@ -245,62 +258,64 @@ impl BumpCommand {
}
if !dry_run && !self.update_file_cleanly(&composer_json, &updates)? {
- let mut composer_definition = composer_json.read()?;
+ let mut composer_definition = match composer_json.read()? {
+ PhpMixed::Array(m) => m,
+ _ => indexmap::IndexMap::new(),
+ };
for (key, packages) in &updates {
for (package, version) in packages {
- composer_definition
+ let section = composer_definition
.entry(key.to_string())
- .or_insert_with(indexmap::IndexMap::new)
- .insert(package.clone(), version.clone());
+ .or_insert_with(|| Box::new(PhpMixed::Array(indexmap::IndexMap::new())));
+ if let PhpMixed::Array(map) = section.as_mut() {
+ map.insert(package.clone(), Box::new(PhpMixed::String(version.clone())));
+ }
}
}
- composer_json.write(composer_definition)?;
+ composer_json.write(PhpMixed::Array(composer_definition))?;
}
let change_count: usize = updates.values().map(|m| m.len()).sum();
if change_count > 0 {
if dry_run {
- io.write3(
- &format!("<info>{} would be updated with:</info>", composer_json_path),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "<info>{} would be updated with:</info>",
+ composer_json_path
+ ));
for (require_type, packages) in &updates {
for (package, version) in packages {
- io.write3(
- &format!("<info> - {}.{}: {}</info>", require_type, package, version),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "<info> - {}.{}: {}</info>",
+ require_type, package, version
+ ));
}
}
} else {
- io.write3(
- &format!(
- "<info>{} has been updated ({} changes).</info>",
- composer_json_path, change_count
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "<info>{} has been updated ({} changes).</info>",
+ composer_json_path, change_count
+ ));
}
} else {
- io.write3(
- &format!(
- "<info>No requirements to update in {}.</info>",
- composer_json_path
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "<info>No requirements to update in {}.</info>",
+ composer_json_path
+ ));
}
if !dry_run
&& composer.get_locker().is_locked()
- && composer.get_config().get("lock").as_bool().unwrap_or(true)
+ && composer
+ .get_config()
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(true)
&& change_count > 0
{
- composer.get_locker().update_hash(&composer_json)?;
+ composer
+ .get_locker()
+ .update_hash(&composer_json, None::<fn(_) -> _>)?;
}
if dry_run && change_count > 0 {
@@ -330,7 +345,7 @@ impl BumpCommand {
for (key, packages) in updates {
for (package, version) in packages {
- if !manipulator.add_link(key, package, version)? {
+ if !manipulator.add_link(key, package, version, false)? {
return Ok(false);
}
}
diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs
index ab44aa2..dae4ec0 100644
--- a/crates/shirabe/src/command/check_platform_reqs_command.rs
+++ b/crates/shirabe/src/command/check_platform_reqs_command.rs
@@ -2,10 +2,11 @@
use anyhow::Result;
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_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, strip_tags};
use shirabe_semver::constraint::constraint::Constraint;
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
@@ -36,10 +37,10 @@ impl CheckPlatformReqsCommand {
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),
- 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_definition(&[
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables checking of require-dev packages requirements.", None).unwrap().into(),
+ InputOption::new("lock", None, Some(InputOption::VALUE_NONE), "Checks requirements only from the lock file, not from installed packages.", None).unwrap().into(),
+ 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()))).unwrap().into(),
])
.set_help(
"Checks that your PHP and extensions versions match the platform requirements of the installed packages.\n\n\
@@ -61,12 +62,14 @@ impl CheckPlatformReqsCommand {
let mut requires: IndexMap<String, Vec<Link>> = IndexMap::new();
let mut remove_packages: Vec<String> = vec![];
- let installed_repo_base = if input.get_option("lock").as_bool().unwrap_or(false) {
+ let installed_repo_base: Box<
+ dyn crate::repository::repository_interface::RepositoryInterface,
+ > = if input.get_option("lock").as_bool().unwrap_or(false) {
io.write_error(&format!(
"<info>Checking {}platform requirements using the lock file</info>",
if no_dev { "non-dev " } else { "" }
));
- composer.get_locker().get_locked_repository(!no_dev)?
+ Box::new(composer.get_locker().get_locked_repository(!no_dev)?)
} else {
let local_repo = composer.get_repository_manager().get_local_repository();
if local_repo.get_packages().is_empty() {
@@ -74,7 +77,8 @@ impl CheckPlatformReqsCommand {
"<warning>No vendor dir present, checking {}platform requirements from the lock file</warning>",
if no_dev { "non-dev " } else { "" }
));
- composer.get_locker().get_locked_repository(!no_dev)?
+ Box::new(composer.get_locker().get_locked_repository(!no_dev)?)
+ as Box<dyn crate::repository::repository_interface::RepositoryInterface>
} else {
if no_dev {
remove_packages = local_repo.get_dev_package_names().clone();
@@ -126,28 +130,31 @@ impl CheckPlatformReqsCommand {
'requirements: for (require, links) in &requires_sorted {
if PlatformRepository::is_platform_package(require) {
let candidates = installed_repo_with_platform
- .find_packages_with_replacers_and_providers(require);
+ .find_packages_with_replacers_and_providers(require, None);
if !candidates.is_empty() {
let mut req_results: Vec<CheckResult> = vec![];
'candidates: for candidate in &candidates {
- let candidate_constraint = if candidate.get_name() == require {
- let mut c = Constraint::new("=", candidate.get_version());
- c.set_pretty_string(candidate.get_pretty_version());
- Some(c)
- } else {
- let mut found = None;
- for link in candidate
- .get_provides()
- .iter()
- .chain(candidate.get_replaces().iter())
- {
- if link.get_target() == require {
- found = Some(link.get_constraint().clone_box());
- break;
+ let candidate_constraint: Option<Box<dyn ConstraintInterface>> =
+ if candidate.get_name() == require {
+ let mut c = Constraint::new("=", candidate.get_version());
+ c.set_pretty_string(Some(
+ candidate.get_pretty_version().to_string(),
+ ));
+ Some(Box::new(c))
+ } else {
+ let mut found: Option<Box<dyn ConstraintInterface>> = None;
+ for (_, link) in candidate
+ .get_provides()
+ .iter()
+ .chain(candidate.get_replaces().iter())
+ {
+ if link.get_target() == require {
+ found = Some(link.get_constraint().clone_box());
+ break;
+ }
}
- }
- found.map(|c| Constraint::from_constraint_interface(c))
- };
+ found
+ };
let candidate_constraint = match candidate_constraint {
Some(c) => c,
@@ -155,7 +162,7 @@ impl CheckPlatformReqsCommand {
};
for link in links {
- if !link.get_constraint().matches(&candidate_constraint) {
+ if !link.get_constraint().matches(&*candidate_constraint) {
req_results.push(CheckResult {
platform_package: if candidate.get_name() == require {
candidate.get_pretty_name().to_string()
@@ -290,13 +297,13 @@ impl CheckPlatformReqsCommand {
448,
));
} else {
- let rows: Vec<Vec<PhpMixed>> = results
+ let rows: Vec<PhpMixed> = results
.iter()
.map(|result| {
- vec![
- PhpMixed::String(result.platform_package.clone()),
- PhpMixed::String(result.version.clone()),
- if let Some(link) = &result.link {
+ PhpMixed::List(vec![
+ Box::new(PhpMixed::String(result.platform_package.clone())),
+ Box::new(PhpMixed::String(result.version.clone())),
+ Box::new(if let Some(link) = &result.link {
PhpMixed::String(format!(
"{} {} {} ({})",
link.get_source(),
@@ -306,13 +313,13 @@ impl CheckPlatformReqsCommand {
))
} else {
PhpMixed::String(String::new())
- },
- PhpMixed::String(
+ }),
+ Box::new(PhpMixed::String(
format!("{} {}", result.status, result.provider)
.trim_end()
.to_string(),
- ),
- ]
+ )),
+ ])
})
.collect();
diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs
index 63f1a66..c7d6f55 100644
--- a/crates/shirabe/src/command/clear_cache_command.rs
+++ b/crates/shirabe/src/command/clear_cache_command.rs
@@ -4,8 +4,8 @@ use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandD
use crate::composer::Composer;
use crate::factory::Factory;
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_external_packages::symfony::component::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct ClearCacheCommand {
diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs
index d4f64f0..bef9d47 100644
--- a/crates/shirabe/src/command/config_command.rs
+++ b/crates/shirabe/src/command/config_command.rs
@@ -3,9 +3,9 @@
use crate::io::io_interface;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use crate::console::input::input_option::InputOption;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
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;
use shirabe_php_shim::{
ArrayObject, InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE,
@@ -37,7 +37,7 @@ use shirabe_semver::version_parser::VersionParser;
pub struct ConfigCommand {
base_command_data: BaseCommandData,
- config: Option<Config>,
+ config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
config_file: Option<JsonFile>,
config_source: Option<JsonConfigSource>,
@@ -64,24 +64,22 @@ impl ConfigCommand {
pub(crate) fn configure(&mut self) {
// TODO(cli-completion): suggest_setting_keys() for `setting-key` argument
- self
- .inner
- .set_name("config")
+ self.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),
- 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_definition(&[
+ InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None).unwrap().into(),
+ InputOption::new("editor", Some(PhpMixed::String("e".to_string())), Some(InputOption::VALUE_NONE), "Open editor", None).unwrap().into(),
+ InputOption::new("auth", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None).unwrap().into(),
+ InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None).unwrap().into(),
+ InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "List configuration settings", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None).unwrap().into(),
+ InputOption::new("json", Some(PhpMixed::String("j".to_string())), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None).unwrap().into(),
+ InputOption::new("merge", Some(PhpMixed::String("m".to_string())), 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None).unwrap().into(),
+ InputArgument::new("setting-key", None, "Setting key", None).unwrap().into(),
+ InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None).unwrap().into(),
])
.set_help(
"This command allows you to edit composer config settings and repositories\n\
@@ -127,15 +125,17 @@ impl ConfigCommand {
) -> anyhow::Result<()> {
self.initialize(input, output)?;
- let auth_config_file = self
- .inner
- .get_auth_config_file(input, self.config.as_ref().unwrap());
+ let auth_config_file =
+ self.get_auth_config_file(input, &*self.config.as_ref().unwrap().borrow());
- 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,
- ));
+ self.auth_config_file = Some(JsonFile::new(
+ auth_config_file,
+ None,
+ Some(self.get_io().clone_box()),
+ )?);
+ // TODO(phase-b): JsonConfigSource::new takes owned JsonFile (PHP sharing semantics).
+ // Skipping auth_config_source assignment until Rc<RefCell<JsonFile>> refactor lands.
+ self.auth_config_source = None;
// Initialize the global file if it's not there, ignoring any warnings or notices
if input.get_option("global").as_bool() == Some(true)
@@ -188,7 +188,10 @@ impl ConfigCommand {
editor = Some("notepad".to_string());
} else {
for candidate in &["editor", "vim", "vi", "nano", "pico", "ed"] {
- if !exec(&format!("which {}", candidate)).is_empty() {
+ if !exec(&format!("which {}", candidate), None, None)
+ .unwrap_or_default()
+ .is_empty()
+ {
editor = Some(candidate.to_string());
break;
}
@@ -207,22 +210,25 @@ impl ConfigCommand {
} else {
self.config_file.as_ref().unwrap().get_path().to_string()
};
- system(&format!(
- "{} {}{}",
- editor.unwrap_or_default(),
- file,
- if Platform::is_windows() {
- ""
- } else {
- " > `tty`"
- }
- ));
+ system(
+ &format!(
+ "{} {}{}",
+ editor.unwrap_or_default(),
+ file,
+ if Platform::is_windows() {
+ ""
+ } else {
+ " > `tty`"
+ }
+ ),
+ None,
+ );
return Ok(0);
}
if input.get_option("global").as_bool() != Some(true) {
- self.config.as_mut().unwrap().merge(
+ self.config.as_mut().unwrap().borrow_mut().merge(
self.config_file.as_ref().unwrap().read()?,
self.config_file.as_ref().unwrap().get_path(),
);
@@ -233,21 +239,20 @@ impl ConfigCommand {
};
let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
wrap.insert("config".to_string(), Box::new(auth_data));
- self.config.as_mut().unwrap().merge(
+ self.config.as_mut().unwrap().borrow_mut().merge(
PhpMixed::Array(wrap),
self.auth_config_file.as_ref().unwrap().get_path(),
);
}
- self.inner
- .get_io()
- .load_configuration(self.config.as_ref().unwrap());
+ self.get_io()
+ .load_configuration(&mut *self.config.as_ref().unwrap().borrow_mut())?;
// List the configuration of the file settings
if input.get_option("list").as_bool() == Some(true) {
self.list_configuration(
- self.config.as_ref().unwrap().all(),
- self.config.as_ref().unwrap().raw(),
+ self.config.as_ref().unwrap().borrow_mut().all(0)?,
+ self.config.as_ref().unwrap().borrow().raw(),
output,
None,
input.get_option("source").as_bool() == Some(true),
@@ -297,31 +302,33 @@ impl ConfigCommand {
properties_defaults.insert("suggest".to_string(), PhpMixed::List(vec![]));
properties_defaults.insert("extra".to_string(), PhpMixed::List(vec![]));
let raw_data = self.config_file.as_ref().unwrap().read()?;
- let mut data = self.config.as_ref().unwrap().all();
+ let mut data = self.config.as_ref().unwrap().borrow_mut().all(0)?;
let mut source = self
- .inner
.config
.as_ref()
.unwrap()
.get_source_of_value(&setting_key);
let mut value: PhpMixed;
- let mut matches: Vec<String> = vec![];
- if Preg::is_match(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^repos?(?:itories)?(?:\\.(.+))?/",
&setting_key,
Some(&mut matches),
)
.unwrap_or(false)
{
- if matches.get(1).is_none() {
+ if matches.get(&CaptureKey::ByIndex(1)).is_none() {
value = data
.as_array()
.and_then(|a| a.get("repositories"))
.map(|v| (**v).clone())
.unwrap_or_else(|| PhpMixed::Array(IndexMap::new()));
} else {
- let repo_key = matches[1].clone();
+ let repo_key = matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
let repos = data
.as_array()
.and_then(|a| a.get("repositories"))
@@ -385,7 +392,7 @@ impl ConfigCommand {
.map(|c| c.contains_key(&setting_key))
.unwrap_or(false)
{
- value = self.config.as_ref().unwrap().get_with_flags(
+ value = self.config.as_ref().unwrap().borrow_mut().get_with_flags(
&setting_key,
if input.get_option("absolute").as_bool() == Some(true) {
0
@@ -396,8 +403,10 @@ impl ConfigCommand {
// ensure we get {} output for properties which are objects
if value.as_array().map(|a| a.is_empty()).unwrap_or(false) {
let schema = JsonFile::parse_json(
- &file_get_contents(JsonFile::COMPOSER_SCHEMA_PATH).unwrap_or_default(),
- "composer.schema.json",
+ Some(
+ &file_get_contents(JsonFile::COMPOSER_SCHEMA_PATH).unwrap_or_default(),
+ ),
+ Some("composer.schema.json"),
)?;
let type_value = schema
.as_array()
@@ -439,13 +448,7 @@ impl ConfigCommand {
&& in_array(setting_key.as_str(), &properties, true)
{
value = (**raw_data.as_array().unwrap().get(&setting_key).unwrap()).clone();
- source = self
- .inner
- .config_file
- .as_ref()
- .unwrap()
- .get_path()
- .to_string();
+ source = self.config_file.as_ref().unwrap().get_path().to_string();
} else if let Some(v) = properties_defaults.get(&setting_key) {
value = v.clone();
source = "defaults".to_string();
@@ -458,7 +461,7 @@ impl ConfigCommand {
}
let value_str = if is_array(&value) || is_object(&value) || is_bool(&value) {
- JsonFile::encode(&value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)?
+ JsonFile::encode(&value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
} else {
value.as_string().unwrap_or("").to_string()
};
@@ -468,7 +471,7 @@ impl ConfigCommand {
source_of_config_value = format!(" ({})", source);
}
- self.get_io().write(
+ self.get_io().write3(
&format!("{}{}", value_str, source_of_config_value),
true,
io_interface::QUIET,
@@ -516,7 +519,6 @@ impl ConfigCommand {
{
if setting_key == "disable-tls"
&& self
- .inner
.config
.as_ref()
.unwrap()
@@ -547,8 +549,8 @@ impl ConfigCommand {
return Ok(0);
}
// handle preferred-install per-package config
- let mut matches: Vec<String> = vec![];
- if Preg::is_match(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^preferred-install\\.(.+)/",
&setting_key,
Some(&mut matches),
@@ -588,8 +590,8 @@ impl ConfigCommand {
}
// handle allow-plugins config setting elements true or false to add/remove
- let mut matches: Vec<String> = vec![];
- if Preg::is_match(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"{^allow-plugins\\.([a-zA-Z0-9/*-]+)}",
&setting_key,
Some(&mut matches),
@@ -660,8 +662,8 @@ impl ConfigCommand {
}
// handle repositories
- let mut matches: Vec<String> = vec![];
- if Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^repos?(?:itories)?\\.(.+)/",
&setting_key,
Some(&mut matches),
@@ -712,7 +714,7 @@ impl ConfigCommand {
return Ok(0);
}
} else {
- let value = JsonFile::parse_json(&values[0], "composer.json")?;
+ let value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?;
self.config_source.as_mut().unwrap().add_repository(
&matches[1],
value,
@@ -731,8 +733,8 @@ impl ConfigCommand {
}
// handle extra
- let mut matches: Vec<String> = vec![];
- if Preg::is_match("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
self.config_source
.as_mut()
@@ -744,7 +746,7 @@ impl ConfigCommand {
let mut value = PhpMixed::String(values[0].clone());
if input.get_option("json").as_bool() == Some(true) {
- value = JsonFile::parse_json(&values[0], "composer.json")?;
+ value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?;
if input.get_option("merge").as_bool() == Some(true) {
let current_value_outer = self.config_file.as_ref().unwrap().read()?;
let bits = explode(".", &setting_key);
@@ -787,8 +789,8 @@ impl ConfigCommand {
}
// handle suggest
- let mut matches: Vec<String> = vec![];
- if Preg::is_match("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
self.config_source
.as_mut()
@@ -822,8 +824,9 @@ impl ConfigCommand {
}
// handle platform
- let mut matches: Vec<String> = vec![];
- if Preg::is_match("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false)
+ {
if input.get_option("unset").as_bool() == Some(true) {
self.config_source
.as_mut()
@@ -881,7 +884,7 @@ impl ConfigCommand {
.collect(),
);
if input.get_option("json").as_bool() == Some(true) {
- value = JsonFile::parse_json(&values[0], "composer.json")?;
+ value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?;
if !is_array(&value) {
return Err(RuntimeException {
message: format!("Expected an array or object for {}", setting_key),
@@ -942,12 +945,8 @@ impl ConfigCommand {
}
// handle auth
- let mut matches: Vec<String> = vec![];
- if Preg::is_match(
- "/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|custom-headers|bearer|forgejo-token)\\.(.+)/",
- &setting_key,
- Some(&mut matches),
- ).unwrap_or(false) {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3("/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|custom-headers|bearer|forgejo-token)\\.(.+)/", &setting_key, Some(&mut matches)).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.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
@@ -1023,8 +1022,8 @@ impl ConfigCommand {
}
// Check if the header is in correct "Name: Value" format
- let mut header_parts: Vec<String> = vec![];
- if !Preg::is_match("/^[^:]+:\\s*.+$/", header, Some(&mut header_parts)).unwrap_or(false) {
+ let mut header_parts: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match3("/^[^:]+:\\s*.+$/", header, Some(&mut header_parts)).unwrap_or(false) {
return Err(RuntimeException {
message: format!("Header \"{}\" is not in \"Header-Name: Header-Value\" format", header),
code: 0,
@@ -1056,8 +1055,8 @@ impl ConfigCommand {
}
// handle script
- let mut matches: Vec<String> = vec![];
- if Preg::is_match("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
self.config_source
.as_mut()
@@ -1143,7 +1142,6 @@ impl ConfigCommand {
if key == "disable-tls" {
if !normalized_value.as_bool().unwrap_or(false)
&& self
- .inner
.config
.as_ref()
.unwrap()
@@ -1156,7 +1154,6 @@ impl ConfigCommand {
);
} else if normalized_value.as_bool().unwrap_or(false)
&& !self
- .inner
.config
.as_ref()
.unwrap()
@@ -1252,7 +1249,9 @@ impl ConfigCommand {
|| (key == "repositories" && k.is_none()))
{
let mut new_k = k.clone().unwrap_or_default();
- new_k.push_str(&Preg::replace("{^config\\.}", "", &format!("{}.", key)));
+ new_k.push_str(
+ &Preg::replace("{^config\\.}", "", &format!("{}.", key)).unwrap_or_default(),
+ );
k = Some(new_k);
self.list_configuration(value_inner, raw_val, output, k.clone(), show_source);
k = orig_k.clone();
@@ -1285,11 +1284,11 @@ impl ConfigCommand {
let source = if show_source {
format!(
" ({})",
- self.config.as_ref().unwrap().get_source_of_value(&format!(
- "{}{}",
- k.clone().unwrap_or_default(),
- key
- ))
+ self.config
+ .as_ref()
+ .unwrap()
+ .borrow_mut()
+ .get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key))
)
} else {
String::new()
@@ -1304,13 +1303,14 @@ impl ConfigCommand {
} else {
k.clone().unwrap()
};
- let id = Preg::replace("{\\..*$}", "", &id_source);
+ let id = Preg::replace("{\\..*$}", "", &id_source).unwrap_or_default();
let id = Preg::replace(
"{[^a-z0-9]}i",
"-",
&strtolower(&shirabe_php_shim::trim(&id, " \t\n\r\0\u{0B}")),
- );
- let id = Preg::replace("{-+}", "-", &id);
+ )
+ .unwrap_or_default();
+ let id = Preg::replace("{-+}", "-", &id).unwrap_or_default();
link = format!("https://getcomposer.org/doc/06-config.md#{}", id);
}
if is_string(&raw_val)
@@ -1320,7 +1320,7 @@ impl ConfigCommand {
.unwrap_or_default()
!= value_display
{
- io.write(
+ io.write3(
&format!(
"[<fg=yellow;href={}>{}{}</>] <info>{} ({})</info>{}",
link,
@@ -1334,7 +1334,7 @@ impl ConfigCommand {
io_interface::QUIET,
);
} else {
- io.write(
+ io.write3(
&format!(
"[<fg=yellow;href={}>{}{}</>] <info>{}</info>{}",
link,
@@ -1531,7 +1531,7 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)>
(
Box::new(|val| {
PhpMixed::Bool(
- Preg::is_match(
+ Preg::is_match3(
"/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i",
val.as_string().unwrap_or(""),
None,
@@ -2008,12 +2008,12 @@ fn key_first_key(value: &PhpMixed) -> Option<String> {
}
impl BaseConfigCommand for ConfigCommand {
- fn config(&self) -> Option<&Config> {
+ fn config(&self) -> Option<&std::rc::Rc<std::cell::RefCell<Config>>> {
self.config.as_ref()
}
- fn config_mut(&mut self) -> Option<&mut Config> {
- self.config.as_mut()
+ fn config_mut(&mut self) -> &mut Option<std::rc::Rc<std::cell::RefCell<Config>>> {
+ &mut self.config
}
fn config_file(&self) -> Option<&JsonFile> {
@@ -2024,6 +2024,10 @@ impl BaseConfigCommand for ConfigCommand {
self.config_file.as_mut()
}
+ fn set_config_file(&mut self, file: Option<JsonFile>) {
+ self.config_file = file;
+ }
+
fn config_source(&self) -> Option<&JsonConfigSource> {
self.config_source.as_ref()
}
@@ -2031,6 +2035,10 @@ impl BaseConfigCommand for ConfigCommand {
fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource> {
self.config_source.as_mut()
}
+
+ fn set_config_source(&mut self, source: Option<JsonConfigSource>) {
+ self.config_source = source;
+ }
}
impl HasBaseCommandData for ConfigCommand {
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index cc637ce..bd4a92a 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -1,7 +1,8 @@
//! ref: composer/src/Composer/Command/CreateProjectCommand.php
use anyhow::Result;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
@@ -60,32 +61,32 @@ impl CreateProjectCommand {
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),
- 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_definition(&[
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None).unwrap().into(),
+ InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None).unwrap().into(),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None).unwrap().into(),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None).unwrap().into(),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables installation of require-dev packages (enabled by default, only present for BC).", None).unwrap().into(),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None).unwrap().into(),
+ InputOption::new("no-custom-installers", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Use no-plugins instead.", None).unwrap().into(),
+ InputOption::new("no-scripts", None, Some(InputOption::VALUE_NONE), "Whether to prevent execution of all defined scripts in the root package.", None).unwrap().into(),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("keep-vcs", None, Some(InputOption::VALUE_NONE), "Whether to prevent deleting the vcs folder.", None).unwrap().into(),
+ InputOption::new("remove-vcs", None, Some(InputOption::VALUE_NONE), "Whether to force deletion of the vcs folder without prompting.", None).unwrap().into(),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Whether to skip installation of the package dependencies.", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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()))).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into(),
+ InputOption::new("ask", None, Some(InputOption::VALUE_NONE), "Whether to ask for project directory.", None).unwrap().into(),
])
.set_help(
"The <info>create-project</info> command creates a new project from a given\n\
@@ -111,12 +112,11 @@ impl CreateProjectCommand {
input: &mut dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let config = Factory::create_config(None, None)?;
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?));
let io = self.get_io();
- let (prefer_source, prefer_dist) = self
- .inner
- .get_preferred_install_options(&config, input, true)?;
+ let (prefer_source, prefer_dist) =
+ self.get_preferred_install_options(&config, input, true)?;
if input.get_option("dev").as_bool().unwrap_or(false) {
io.write_error("<warning>You are using the deprecated option \"dev\". Dev packages are installed by default now.</warning>");
@@ -207,7 +207,7 @@ impl CreateProjectCommand {
pub fn install_project(
&mut self,
io: &dyn IOInterface,
- mut config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
input: &dyn InputInterface,
package_name: Option<String>,
directory: Option<String>,
@@ -246,7 +246,7 @@ impl CreateProjectCommand {
.unwrap_or_else(PlatformRequirementFilterFactory::ignore_nothing);
// we need to manually load the configuration to pass the auth credentials to the io interface!
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
self.suggested_packages_reporter = Some(SuggestedPackagesReporter::new(io));
@@ -254,7 +254,7 @@ impl CreateProjectCommand {
self.install_root_package(
input,
io,
- &mut config,
+ &config,
package_name,
&*platform_requirement_filter,
directory.clone(),
@@ -291,7 +291,7 @@ impl CreateProjectCommand {
true,
)?;
let composer_json_repositories_config =
- composer.get_config().get_repositories();
+ composer.get_config().borrow().get_repositories();
let name = RepositoryFactory::generate_repository_name(
PhpMixed::Int(index as i64),
&repo_config,
@@ -331,7 +331,11 @@ impl CreateProjectCommand {
}
}
- let process = composer.get_loop().borrow().get_process_executor().cloned();
+ let process = composer
+ .get_loop()
+ .borrow()
+ .get_process_executor()
+ .map(std::rc::Rc::clone);
let fs = Filesystem::new(process);
// dispatch event
@@ -341,10 +345,8 @@ impl CreateProjectCommand {
);
// use the new config including the newly installed project
- let config = composer.get_config();
- let (ps, pd) = self
- .inner
- .get_preferred_install_options(config, input, false)?;
+ let config = std::rc::Rc::clone(composer.get_config());
+ let (ps, pd) = self.get_preferred_install_options(&*config.borrow(), input, false)?;
prefer_source = ps;
prefer_dist = pd;
@@ -364,19 +366,28 @@ impl CreateProjectCommand {
self.suggested_packages_reporter.as_ref().unwrap().clone(),
)
.set_optimize_autoloader(
- config.get("optimize-autoloader").as_bool().unwrap_or(false),
+ config
+ .borrow_mut()
+ .get("optimize-autoloader")
+ .as_bool()
+ .unwrap_or(false),
)
.set_class_map_authoritative(
config
+ .borrow_mut()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false),
)
.set_apcu_autoloader(
- config.get("apcu-autoloader").as_bool().unwrap_or(false),
+ config
+ .borrow_mut()
+ .get("apcu-autoloader")
+ .as_bool()
+ .unwrap_or(false),
None,
)
- .set_audit_config(self.create_audit_config(config, input)?);
+ .set_audit_config(self.create_audit_config(&mut *config.borrow_mut(), input)?);
if !composer.get_locker().is_locked() {
installer.set_update(true);
@@ -446,7 +457,7 @@ impl CreateProjectCommand {
drop(finder);
let mut had_error: Option<anyhow::Error> = None;
for dir in &dirs {
- if !fs.remove_directory(dir, false)? {
+ if !fs.remove_directory(dir)? {
had_error = Some(
RuntimeException {
message: format!("Could not remove {}", dir),
@@ -510,7 +521,7 @@ impl CreateProjectCommand {
&self,
input: &dyn InputInterface,
io: &dyn IOInterface,
- config: &mut Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
package_name: &str,
platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
directory: Option<String>,
@@ -552,7 +563,9 @@ impl CreateProjectCommand {
};
directory = rtrim(&directory, Some("/\\"));
- let process = ProcessExecutor::new(Some(Box::new(io)), None);
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ Box::new(io),
+ ))));
let fs = Filesystem::new(Some(process));
if !fs.is_absolute_path(&directory) {
directory = format!(
@@ -571,22 +584,16 @@ impl CreateProjectCommand {
}
// set the base dir to ensure $config->all() below resolves the correct absolute paths to vendor-dir etc
- config.set_base_dir(&directory);
+ config.borrow_mut().set_base_dir(Some(directory.clone()));
if !secure_http {
let mut merge_map: indexmap::IndexMap<String, PhpMixed> = indexmap::IndexMap::new();
let mut inner_map: indexmap::IndexMap<String, Box<PhpMixed>> =
indexmap::IndexMap::new();
inner_map.insert("secure-http".to_string(), Box::new(PhpMixed::Bool(false)));
merge_map.insert("config".to_string(), PhpMixed::Array(inner_map));
- config.merge(
- PhpMixed::Array(
- merge_map
- .into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ),
- Some(Config::SOURCE_COMMAND.to_string()),
- );
+ config
+ .borrow_mut()
+ .merge(&merge_map, Config::SOURCE_COMMAND);
}
io.write_error(&format!(
@@ -606,7 +613,7 @@ impl CreateProjectCommand {
}
.into());
}
- if !fs.is_dir_empty(&directory)? {
+ if !fs.is_dir_empty(&directory) {
return Err(InvalidArgumentException {
message: format!("Project directory \"{}\" is not empty.", directory),
code: 0,
@@ -618,20 +625,34 @@ impl CreateProjectCommand {
if stability.is_none() {
if package_version.is_none() {
stability = Some("stable".to_string());
- } else if let Some(matched) = Preg::is_match_strict_groups(
- &format!(
- "{{^[^,\\s]*?@({})$}}i",
- implode(
- "|",
- &STABILITIES
- .keys()
- .map(|k| k.to_string())
- .collect::<Vec<_>>()
- )
- ),
- package_version.as_deref().unwrap_or(""),
- ) {
- stability = Some(matched.get(1).cloned().unwrap_or_default());
+ } else if {
+ let mut matched: IndexMap<CaptureKey, String> = IndexMap::new();
+ let ok = Preg::is_match_strict_groups3(
+ &format!(
+ "{{^[^,\\s]*?@({})$}}i",
+ implode(
+ "|",
+ &STABILITIES
+ .keys()
+ .map(|k| k.to_string())
+ .collect::<Vec<_>>()
+ )
+ ),
+ package_version.as_deref().unwrap_or(""),
+ Some(&mut matched),
+ )
+ .unwrap_or(false);
+ if ok {
+ stability = Some(
+ matched
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ );
+ }
+ ok
+ } {
+ // stability already set above
} else {
stability = Some(VersionParser::parse_stability(
package_version.as_deref().unwrap_or(""),
@@ -662,24 +683,28 @@ impl CreateProjectCommand {
let composer = self.create_composer_instance(
input,
io,
- Some(config.all()),
+ Some(config.borrow_mut().all(0)?),
disable_plugins,
Some(disable_scripts),
)?;
let config = composer.get_config();
// set the base dir here again on the new config instance, as otherwise in case the vendor dir is defined in an env var for example it would still override the value set above by $config->all()
- config.set_base_dir(&directory);
+ config.borrow_mut().set_base_dir(Some(directory.clone()));
let rm = composer.get_repository_manager();
let mut repository_set = RepositorySet::new(&stability);
if repositories.is_none() {
repository_set.add_repository(Box::new(CompositeRepository::new(
- RepositoryFactory::default_repos(Some(io), Some(config), Some(rm))?,
+ RepositoryFactory::default_repos(
+ Some(io),
+ Some(std::rc::Rc::clone(&config)),
+ Some(rm),
+ )?,
)));
} else {
for repo in repositories.unwrap() {
let mut repo_config =
- RepositoryFactory::config_from_string(io, config, repo, true)?;
+ RepositoryFactory::config_from_string(io, &config, repo, true)?;
let is_packagist_disabled = (repo_config.contains_key("packagist")
&& repo_config.len() == 1
&& repo_config.get("packagist").and_then(|v| v.as_bool()) == Some(false))
@@ -712,14 +737,14 @@ impl CreateProjectCommand {
repository_set.add_repository(RepositoryFactory::create_repo(
io,
- config,
- &repo_config,
+ &config,
+ repo_config.clone(),
Some(rm),
)?);
}
}
- let platform_overrides = config.get("platform");
+ let platform_overrides = config.borrow_mut().get("platform");
let platform_repo = PlatformRepository::new(
vec![],
match platform_overrides {
@@ -799,7 +824,7 @@ impl CreateProjectCommand {
// TODO(phase-b): self.get_io().write_error(...) inside the closure
let _ = &signal;
let fs = Filesystem::new(None);
- fs.remove_directory(&real_dir_clone, false).ok();
+ fs.remove_directory(&real_dir_clone).ok();
handler.exit_with_last_signal();
}),
));
@@ -843,7 +868,7 @@ impl CreateProjectCommand {
im.set_output_progress(!no_progress);
im.add_installer(Box::new(project_installer));
im.execute(
- Box::new(InstalledArrayRepository::new(vec![])),
+ Box::new(InstalledArrayRepository::new()?),
vec![Box::new(InstallOperation::new(package.clone()))],
)?;
im.notify_installs(io);
diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs
index 23b1d58..1cfcc04 100644
--- a/crates/shirabe/src/command/depends_command.rs
+++ b/crates/shirabe/src/command/depends_command.rs
@@ -5,8 +5,8 @@ use crate::command::base_dependency_command::BaseDependencyCommand;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct DependsCommand {
@@ -21,34 +21,42 @@ impl DependsCommand {
self.set_name("depends")
.set_aliases(&["why".to_string()])
.set_description("Shows which packages cause the given package to be installed")
- .set_definition(vec![
+ .set_definition(&[
InputArgument::new(
- BaseDependencyCommand::ARGUMENT_PACKAGE,
+ <Self as BaseDependencyCommand>::ARGUMENT_PACKAGE,
Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
- BaseDependencyCommand::OPTION_RECURSIVE,
+ <Self as BaseDependencyCommand>::OPTION_RECURSIVE,
Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
- BaseDependencyCommand::OPTION_TREE,
+ <Self as BaseDependencyCommand>::OPTION_TREE,
Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"locked",
None,
Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"Displays detailed information about where a package is referenced.\n\n\
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index 32ed378..49b926d 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
@@ -54,8 +54,8 @@ use crate::util::process_executor::ProcessExecutor;
pub struct DiagnoseCommand {
base_command_data: BaseCommandData,
- pub(crate) http_downloader: Option<HttpDownloader>,
- pub(crate) process: Option<ProcessExecutor>,
+ pub(crate) http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
+ pub(crate) process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
pub(crate) exit_code: i64,
}
@@ -79,11 +79,11 @@ impl DiagnoseCommand {
let composer = self.try_composer(None, None);
let io = self.get_io();
- let config: Config;
+ let config: std::rc::Rc<std::cell::RefCell<Config>>;
if let Some(ref c) = composer {
config = c.get_config().clone();
- let command_event = CommandEvent::new(
+ let command_event = CommandEvent::new6(
PluginEvents::COMMAND,
"diagnose",
input,
@@ -97,13 +97,19 @@ impl DiagnoseCommand {
c.get_loop()
.borrow()
.get_process_executor()
- .cloned()
- .unwrap_or_else(|| ProcessExecutor::new(Some(io.clone_box()))),
+ .map(std::rc::Rc::clone)
+ .unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ io.clone_box(),
+ ))))
+ }),
);
} else {
- config = Factory::create_config(None)?;
+ config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?));
- self.process = Some(ProcessExecutor::new(Some(io.clone_box())));
+ self.process = Some(std::rc::Rc::new(std::cell::RefCell::new(
+ ProcessExecutor::new(Some(io.clone_box())),
+ )));
}
let mut config_inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
@@ -111,14 +117,20 @@ impl DiagnoseCommand {
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(&secure_http_wrap, Config::SOURCE_COMMAND);
- config.prohibit_url_by_config("http://repo.packagist.org", &NullIO::new());
+ config
+ .borrow_mut()
+ .merge(&secure_http_wrap, Config::SOURCE_COMMAND);
+ config
+ .borrow_mut()
+ .prohibit_url_by_config("http://repo.packagist.org", &NullIO::new());
- self.http_downloader = Some(Factory::create_http_downloader(io, &config)?);
+ self.http_downloader = Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, &config, indexmap::IndexMap::new())?,
+ )));
if strpos(file!(), "phar:") == Some(0) {
io.write_no_newline("Checking pubkeys: ");
- let r = self.check_pub_keys(&config);
+ let r = self.check_pub_keys(&*config.borrow());
self.output_result(r);
io.write_no_newline("Checking Composer version: ");
@@ -132,10 +144,11 @@ impl DiagnoseCommand {
));
io.write_no_newline("Checking Composer and its dependencies for vulnerabilities: ");
- let r = self.check_composer_audit(&config)?;
+ let r = self.check_composer_audit(&*config.borrow())?;
self.output_result(r);
let platform_overrides = config
+ .borrow_mut()
.get("platform")
.as_array()
.cloned()
@@ -241,14 +254,14 @@ impl DiagnoseCommand {
self.output_result(PhpMixed::String(r));
io.write_no_newline("Checking http connectivity to packagist: ");
- let r = self.check_http("http", &config)?;
+ let r = self.check_http("http", &*config.borrow())?;
self.output_result(r);
io.write_no_newline("Checking https connectivity to packagist: ");
- let r = self.check_http("https", &config)?;
+ let r = self.check_http("https", &*config.borrow())?;
self.output_result(r);
- for repo in config.get_repositories() {
+ for repo in config.borrow().get_repositories() {
let repo_arr = repo.as_array().cloned().unwrap_or_default();
if repo_arr.get("type").and_then(|v| v.as_string()) == Some("composer")
&& repo_arr.get("url").is_some()
@@ -256,7 +269,7 @@ impl DiagnoseCommand {
let composer_repo = ComposerRepository::new(
PhpMixed::Array(repo_arr.clone()),
self.get_io().clone_box(),
- config.clone(),
+ &*config.borrow(),
self.http_downloader.clone().unwrap(),
);
// PHP: ReflectionMethod($composerRepo, 'getPackagesJsonUrl')
@@ -276,13 +289,13 @@ impl DiagnoseCommand {
.and_then(|v| v.as_string())
.unwrap_or("")
));
- let r = self.check_composer_repo(&url, &config)?;
+ let r = self.check_composer_repo(&url, &*config.borrow())?;
self.output_result(r);
}
}
let proxy_manager = ProxyManager::get_instance();
- let protos: Vec<&str> = if config.get("disable-tls").as_bool() == Some(true) {
+ let protos: Vec<&str> = if config.borrow_mut().get("disable-tls").as_bool() == Some(true) {
vec!["http"]
} else {
vec!["http", "https"]
@@ -319,6 +332,7 @@ impl DiagnoseCommand {
}
let oauth = config
+ .borrow_mut()
.get("github-oauth")
.as_array()
.cloned()
@@ -372,7 +386,7 @@ impl DiagnoseCommand {
}
io.write_no_newline("Checking disk free space: ");
- let r = self.check_disk_space(&config);
+ let r = self.check_disk_space(&*config.borrow());
self.output_result(r);
Ok(self.exit_code)
@@ -403,7 +417,7 @@ impl DiagnoseCommand {
fn check_composer_lock_schema(&self, locker: &Locker) -> anyhow::Result<PhpMixed> {
let json = locker.get_json_file();
- match json.validate_schema(JsonFile::LOCK_SCHEMA) {
+ match json.validate_schema(JsonFile::LOCK_SCHEMA, None) {
Ok(_) => {}
Err(e) => {
if let Some(jve) = e.downcast_ref::<JsonValidationException>() {
@@ -427,7 +441,7 @@ impl DiagnoseCommand {
}
let mut output = String::new();
- self.process.as_mut().unwrap().execute(
+ self.process.as_mut().unwrap().borrow_mut().execute(
&vec![
"git".to_string(),
"config".to_string(),
@@ -467,15 +481,15 @@ impl DiagnoseCommand {
tls_warning = Some("<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>".to_string());
}
- match self.http_downloader.as_mut().unwrap().get(
+ match self.http_downloader.as_ref().unwrap().borrow_mut().get(
&format!("{}://repo.packagist.org/packages.json", proto),
IndexMap::new(),
) {
Ok(_) => {}
Err(e) => {
if let Some(te) = e.downcast_ref::<TransportException>() {
- let hints = HttpDownloader::get_exception_hints(te);
- if !hints.is_empty() && count(&hints) > 0 {
+ let hints = HttpDownloader::get_exception_hints(te).unwrap_or_default();
+ if !hints.is_empty() {
for hint in hints {
result_list.push(Box::new(PhpMixed::String(hint)));
}
@@ -517,15 +531,16 @@ impl DiagnoseCommand {
match self
.http_downloader
- .as_mut()
+ .as_ref()
.unwrap()
+ .borrow_mut()
.get(url, IndexMap::new())
{
Ok(_) => {}
Err(e) => {
if let Some(te) = e.downcast_ref::<TransportException>() {
- let hints = HttpDownloader::get_exception_hints(te);
- if !hints.is_empty() && count(&hints) > 0 {
+ let hints = HttpDownloader::get_exception_hints(te).unwrap_or_default();
+ if !hints.is_empty() {
for hint in hints {
result_list.push(Box::new(PhpMixed::String(hint)));
}
@@ -574,16 +589,22 @@ impl DiagnoseCommand {
let json = self
.http_downloader
- .as_mut()
+ .as_ref()
.unwrap()
+ .borrow_mut()
.get(
&format!("{}://repo.packagist.org/packages.json", protocol),
IndexMap::new(),
)?
.decode_json()?;
if let Some(provider_includes) = json.as_array().and_then(|a| a.get("provider-includes")) {
- let mut hash_val = reset(&provider_includes.as_array().cloned().unwrap_or_default());
- hash_val = hash_val
+ let provider_includes_arr = provider_includes.as_array().cloned().unwrap_or_default();
+ let first = provider_includes_arr
+ .values()
+ .next()
+ .map(|v| (**v).clone())
+ .unwrap_or(PhpMixed::Null);
+ let hash_val = first
.as_array()
.and_then(|a| a.get("sha256"))
.map(|v| (**v).clone())
@@ -596,8 +617,9 @@ impl DiagnoseCommand {
);
let provider = self
.http_downloader
- .as_mut()
+ .as_ref()
.unwrap()
+ .borrow_mut()
.get(
&format!("{}://repo.packagist.org/{}", protocol, path),
IndexMap::new(),
@@ -641,7 +663,13 @@ impl DiagnoseCommand {
Box::new(PhpMixed::Bool(false)),
);
- match self.http_downloader.as_mut().unwrap().get(&url, opts) {
+ match self
+ .http_downloader
+ .as_ref()
+ .unwrap()
+ .borrow_mut()
+ .get(&url, opts)
+ {
Ok(response) => {
let expiration = response.get_header("github-authentication-token-expiration");
@@ -704,8 +732,9 @@ impl DiagnoseCommand {
);
let data = self
.http_downloader
- .as_mut()
+ .as_ref()
.unwrap()
+ .borrow_mut()
.get(&url, opts)?
.decode_json()?;
@@ -790,15 +819,27 @@ impl DiagnoseCommand {
}
}
- fn check_version(&mut self, config: &Config) -> anyhow::Result<PhpMixed> {
+ fn check_version(
+ &mut self,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
+ ) -> anyhow::Result<PhpMixed> {
let result = self.check_connectivity_and_composer_network_http_enablement();
if result.as_bool() != Some(true) {
return Ok(result);
}
- let versions_util = Versions::new(config.clone(), self.http_downloader.clone().unwrap());
+ let versions_util = Versions::new(
+ std::rc::Rc::clone(config),
+ self.http_downloader.clone().unwrap(),
+ );
let latest = match versions_util.get_latest(None) {
- Ok(l) => l,
+ Ok(Ok(l)) => l,
+ Ok(Err(e)) => {
+ return Ok(PhpMixed::String(format!(
+ "<error>[{}] {}</error>",
+ "UnexpectedValueException", e.message
+ )));
+ }
Err(e) => {
return Ok(PhpMixed::String(format!(
"<error>[{}] {}</error>",
@@ -809,8 +850,7 @@ impl DiagnoseCommand {
};
let latest_version = latest
- .as_array()
- .and_then(|a| a.get("version"))
+ .get("version")
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
@@ -832,7 +872,7 @@ impl DiagnoseCommand {
return Ok(result);
}
- let auditor = Auditor::new();
+ let auditor = Auditor;
let mut repo_set = RepositorySet::new(
"stable".to_string(),
IndexMap::new(),
@@ -920,7 +960,7 @@ impl DiagnoseCommand {
}
let version = curl_version();
- let version_arr = version.as_array().cloned().unwrap_or_default();
+ let version_arr = version.unwrap_or_default();
let libz_version = version_arr
.get("libz_version")
.and_then(|v| v.as_string())
@@ -1102,16 +1142,20 @@ impl DiagnoseCommand {
ob_start();
phpinfo(INFO_GENERAL);
let phpinfo_str = ob_get_clean();
- let mut phpinfo_match: Vec<String> = vec![];
+ let mut phpinfo_match: IndexMap<CaptureKey, String> = IndexMap::new();
if phpinfo_str.is_some()
- && Preg::is_match_strict_groups(
+ && Preg::is_match_strict_groups3(
"{Configure Command(?: *</td><td class=\"v\">| *=> *)(.*?)(?:</td>|$)}m",
phpinfo_str.as_ref().unwrap(),
Some(&mut phpinfo_match),
)
.unwrap_or(false)
{
- let configure = &phpinfo_match[1];
+ let configure = phpinfo_match
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ let configure = configure.as_str();
if str_contains(configure, "--enable-sigchild") {
warnings.insert("sigchild".to_string(), PhpMixed::Bool(true));
@@ -1251,9 +1295,11 @@ impl DiagnoseCommand {
),
"openssl_version" => {
// Attempt to parse version number out, fallback to whole string value.
- let openssl_trimmed =
- trim(&strstr(OPENSSL_VERSION_TEXT, " ", false), " \t\n\r\0\u{0B}");
- let mut openssl_version = strstr(&openssl_trimmed, " ", true);
+ let openssl_trimmed = trim(
+ &strstr(OPENSSL_VERSION_TEXT, " ").unwrap_or_default(),
+ " \t\n\r\0\u{0B}",
+ );
+ let mut openssl_version = strstr(&openssl_trimmed, " ").unwrap_or_default();
if openssl_version.is_empty() {
openssl_version = OPENSSL_VERSION_TEXT.to_string();
}
diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs
index 9389132..7322322 100644
--- a/crates/shirabe/src/command/dump_autoload_command.rs
+++ b/crates/shirabe/src/command/dump_autoload_command.rs
@@ -1,8 +1,8 @@
//! ref: composer/src/Composer/Command/DumpAutoloadCommand.php
use anyhow::Result;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, file_exists};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -23,18 +23,18 @@ impl DumpAutoloadCommand {
.set_name("dump-autoload")
.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),
- 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_definition(&[
+ 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).unwrap().into(),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize`.", None).unwrap().into(),
+ InputOption::new("apcu", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None).unwrap().into(),
+ InputOption::new("apcu-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu", None).unwrap().into(),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything.", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
])
.set_help(
"<info>php composer.phar dump-autoload</info>\n\n\
@@ -46,14 +46,8 @@ impl DumpAutoloadCommand {
let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "dump-autoload".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event =
+ CommandEvent::new(PluginEvents::COMMAND, "dump-autoload", input, output);
composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None);
@@ -74,12 +68,17 @@ impl DumpAutoloadCommand {
}
let optimize = input.get_option("optimize").as_bool().unwrap_or(false)
- || config.get("optimize-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("optimize-autoloader")
+ .as_bool()
+ .unwrap_or(false);
let authoritative = input
.get_option("classmap-authoritative")
.as_bool()
.unwrap_or(false)
|| config
+ .borrow_mut()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false);
@@ -89,7 +88,11 @@ impl DumpAutoloadCommand {
.map(|s| s.to_string());
let apcu = apcu_prefix.is_some()
|| input.get_option("apcu").as_bool().unwrap_or(false)
- || config.get("apcu-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("apcu-autoloader")
+ .as_bool()
+ .unwrap_or(false);
if input.get_option("strict-psr").as_bool().unwrap_or(false) && !optimize && !authoritative
{
@@ -148,7 +151,7 @@ impl DumpAutoloadCommand {
generator.set_apcu(apcu, apcu_prefix.as_deref());
generator.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?);
let class_map = generator.dump(
- config,
+ &*config.borrow(),
&local_repo,
package,
installation_manager,
@@ -185,7 +188,7 @@ impl DumpAutoloadCommand {
.get_option("strict-ambiguous")
.as_bool()
.unwrap_or(false)
- && !class_map.get_ambiguous_classes(false).is_empty()
+ && !class_map.get_ambiguous_classes(false)?.is_empty()
{
return Ok(2);
}
diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs
index 7f339b6..59d2290 100644
--- a/crates/shirabe/src/command/exec_command.rs
+++ b/crates/shirabe/src/command/exec_command.rs
@@ -1,8 +1,8 @@
//! ref: composer/src/Composer/Command/ExecCommand.php
use anyhow::Result;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, RuntimeException, basename, chdir, getcwd, glob};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -21,21 +21,17 @@ impl ExecCommand {
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),
+ .set_definition(&[
+ InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None).unwrap().into(),
// 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,
- ),
- InputArgument::new(
- "args",
- Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
- "Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments",
- None,
- ),
+ InputArgument::new("binary",
+ Some(InputArgument::OPTIONAL),
+ "The binary to run, e.g. phpunit",
+ None,).unwrap().into(),
+ InputArgument::new("args",
+ Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
+ "Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments",
+ None,).unwrap().into(),
])
.set_help(
"Executes a vendored binary/script.\n\n\
@@ -44,7 +40,7 @@ impl ExecCommand {
}
pub fn interact(
- &self,
+ &mut self,
input: &mut dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<()> {
@@ -80,11 +76,11 @@ impl ExecCommand {
}
pub fn execute(
- &self,
+ &mut self,
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.require_composer(None, None)?;
+ let mut 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()
@@ -92,7 +88,8 @@ impl ExecCommand {
let bins = self.get_binaries(true)?;
if bins.is_empty() {
let bin_dir = composer
- .get_config()
+ .get_config_mut()
+ .borrow_mut()
.get("bin-dir")
.as_string()
.unwrap_or("")
@@ -126,14 +123,11 @@ impl ExecCommand {
// TODO(phase-b): add_listener takes a Callable; wiring binary as callable not yet ported
let _ = (dispatcher, &binary);
- let initial_working_directory = self.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 {
- message: format!(
- "Could not switch back to working directory \"{}\"",
- iwd.display()
- ),
+ message: format!("Could not switch back to working directory \"{}\"", iwd),
code: 0,
})?;
}
@@ -149,13 +143,14 @@ impl ExecCommand {
})
.unwrap_or_default();
- Ok(dispatcher.dispatch_script("__exec_command", true, args)?)
+ Ok(dispatcher.dispatch_script("__exec_command", true, args, indexmap::IndexMap::new())?)
}
- fn get_binaries(&self, for_display: bool) -> Result<Vec<String>> {
- let composer = self.require_composer(None, None)?;
+ fn get_binaries(&mut self, for_display: bool) -> Result<Vec<String>> {
+ let mut composer = self.require_composer(None, None)?;
let bin_dir = composer
- .get_config()
+ .get_config_mut()
+ .borrow_mut()
.get("bin-dir")
.as_string()
.unwrap_or("")
diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs
index 73a7078..18b62c4 100644
--- a/crates/shirabe/src/command/fund_command.rs
+++ b/crates/shirabe/src/command/fund_command.rs
@@ -5,9 +5,9 @@ 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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
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;
@@ -20,7 +20,9 @@ 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::package::package_interface::PackageInterface;
use crate::repository::composite_repository::CompositeRepository;
+use crate::repository::repository_interface::RepositoryInterface;
#[derive(Debug)]
pub struct FundCommand {
@@ -31,13 +33,15 @@ impl FundCommand {
pub fn configure(&mut self) {
self.set_name("fund")
.set_description("Discover how to help fund the maintenance of your dependencies")
- .set_definition(vec![InputOption::new(
+ .set_definition(&[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())),
- )]);
+ )
+ .unwrap()
+ .into()]);
}
pub fn execute(
@@ -54,10 +58,7 @@ impl FundCommand {
let mut packages_to_load: IndexMap<String, Box<MatchAllConstraint>> = IndexMap::new();
for package in repo.get_packages() {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
continue;
}
packages_to_load.insert(
@@ -75,14 +76,9 @@ impl FundCommand {
// collect funding data from default branches
for package in &result.packages {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_none()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_none() {
// TODO: check for CompleteAliasPackage as well
- if let Some(complete_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
- {
+ if let Some(complete_pkg) = package.as_any().downcast_ref::<CompletePackage>() {
if complete_pkg.is_default_branch()
&& !complete_pkg.get_funding().is_empty()
&& packages_to_load.contains_key(complete_pkg.get_name())
@@ -96,17 +92,13 @@ impl FundCommand {
// collect funding from installed packages if none was found in the default branch above
for package in repo.get_packages() {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
+ if package.as_any().downcast_ref::<AliasPackage>().is_some()
|| !packages_to_load.contains_key(package.get_name())
{
continue;
}
// TODO: check for CompleteAliasPackage as well
- if let Some(complete_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
- {
+ if let Some(complete_pkg) = package.as_any().downcast_ref::<CompletePackage>() {
if !complete_pkg.get_funding().is_empty() {
Self::insert_funding_data(&mut fundings, complete_pkg)?;
}
@@ -158,7 +150,8 @@ impl FundCommand {
);
io.write("Thank you!");
} else if format == "json" {
- io.write(&JsonFile::encode(&fundings, 448));
+ let fundings_mixed: PhpMixed = fundings.clone().into();
+ io.write(&JsonFile::encode(&fundings_mixed, 448));
} else {
io.write("No funding links were found in your package dependencies. This doesn't mean they don't need your support!");
}
diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs
index 5acceb3..9be6b43 100644
--- a/crates/shirabe/src/command/global_command.rs
+++ b/crates/shirabe/src/command/global_command.rs
@@ -4,9 +4,9 @@ use std::path::Path;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-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_external_packages::symfony::component::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::input::string_input::StringInput;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{LogicException, RuntimeException, chdir};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -28,14 +28,18 @@ impl GlobalCommand {
pub fn configure(&mut self) {
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),
+ .set_definition(&[
+ InputArgument::new("command-name", Some(InputArgument::REQUIRED), "", None)
+ .unwrap()
+ .into(),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"Use this command as a wrapper to run other Composer commands\n\
@@ -52,8 +56,8 @@ impl GlobalCommand {
);
}
- pub fn run(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let tokens = Preg::split(r"{\s+}", &input.to_string())?;
+ pub fn run(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
+ let tokens = Preg::split(r"{\s+}", &input.to_input_string())?;
let mut args: Vec<String> = vec![];
for token in &tokens {
if !token.is_empty() && !token.starts_with('-') {
@@ -69,11 +73,12 @@ impl GlobalCommand {
}
let sub_input = self.prepare_subcommand_input(input, false)?;
- Ok(self.get_application().run(&sub_input, output)?)
+ let mut app = self.get_application()?;
+ Ok(app.run(Some(&sub_input), Some(output))?)
}
fn prepare_subcommand_input(
- &self,
+ &mut self,
input: &dyn InputInterface,
quiet: bool,
) -> Result<StringInput> {
@@ -81,11 +86,11 @@ impl GlobalCommand {
Platform::clear_env("COMPOSER");
}
- let config = Factory::create_config(None, None)?;
+ let mut config = Factory::create_config(None, None)?;
let home = config.get("home").as_string().unwrap_or("").to_string();
if !Path::new(&home).is_dir() {
- let fs = Filesystem::new(None);
+ let mut fs = Filesystem::new(None);
fs.ensure_directory_exists(&home)?;
if !Path::new(&home).is_dir() {
return Err(RuntimeException {
@@ -96,7 +101,7 @@ impl GlobalCommand {
}
}
- chdir(&home).map_err(|e| RuntimeException {
+ chdir(&home).map_err(|_e| RuntimeException {
message: format!("Could not switch to home directory \"{}\"", home),
code: 0,
})?;
@@ -108,15 +113,15 @@ impl GlobalCommand {
));
}
- let new_input_str = Preg::replace(
+ let new_input_str = Preg::replace4(
r"{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}",
"",
- &input.to_string(),
+ &input.to_input_string(),
1,
)?;
- self.get_application().reset_composer();
+ self.get_application()?.reset_composer();
- Ok(StringInput::new(new_input_str))
+ Ok(StringInput::new(&new_input_str))
}
pub fn is_proxy_command(&self) -> bool {
diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs
index 9b92707..4f6fab3 100644
--- a/crates/shirabe/src/command/home_command.rs
+++ b/crates/shirabe/src/command/home_command.rs
@@ -1,9 +1,9 @@
//! ref: composer/src/Composer/Command/HomeCommand.php
use anyhow::Result;
-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 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::{FILTER_VALIDATE_URL, PhpMixed, filter_var};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
@@ -11,6 +11,8 @@ use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use crate::package::complete_package_interface::CompletePackageInterface;
+use crate::package::package_interface::PackageInterface;
+use crate::package::root_package_interface::RootPackageInterface;
use crate::repository::repository_factory::RepositoryFactory;
use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::root_package_repository::RootPackageRepository;
@@ -28,27 +30,33 @@ impl HomeCommand {
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![
+ .set_definition(&[
InputArgument::new(
"packages",
Some(InputArgument::IS_ARRAY),
"Package(s) to browse to.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"homepage",
Some(shirabe_php_shim::PhpMixed::String("H".to_string())),
Some(InputOption::VALUE_NONE),
"Open the homepage instead of the repository URL.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"show",
Some(shirabe_php_shim::PhpMixed::String("s".to_string())),
Some(InputOption::VALUE_NONE),
"Only show the homepage or repository URL.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"The home command opens or shows a package's repository URL or\n\
@@ -60,7 +68,7 @@ impl HomeCommand {
}
pub fn execute(
- &self,
+ &mut self,
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
@@ -98,7 +106,7 @@ impl HomeCommand {
let mut package_exists = false;
'repos: for repo in &repos {
- for package in repo.find_packages(package_name) {
+ for package in repo.find_packages(&package_name, None) {
package_exists = true;
if let Some(complete_pkg) = package.as_complete_package_interface() {
if self.handle_package(complete_pkg, show_homepage, show_only) {
@@ -132,16 +140,15 @@ impl HomeCommand {
}
fn handle_package(
- &self,
+ &mut self,
package: &dyn CompletePackageInterface,
show_homepage: bool,
show_only: bool,
) -> bool {
let support = package.get_support();
- let mut url = support
+ let mut url: Option<String> = support
.get("source")
- .and_then(|v| v.as_string())
- .map(|s| s.to_string())
+ .cloned()
.or_else(|| package.get_source_url().map(|s| s.to_string()));
if url.as_deref().map_or(true, |s| s.is_empty()) || show_homepage {
url = package.get_homepage().map(|s| s.to_string());
@@ -166,49 +173,49 @@ impl HomeCommand {
true
}
- fn open_browser(&self, url: &str) {
- let io = self.get_io();
- let mut process = ProcessExecutor::new(io);
+ fn open_browser(&mut self, url: &str) {
+ let mut process = ProcessExecutor::new(Some(self.get_io().clone_box()));
if Platform::is_windows() {
- process.execute(&["start", "\"web\"", "explorer", url], None);
+ let _ = process.execute(
+ PhpMixed::from(vec!["start", "\"web\"", "explorer", url]),
+ None,
+ None,
+ );
return;
}
- let linux = process.execute(&["which", "xdg-open"], None);
- let osx = process.execute(&["which", "open"], None);
+ let linux = process
+ .execute(PhpMixed::from(vec!["which", "xdg-open"]), None, None)
+ .unwrap_or(1);
+ let osx = process
+ .execute(PhpMixed::from(vec!["which", "open"]), None, None)
+ .unwrap_or(1);
if linux == 0 {
- process.execute(&["xdg-open", url], None);
+ let _ = process.execute(PhpMixed::from(vec!["xdg-open", url]), None, None);
} else if osx == 0 {
- process.execute(&["open", url], None);
+ let _ = process.execute(PhpMixed::from(vec!["open", url]), None, None);
} else {
- io.write_error(&format!(
+ self.get_io().write_error(&format!(
"No suitable browser opening command found, open yourself: {}",
url
));
}
}
- fn initialize_repos(&self) -> Result<Vec<Box<dyn RepositoryInterface>>> {
+ fn initialize_repos(&mut self) -> Result<Vec<Box<dyn RepositoryInterface>>> {
let composer = self.try_composer(None, None);
if let Some(composer) = composer {
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone_package(),
+ composer.get_package().clone_box(),
)));
- repos.push(Box::new(
- composer.get_repository_manager().get_local_repository(),
- ));
- for repo in composer.get_repository_manager().get_repositories() {
- repos.push(repo);
- }
+ // TODO(phase-b): get_local_repository / get_repositories return shared refs; needs Rc<dyn ...> migration
return Ok(repos);
}
- Ok(RepositoryFactory::default_repos_with_default_manager(
- self.get_io(),
- ))
+ RepositoryFactory::default_repos_with_default_manager(self.get_io())
}
}
diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs
index 04928ed..1f8f595 100644
--- a/crates/shirabe/src/command/init_command.rs
+++ b/crates/shirabe/src/command/init_command.rs
@@ -3,7 +3,7 @@
use crate::io::io_interface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses;
use shirabe_external_packages::symfony::component::console::helper::formatter_helper::FormatterHelper;
use shirabe_external_packages::symfony::component::console::input::array_input::ArrayInput;
@@ -25,7 +25,7 @@ use crate::factory::Factory;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::json::json_validation_exception::JsonValidationException;
-use crate::package::base_package::BasePackage;
+use crate::package::base_package::{self, BasePackage};
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_factory::RepositoryFactory;
@@ -86,18 +86,18 @@ impl InitCommand {
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),
- 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_definition(&[
+ InputOption::new("name", None, Some(InputOption::VALUE_REQUIRED), "Name of the package", None).unwrap().into(),
+ InputOption::new("description", None, Some(InputOption::VALUE_REQUIRED), "Description of package", None).unwrap().into(),
+ InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None).unwrap().into(),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None).unwrap().into(),
+ InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &base_package::STABILITIES.keys().map(|k| k.to_string()).collect::<Vec<_>>())), None).unwrap().into(),
+ InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None).unwrap().into(),
+ InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None).unwrap().into(),
+ 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).unwrap().into(),
])
.set_help(
"The <info>init</info> command creates a basic composer.json file\n\
@@ -188,10 +188,12 @@ impl InitCommand {
})
.unwrap_or_default();
if (repositories.len() as i64) > 0 {
- let config = Factory::create_config(Some(io), None)?;
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
+ Some(io),
+ None,
+ )?));
for repo in &repositories {
- let repo_config =
- RepositoryFactory::config_from_string(io, &config, repo, Some(true))?;
+ let repo_config = RepositoryFactory::config_from_string(io, &config, repo, true)?;
let entry = options
.entry("repositories".to_string())
.or_insert_with(|| PhpMixed::List(vec![]));
@@ -294,7 +296,7 @@ impl InitCommand {
let json = JsonFile::encode(&options_for_encode, 448);
if input.is_interactive() {
- io.write_error(
+ io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(String::new())),
Box::new(PhpMixed::String(json)),
@@ -307,17 +309,13 @@ impl InitCommand {
"Do you confirm generation [<comment>yes</comment>]? ".to_string(),
true,
) {
- io.write_error(
- PhpMixed::String("<error>Command aborted</error>".to_string()),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3("<error>Command aborted</error>", true, io_interface::NORMAL);
return Ok(1);
}
} else {
- io.write_error(
- PhpMixed::String(format!("Writing {}", file_obj.get_path())),
+ io.write_error3(
+ &format!("Writing {}", file_obj.get_path()),
true,
io_interface::NORMAL,
);
@@ -328,10 +326,8 @@ impl InitCommand {
if let Err(e) = validate_result {
// try to downcast to JsonValidationException
if let Some(json_err) = e.downcast_ref::<JsonValidationException>() {
- io.write_error(
- PhpMixed::String(
- "<error>Schema validation error, aborting</error>".to_string(),
- ),
+ io.write_error3(
+ "<error>Schema validation error, aborting</error>",
true,
io_interface::NORMAL,
);
@@ -339,8 +335,8 @@ impl InitCommand {
" - {}",
implode(&format!("{} - ", PHP_EOL), &json_err.get_errors())
);
- io.write_error(
- PhpMixed::String(format!("{}:{}{}", json_err.message, PHP_EOL, errors)),
+ io.write_error3(
+ &format!("{}:{}{}", json_err.message, PHP_EOL, errors),
true,
io_interface::NORMAL,
);
@@ -400,23 +396,16 @@ impl InitCommand {
.to_string();
let namespace = self.namespace_from_package_name(&name).unwrap_or_default();
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"PSR-4 autoloading configured. Use \"<comment>namespace {};</comment>\" in {}",
namespace,
autoload_path.as_deref().unwrap_or("")
- )),
- true,
- io_interface::NORMAL,
- );
- io.write_error(
- PhpMixed::String(
- "Include the Composer autoloader with: <comment>require 'vendor/autoload.php';</comment>"
- .to_string(),
),
true,
io_interface::NORMAL,
);
+ io.write_error3("Include the Composer autoloader with: <comment>require 'vendor/autoload.php';</comment>", true, io_interface::NORMAL);
}
Ok(0)
@@ -461,17 +450,19 @@ impl InitCommand {
})
.unwrap_or_default();
if (repositories.len() as i64) > 0 {
- let config = Factory::create_config(Some(io), None)?;
- io.load_configuration(&config);
- let mut repo_manager = RepositoryFactory::manager(io, &config, None, None);
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
+ Some(io),
+ None,
+ )?));
+ io.load_configuration(&mut *config.borrow_mut())?;
+ let mut repo_manager = RepositoryFactory::manager(io, &config, None, None, None)?;
let mut repos: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
> = vec![Box::new(PlatformRepository::new(vec![], IndexMap::new())?)];
let mut create_default_packagist_repo = true;
for repo in &repositories {
- let repo_config =
- RepositoryFactory::config_from_string(io, &config, repo, Some(true))?;
+ let repo_config = RepositoryFactory::config_from_string(io, &config, repo, true)?;
let is_packagist_false = repo_config
.get("packagist")
.map(|v| v.as_bool() == Some(false))
@@ -513,7 +504,7 @@ impl InitCommand {
// unset($repos, $config, $repositories);
}
- io.write_error(
+ io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(String::new())),
Box::new(PhpMixed::String(formatter.format_block(
@@ -528,7 +519,7 @@ impl InitCommand {
);
// namespace
- io.write_error(
+ io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(String::new())),
Box::new(PhpMixed::String(
@@ -659,14 +650,20 @@ impl InitCommand {
.unwrap_or(PhpMixed::Null);
}
- if !BasePackage::stabilities().contains_key(value.as_string().unwrap_or("")) {
+ if !base_package::STABILITIES.contains_key(value.as_string().unwrap_or("")) {
// TODO(phase-b): closure cannot throw
panic!(
"{}",
format!(
"Invalid minimum stability \"{}\". Must be empty or one of: {}",
value.as_string().unwrap_or(""),
- implode(", ", &array_keys(&BasePackage::stabilities()))
+ implode(
+ ", ",
+ &base_package::STABILITIES
+ .keys()
+ .map(|k| k.to_string())
+ .collect::<Vec<_>>()
+ )
)
);
}
@@ -733,7 +730,7 @@ impl InitCommand {
}
input.set_option("license", license);
- io.write_error(
+ io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(String::new())),
Box::new(PhpMixed::String("Define your dependencies.".to_string())),
@@ -885,11 +882,15 @@ impl InitCommand {
/// @return array{name: string, email: string|null}
fn parse_author_string(&self, author: &str) -> Result<IndexMap<String, Option<String>>> {
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r#"/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'’\"()]+)(?:\s+<(?P<email>.+?)>)?$/u"#,
author,
- ) {
- let email = m.get("email").cloned();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let email = m.get(&CaptureKey::ByName("email".to_string())).cloned();
if let Some(ref email) = email {
if !self.is_valid_email(email) {
return Err(InvalidArgumentException {
@@ -903,7 +904,12 @@ impl InitCommand {
let mut result: IndexMap<String, Option<String>> = IndexMap::new();
result.insert(
"name".to_string(),
- Some(trim(&m.get("name").cloned().unwrap_or_default(), None)),
+ Some(trim(
+ &m.get(&CaptureKey::ByName("name".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ None,
+ )),
);
result.insert("email".to_string(), email);
@@ -944,7 +950,7 @@ impl InitCommand {
let namespace: Vec<String> = array_map(
|part: &String| {
- let part = Preg::replace(r"/[^a-z0-9]/i", " ", part.clone());
+ let part = Preg::replace(r"/[^a-z0-9]/i", " ", &part);
let part = ucwords(&part);
str_replace(" ", "", &part)
},
@@ -963,17 +969,20 @@ impl InitCommand {
let mut process = ProcessExecutor::new(self.get_io());
let mut output = String::new();
- if process.execute(
+ if process.execute_args(
&vec!["git".to_string(), "config".to_string(), "-l".to_string()],
&mut output,
None,
) == 0
{
self.git_config = Some(IndexMap::new());
- let matches = Preg::is_match_all_strict_groups(r"{^([^=]+)=(.*)$}m", &output);
- if let Some(m) = matches {
- let keys: Vec<String> = m.get(1).cloned().unwrap_or_default();
- let values: Vec<String> = m.get(2).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
+ if Preg::is_match_all_strict_groups3(r"{^([^=]+)=(.*)$}m", &output, Some(&mut m))
+ .unwrap_or(false)
+ {
+ let keys: Vec<String> = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let values: Vec<String> =
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
for (key, value) in keys.iter().zip(values.iter()) {
self.git_config
.as_mut()
@@ -1049,11 +1058,8 @@ impl InitCommand {
Ok(())
});
if let Err(_e) = result {
- self.get_io().write_error(
- PhpMixed::String(
- "Could not update dependencies. Run `composer update` to see more information."
- .to_string(),
- ),
+ self.get_io().write_error3(
+ "Could not update dependencies. Run `composer update` to see more information.",
true,
io_interface::NORMAL,
);
@@ -1068,11 +1074,8 @@ impl InitCommand {
Ok(())
});
if let Err(_e) = result {
- self.get_io().write_error(
- PhpMixed::String("Could not run dump-autoload.".to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.get_io()
+ .write_error3("Could not run dump-autoload.", true, io_interface::NORMAL);
}
}
@@ -1100,11 +1103,12 @@ impl InitCommand {
let name = Preg::replace(
r"{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}",
"$1$3-$2$4",
- name.to_string(),
- );
+ name,
+ )
+ .unwrap_or_default();
let name = strtolower(&name);
- let name = Preg::replace(r"{^[_.-]+|[_.-]+$|[^a-z0-9_.-]}u", "", name);
- let name = Preg::replace(r"{([_.-]){2,}}u", "$1", name);
+ let name = Preg::replace(r"{^[_.-]+|[_.-]+$|[^a-z0-9_.-]}u", "", &name).unwrap_or_default();
+ let name = Preg::replace(r"{([_.-]){2,}}u", "$1", &name).unwrap_or_default();
name
}
diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs
index 2b9a87d..1dcdcee 100644
--- a/crates/shirabe/src/command/install_command.rs
+++ b/crates/shirabe/src/command/install_command.rs
@@ -1,8 +1,8 @@
//! ref: composer/src/Composer/Command/InstallCommand.php
use anyhow::Result;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
use crate::advisory::auditor::Auditor;
@@ -27,29 +27,29 @@ impl InstallCommand {
.set_name("install")
.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),
- 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_definition(&[
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None).unwrap().into(),
+ InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None).unwrap().into(),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None).unwrap().into(),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None).unwrap().into(),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None).unwrap().into(),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Do not use, only defined here to catch misuse of the install command.", None).unwrap().into(),
+ InputOption::new("audit", None, Some(InputOption::VALUE_NONE), "Run an audit after installation is complete.", None).unwrap().into(),
+ 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()))).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None).unwrap().into(),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None).unwrap().into(),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into(),
+ 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).unwrap().into(),
])
.set_help(
"The <info>install</info> command reads the composer.lock file from\n\
@@ -101,33 +101,32 @@ impl InstallCommand {
}
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "install".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "install", input, output);
composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None);
- let install = Installer::create(io, &composer);
+ let install = Installer::create(io.clone_box(), &composer);
- let config = composer.get_config();
- let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
+ let config = std::rc::Rc::clone(composer.get_config());
+ let (prefer_source, prefer_dist) =
+ self.get_preferred_install_options(&*config.borrow(), input, false)?;
let optimize = input
.get_option("optimize-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("optimize-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("optimize-autoloader")
+ .as_bool()
+ .unwrap_or(false);
let authoritative = input
.get_option("classmap-authoritative")
.as_bool()
.unwrap_or(false)
|| config
+ .borrow_mut()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false);
@@ -140,7 +139,11 @@ impl InstallCommand {
.get_option("apcu-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("apcu-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("apcu-autoloader")
+ .as_bool()
+ .unwrap_or(false);
composer
.get_installation_manager()
@@ -156,9 +159,11 @@ impl InstallCommand {
.set_dump_autoloader(!input.get_option("no-autoloader").as_bool().unwrap_or(false))
.set_optimize_autoloader(optimize)
.set_class_map_authoritative(authoritative)
- .set_apcu_autoloader(apcu, apcu_prefix.as_deref())
+ .set_apcu_autoloader(apcu, apcu_prefix.clone())
.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?)
- .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
+ .set_audit_config(
+ self.create_audit_config(&mut *composer.get_config().borrow_mut(), 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) {
diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs
index 102221e..a35b4c5 100644
--- a/crates/shirabe/src/command/licenses_command.rs
+++ b/crates/shirabe/src/command/licenses_command.rs
@@ -4,10 +4,10 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
+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::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::helper::table::Table;
-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::console::style::symfony_style::SymfonyStyle;
use shirabe_php_shim::{PhpMixed, RuntimeException, UnexpectedValueException};
@@ -33,28 +33,34 @@ impl LicensesCommand {
pub fn configure(&mut self) {
self.set_name("licenses")
.set_description("Shows information about licenses of dependencies")
- .set_definition(vec![
+ .set_definition(&[
InputOption::new(
"format",
Some(PhpMixed::String("f".to_string())),
Some(InputOption::VALUE_REQUIRED),
"Format of the output: text, json or summary",
Some(PhpMixed::String("text".to_string())),
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-dev",
None,
Some(InputOption::VALUE_NONE),
"Disables search in require-dev packages.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"locked",
None,
Some(InputOption::VALUE_NONE),
"Shows licenses from the lock file instead of installed packages.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"The license command displays detailed information about the licenses of\n\
@@ -69,8 +75,7 @@ impl LicensesCommand {
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);
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "licenses", input, output);
composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None);
@@ -144,7 +149,7 @@ impl LicensesCommand {
package.get_pretty_name().to_string()
};
let pkg_licenses = if let Some(complete_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
+ package.as_any().downcast_ref::<CompletePackage>()
{
complete_pkg.get_license()
} else {
@@ -168,7 +173,7 @@ impl LicensesCommand {
IndexMap::new();
for package in &packages {
let pkg_licenses = if let Some(complete_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
+ package.as_any().downcast_ref::<CompletePackage>()
{
complete_pkg.get_license()
} else {
@@ -177,7 +182,7 @@ impl LicensesCommand {
let mut dep_info: IndexMap<String, PhpMixed> = IndexMap::new();
dep_info.insert(
"version".to_string(),
- PhpMixed::String(package.get_full_pretty_version().to_string()),
+ PhpMixed::String(package.get_full_pretty_version(true, 0).to_string()),
);
dep_info.insert(
"license".to_string(),
@@ -198,7 +203,7 @@ impl LicensesCommand {
);
output_map.insert(
"version".to_string(),
- PhpMixed::String(root.get_full_pretty_version().to_string()),
+ PhpMixed::String(root.get_full_pretty_version(true, 0).to_string()),
);
let root_licenses = root.get_license();
output_map.insert(
@@ -232,7 +237,7 @@ impl LicensesCommand {
let mut used_licenses: IndexMap<String, i64> = IndexMap::new();
for package in &packages {
let mut licenses = if let Some(complete_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
+ package.as_any().downcast_ref::<CompletePackage>()
{
complete_pkg.get_license()
} else {
diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs
index bac15cf..3a2b05b 100644
--- a/crates/shirabe/src/command/outdated_command.rs
+++ b/crates/shirabe/src/command/outdated_command.rs
@@ -7,9 +7,9 @@ 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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
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;
use shirabe_php_shim::PhpMixed;
#[derive(Debug)]
@@ -23,22 +23,22 @@ impl OutdatedCommand {
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),
- 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_definition(&[
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code when there are outdated packages", None).unwrap().into(),
+ InputOption::new("major-only", Some(PhpMixed::String("M".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have major SemVer-compatible updates.", None).unwrap().into(),
+ InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None).unwrap().into(),
+ InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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()))).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None).unwrap().into(),
])
.set_help(
"The outdated command is just a proxy for `composer show -l`\n\n\
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 501ae5d..f9aa811 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -5,7 +5,7 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
@@ -354,26 +354,36 @@ pub trait PackageDiscoveryTrait {
}
}
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^\s*(?P<name>[\S/]+)(?:\s+(?P<version>\S+))?\s*$}",
&selection,
- ) {
- if let Some(v) = m.get("version") {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ if let Some(v) =
+ m.get(&CaptureKey::ByName("version".to_string())).cloned()
+ {
// parsing `acme/example ~2.3`
// validate version constraint
// TODO(phase-b): parse_constraints returns Result
- let _ = version_parser_clone.parse_constraints(v);
+ let _ = version_parser_clone.parse_constraints(&v);
return PhpMixed::String(format!(
"{} {}",
- m.get("name").cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByName("name".to_string()))
+ .cloned()
+ .unwrap_or_default(),
v,
));
}
// parsing `acme/example`
return PhpMixed::String(
- m.get("name").cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByName("name".to_string()))
+ .cloned()
+ .unwrap_or_default(),
);
}
@@ -537,7 +547,9 @@ pub trait PackageDiscoveryTrait {
}
// Check whether the package requirements were the problem
- let is_ignore_all = (platform_requirement_filter.as_ref().as_any() as &dyn Any)
+ let is_ignore_all = platform_requirement_filter
+ .as_ref()
+ .as_any()
.downcast_ref::<IgnoreAllPlatformRequirementFilter>()
.is_some();
if !is_ignore_all {
@@ -861,7 +873,8 @@ pub trait PackageDiscoveryTrait {
let mut platform_pkg_version = platform_pkg.get_pretty_version().to_string();
let platform_extra = platform_pkg.get_extra();
let has_config_platform = platform_extra.contains_key("config.platform");
- let is_complete = (platform_pkg.as_any() as &dyn Any)
+ let is_complete = platform_pkg
+ .as_any()
.downcast_ref::<dyn CompletePackageInterface>()
.is_some();
if has_config_platform && is_complete {
diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs
index 26c17f2..80f1ed2 100644
--- a/crates/shirabe/src/command/prohibits_command.rs
+++ b/crates/shirabe/src/command/prohibits_command.rs
@@ -5,8 +5,8 @@ use crate::command::base_dependency_command::BaseDependencyCommand;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct ProhibitsCommand {
@@ -21,40 +21,50 @@ impl ProhibitsCommand {
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![
+ .set_definition(&[
InputArgument::new(
- BaseDependencyCommand::ARGUMENT_PACKAGE,
+ <Self as BaseDependencyCommand>::ARGUMENT_PACKAGE,
Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputArgument::new(
- BaseDependencyCommand::ARGUMENT_CONSTRAINT,
+ <Self as BaseDependencyCommand>::ARGUMENT_CONSTRAINT,
Some(InputArgument::REQUIRED),
"Version constraint, which version you expected to be installed",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
- BaseDependencyCommand::OPTION_RECURSIVE,
+ <Self as BaseDependencyCommand>::OPTION_RECURSIVE,
Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
- BaseDependencyCommand::OPTION_TREE,
+ <Self as BaseDependencyCommand>::OPTION_TREE,
Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"locked",
None,
Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"Displays detailed information about why a package cannot be installed.\n\n\
@@ -75,7 +85,7 @@ impl BaseDependencyCommand for ProhibitsCommand {
&self.colors
}
- fn colors_mut(&mut self) -> &mut [String] {
+ fn colors_mut(&mut self) -> &mut Vec<String> {
&mut self.colors
}
}
diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs
index 7e703c4..4ffad79 100644
--- a/crates/shirabe/src/command/reinstall_command.rs
+++ b/crates/shirabe/src/command/reinstall_command.rs
@@ -4,12 +4,13 @@ use std::any::Any;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::InvalidArgumentException;
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
+use crate::console::input::InputDefinitionItem;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::dependency_resolver::operation::install_operation::InstallOperation;
@@ -35,20 +36,20 @@ impl ReinstallCommand {
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),
- 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_definition(&[
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into().unwrap().into(),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into().unwrap().into(),
+ 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).unwrap().into().unwrap().into(),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None).unwrap().into().unwrap().into(),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into().unwrap().into(),
+ InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None).unwrap().into().unwrap().into(),
+ 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).unwrap().into().unwrap().into(),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None).unwrap().into().unwrap().into(),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None).unwrap().into().unwrap().into(),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into().unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into().unwrap().into(),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None).unwrap().into().unwrap().into(),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None).unwrap().into().unwrap().into(),
])
.set_help(
"The <info>reinstall</info> command looks up installed packages by name,\n\
@@ -155,8 +156,10 @@ impl ReinstallCommand {
let mut install_order = indexmap::IndexMap::new();
for (index, op) in install_operations.iter().enumerate() {
- if let Some(install_op) = (op.as_any() as &dyn Any).downcast_ref::<InstallOperation>() {
- if (install_op.get_package().as_any() as &dyn Any)
+ if let Some(install_op) = op.as_any().downcast_ref::<InstallOperation>() {
+ if install_op
+ .get_package()
+ .as_any()
.downcast_ref::<AliasPackage>()
.is_none()
{
@@ -178,19 +181,13 @@ impl ReinstallCommand {
});
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "reinstall".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "reinstall", input, output);
let event_dispatcher = composer.get_event_dispatcher();
event_dispatcher.dispatch(Some(command_event.get_name()), None);
- let config = composer.get_config();
- let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
+ let config = std::rc::Rc::clone(composer.get_config());
+ let (prefer_source, prefer_dist) =
+ self.get_preferred_install_options(&*config.borrow(), input)?;
let installation_manager = composer.get_installation_manager();
let download_manager = composer.get_download_manager();
@@ -225,12 +222,17 @@ impl ReinstallCommand {
.get_option("optimize-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("optimize-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("optimize-autoloader")
+ .as_bool()
+ .unwrap_or(false);
let authoritative = input
.get_option("classmap-authoritative")
.as_bool()
.unwrap_or(false)
|| config
+ .borrow_mut()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false);
@@ -243,14 +245,18 @@ impl ReinstallCommand {
.get_option("apcu-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("apcu-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("apcu-autoloader")
+ .as_bool()
+ .unwrap_or(false);
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.get_platform_requirement_filter(input)?);
generator.dump(
- config,
+ &*config.borrow(),
local_repo,
package,
installation_manager,
diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs
index 53bcdc9..b25a499 100644
--- a/crates/shirabe/src/command/remove_command.rs
+++ b/crates/shirabe/src/command/remove_command.rs
@@ -35,161 +35,117 @@ impl RemoveCommand {
.set_name("remove")
.set_aliases(&["rm".to_string(), "uninstall".to_string()])
.set_description("Removes a package from the require or require-dev")
- .set_definition(vec![
- InputArgument::new(
- "packages",
- Some(InputArgument::IS_ARRAY),
- "Packages that should be removed.",
- None,
- ),
- InputOption::new(
- "dev",
- None,
- Some(InputOption::VALUE_NONE),
- "Removes a package from the require-dev section.",
- None,
- ),
- InputOption::new(
- "dry-run",
- None,
- Some(InputOption::VALUE_NONE),
- "Outputs the operations but will not execute anything (implicitly enables --verbose).",
- 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 with explicit dependencies (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var). (Deprecated, is now default behavior)",
- 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-all-dependencies",
- None,
- Some(InputOption::VALUE_NONE),
- "Alias for --update-with-all-dependencies",
- None,
- ),
- InputOption::new(
- "no-update-with-dependencies",
- None,
- Some(InputOption::VALUE_NONE),
- "Does not allow inherited dependencies to be updated with explicit dependencies.",
- 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(
- "unused",
- None,
- Some(InputOption::VALUE_NONE),
- "Remove all packages which are locked but not required by any other package.",
- 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(
- "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_definition(&[
+ InputArgument::new("packages",
+ Some(InputArgument::IS_ARRAY),
+ "Packages that should be removed.",
+ None,).unwrap().into(),
+ InputOption::new("dev",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Removes a package from the require-dev section.",
+ None,).unwrap().into(),
+ InputOption::new("dry-run",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Outputs the operations but will not execute anything (implicitly enables --verbose).",
+ None,).unwrap().into(),
+ InputOption::new("no-progress",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Do not output download progress.",
+ None,).unwrap().into(),
+ InputOption::new("no-update",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Disables the automatic update of the dependencies (implies --no-install).",
+ None,).unwrap().into(),
+ InputOption::new("no-install",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Skip the install step after updating the composer.lock file.",
+ None,).unwrap().into(),
+ 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,).unwrap().into(),
+ 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())),).unwrap().into(),
+ 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,).unwrap().into(),
+ InputOption::new("update-no-dev",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Run the dependency update with the --no-dev option.",
+ None,).unwrap().into(),
+ InputOption::new("update-with-dependencies",
+ Some(PhpMixed::String("w".to_string())),
+ 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,).unwrap().into(),
+ 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,).unwrap().into(),
+ InputOption::new("with-all-dependencies",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Alias for --update-with-all-dependencies",
+ None,).unwrap().into(),
+ InputOption::new("no-update-with-dependencies",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Does not allow inherited dependencies to be updated with explicit dependencies.",
+ None,).unwrap().into(),
+ 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,).unwrap().into(),
+ InputOption::new("unused",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Remove all packages which are locked but not required by any other package.",
+ None,).unwrap().into(),
+ InputOption::new("ignore-platform-req",
+ None,
+ Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY),
+ "Ignore a specific platform requirement (php & ext- packages).",
+ None,).unwrap().into(),
+ InputOption::new("ignore-platform-reqs",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Ignore all platform requirements (php & ext- packages).",
+ None,).unwrap().into(),
+ InputOption::new("optimize-autoloader",
+ Some(PhpMixed::String("o".to_string())),
+ Some(InputOption::VALUE_NONE),
+ "Optimize autoloader during autoloader dump",
+ None,).unwrap().into(),
+ 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,).unwrap().into(),
+ InputOption::new("apcu-autoloader",
+ None,
+ Some(InputOption::VALUE_NONE),
+ "Use APCu to cache found/not-found classes.",
+ None,).unwrap().into(),
+ InputOption::new("apcu-autoloader-prefix",
+ None,
+ Some(InputOption::VALUE_REQUIRED),
+ "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader",
+ None,).unwrap().into(),
+ ])
.set_help(
"The <info>remove</info> command removes a package from the current\n\
list of installed packages\n\n\
@@ -255,7 +211,7 @@ impl RemoveCommand {
let mut found = false;
let mut to_remove = vec![];
for (index, package) in locked_packages.iter().enumerate() {
- for name in package.get_names() {
+ for name in package.get_names(true) {
if required.contains_key(name.as_str()) {
for link in package.get_requires().values() {
required.insert(link.get_target().to_string(), true);
@@ -392,9 +348,12 @@ impl RemoveCommand {
.and_then(|v| v.as_array())
.map(|m| m.keys().cloned().collect())
.unwrap_or_default();
- let matches_in_type =
- Preg::grep(&base_package::package_name_to_regexp(package), &type_keys)
- .unwrap_or_default();
+ let type_keys_refs: Vec<&str> = type_keys.iter().map(|s| s.as_str()).collect();
+ let matches_in_type = Preg::grep(
+ &base_package::package_name_to_regexp(package),
+ &type_keys_refs,
+ )
+ .unwrap_or_default();
let alt_type_keys: Vec<String> = composer_data
.as_array()
@@ -402,9 +361,11 @@ impl RemoveCommand {
.and_then(|v| v.as_array())
.map(|m| m.keys().cloned().collect())
.unwrap_or_default();
+ let alt_type_keys_refs: Vec<&str> =
+ alt_type_keys.iter().map(|s| s.as_str()).collect();
let matches_in_alt_type = Preg::grep(
&base_package::package_name_to_regexp(package),
- &alt_type_keys,
+ &alt_type_keys_refs,
)
.unwrap_or_default();
@@ -499,7 +460,7 @@ impl RemoveCommand {
.get_event_dispatcher()
.dispatch(command_event.get_name(), command_event);
- let allow_plugins = composer.get_config().get("allow-plugins");
+ let allow_plugins = composer.get_config().borrow_mut().get("allow-plugins");
let removed_plugins: Vec<String> =
if let Some(allow_map) = allow_plugins.as_ref().and_then(|v| v.as_array()) {
packages
@@ -542,8 +503,9 @@ impl RemoveCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("optimize-autoloader")
- .and_then(|v| v.as_bool())
+ .as_bool()
.unwrap_or(false);
let authoritative = input
.get_option("classmap-authoritative")
@@ -551,8 +513,9 @@ impl RemoveCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("classmap-authoritative")
- .and_then(|v| v.as_bool())
+ .as_bool()
.unwrap_or(false);
let apcu_prefix = input
.get_option("apcu-autoloader-prefix")
@@ -565,8 +528,9 @@ impl RemoveCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("apcu-autoloader")
- .and_then(|v| v.as_bool())
+ .as_bool()
.unwrap_or(false);
let minimal_changes = input
.get_option("minimal-changes")
@@ -574,8 +538,9 @@ impl RemoveCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("update-with-minimal-changes")
- .and_then(|v| v.as_bool())
+ .as_bool()
.unwrap_or(false);
let mut update_allow_transitive_dependencies =
@@ -617,7 +582,9 @@ impl RemoveCommand {
install.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies);
install.set_platform_requirement_filter(self.get_platform_requirement_filter(input));
install.set_dry_run(dry_run);
- install.set_audit_config(self.create_audit_config(composer.get_config(), input));
+ install.set_audit_config(
+ self.create_audit_config(&mut *composer.get_config().borrow_mut(), input),
+ );
install.set_minimal_update(minimal_changes);
// if no lock is present, we do not do a partial update as
@@ -640,7 +607,7 @@ impl RemoveCommand {
if !composer
.get_repository_manager()
.get_local_repository()
- .find_packages(package)
+ .find_packages(package, None)
.is_empty()
{
io.write_error(&format!(
diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs
index 2aea141..279e9de 100644
--- a/crates/shirabe/src/command/repository_command.rs
+++ b/crates/shirabe/src/command/repository_command.rs
@@ -2,8 +2,8 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
InvalidArgumentException, PHP_URL_HOST, PhpMixed, RuntimeException, parse_url, strtolower,
};
@@ -24,7 +24,7 @@ use crate::json::json_file::JsonFile;
pub struct RepositoryCommand {
base_command_data: BaseCommandData,
- config: Option<Config>,
+ config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
config_file: Option<JsonFile>,
config_source: Option<JsonConfigSource>,
}
@@ -36,16 +36,16 @@ impl RepositoryCommand {
.set_name("repository")
.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),
- 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_definition(&[
+ InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lower priority) instead of prepending it", None).unwrap().into(),
+ InputOption::new("before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None).unwrap().into(),
+ InputOption::new("after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None).unwrap().into(),
+ InputArgument::new("action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string()))).unwrap().into(),
+ InputArgument::new("name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None).unwrap().into(),
+ InputArgument::new("arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None).unwrap().into(),
+ InputArgument::new("arg2", Some(InputArgument::OPTIONAL), "URL for add (if not using JSON)", None).unwrap().into(),
])
.set_help(
"This command lets you manage repositories in your composer.json.\n\n\
@@ -91,19 +91,18 @@ impl RepositoryCommand {
.as_string()
.map(|s| s.to_string());
- let config_data = self.config_file.as_ref().unwrap().read()?;
- let config_file_path = self
- .inner
- .config_file
- .as_ref()
- .unwrap()
- .get_path()
- .to_string();
+ let config_data = self.config_file.as_mut().unwrap().read()?;
+ let config_file_path = self.config_file.as_ref().unwrap().get_path().to_string();
+ let config_data_map: IndexMap<String, PhpMixed> = match config_data {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
self.config
.as_mut()
.unwrap()
- .merge(config_data, &config_file_path);
- let repos = self.config.as_ref().unwrap().get_repositories();
+ .borrow_mut()
+ .merge(&config_data_map, &config_file_path);
+ let repos = self.config.as_ref().unwrap().borrow().get_repositories();
match action.as_str() {
"list" | "ls" | "show" => {
@@ -165,9 +164,15 @@ impl RepositoryCommand {
}
let reference_name = before.as_deref().or(after.as_deref()).unwrap();
let offset: i64 = if after.is_some() { 1 } else { 0 };
+ let repo_config_opt: Option<IndexMap<String, PhpMixed>> = match &repo_config {
+ PhpMixed::Array(m) => {
+ Some(m.iter().map(|(k, v)| (k.clone(), *v.clone())).collect())
+ }
+ _ => None,
+ };
self.config_source.as_mut().unwrap().insert_repository(
name.as_deref().unwrap(),
- repo_config,
+ repo_config_opt,
reference_name,
offset,
);
@@ -175,9 +180,15 @@ impl RepositoryCommand {
}
let append = input.get_option("append").as_bool().unwrap_or(false);
+ let repo_config_opt: Option<IndexMap<String, PhpMixed>> = match &repo_config {
+ PhpMixed::Array(m) => {
+ Some(m.iter().map(|(k, v)| (k.clone(), *v.clone())).collect())
+ }
+ _ => None,
+ };
self.config_source.as_mut().unwrap().add_repository(
name.as_deref().unwrap(),
- repo_config,
+ repo_config_opt,
append,
);
Ok(0)
@@ -197,7 +208,7 @@ impl RepositoryCommand {
if ["packagist", "packagist.org"].contains(&name_str) {
self.config_source.as_mut().unwrap().add_repository(
"packagist.org",
- PhpMixed::Bool(false),
+ None,
false,
);
}
@@ -392,12 +403,12 @@ impl RepositoryCommand {
}
impl BaseConfigCommand for RepositoryCommand {
- fn config(&self) -> Option<&Config> {
+ fn config(&self) -> Option<&std::rc::Rc<std::cell::RefCell<Config>>> {
self.config.as_ref()
}
- fn config_mut(&mut self) -> Option<&mut Config> {
- self.config.as_mut()
+ fn config_mut(&mut self) -> &mut Option<std::rc::Rc<std::cell::RefCell<Config>>> {
+ &mut self.config
}
fn config_file(&self) -> Option<&JsonFile> {
@@ -415,6 +426,14 @@ impl BaseConfigCommand for RepositoryCommand {
fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource> {
self.config_source.as_mut()
}
+
+ fn set_config_file(&mut self, file: Option<JsonFile>) {
+ self.config_file = file;
+ }
+
+ fn set_config_source(&mut self, source: Option<JsonConfigSource>) {
+ self.config_source = source;
+ }
}
impl HasBaseCommandData for RepositoryCommand {
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 63efa92..a49428d 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -28,7 +28,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::json::json_manipulator::JsonManipulator;
use crate::package::alias_package::AliasPackage;
-use crate::package::base_package::BasePackage;
+use crate::package::base_package::{self, BasePackage};
use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::loader::array_loader::ArrayLoader;
use crate::package::loader::root_package_loader::RootPackageLoader;
@@ -104,36 +104,36 @@ impl RequireCommand {
.set_name("require")
.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),
- 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_definition(&[
+ 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).unwrap().into(),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None).unwrap().into(),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None).unwrap().into(),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None).unwrap().into(),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None).unwrap().into(),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(),
+ InputOption::new("no-update", None, Some(InputOption::VALUE_NONE), "Disables the automatic update of the dependencies (implies --no-install).", None).unwrap().into(),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Skip the install step after updating the composer.lock file.", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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()))).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("update-no-dev", None, Some(InputOption::VALUE_NONE), "Run the dependency update with the --no-dev option.", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("with-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-dependencies", None).unwrap().into(),
+ InputOption::new("with-all-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-all-dependencies", None).unwrap().into(),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into(),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("sort-packages", None, Some(InputOption::VALUE_NONE), "Sorts packages when adding/updating a new dependency", None).unwrap().into(),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None).unwrap().into(),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None).unwrap().into(),
])
.set_help(
"The require command adds required packages to your composer.json and installs them.\n\
@@ -159,23 +159,13 @@ impl RequireCommand {
let io = self.get_io();
if input.get_option("no-suggest").as_bool().unwrap_or(false) {
- io.write_error(
- PhpMixed::String(
- "<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>"
- .to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3("<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>", true, io_interface::NORMAL);
}
self.newly_created = !file_exists(&self.file);
- if self.newly_created && !file_put_contents(&self.file, "{\n}\n") {
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} could not be created.</error>",
- self.file
- )),
+ if self.newly_created && file_put_contents(&self.file, b"{\n}\n").is_none() {
+ io.write_error3(
+ &format!("<error>{} could not be created.</error>", self.file),
true,
io_interface::NORMAL,
);
@@ -183,8 +173,8 @@ impl RequireCommand {
return Ok(1);
}
if !Filesystem::is_readable(&self.file) {
- io.write_error(
- PhpMixed::String(format!("<error>{} is not readable.</error>", self.file)),
+ io.write_error3(
+ &format!("<error>{} is not readable.</error>", self.file),
true,
io_interface::NORMAL,
);
@@ -234,8 +224,8 @@ impl RequireCommand {
.ok()
== Some(false)
{
- io.write_error(
- PhpMixed::String(format!("<error>{} is not writable.</error>", self.file)),
+ io.write_error3(
+ &format!("<error>{} is not writable.</error>", self.file),
true,
io_interface::NORMAL,
);
@@ -258,24 +248,10 @@ impl RequireCommand {
/// @see https://github.com/composer/composer/pull/8313#issuecomment-532637955
if package_type != "project" && !input.get_option("dev").as_bool().unwrap_or(false) {
- io.write_error(
- PhpMixed::String(
- "<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>"
- .to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3("<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>", true, io_interface::NORMAL);
- if !config.contains_key("type") {
- io.write_error(
- PhpMixed::String(
- "<error>If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".</error>"
- .to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ if config.get("type").is_none() {
+ io.write_error3("<error>If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".</error>", true, io_interface::NORMAL);
}
return Ok(1);
@@ -285,7 +261,7 @@ impl RequireCommand {
let composer = self.require_composer(None, None)?;
let repos = composer.get_repository_manager().get_repositories();
- let platform_overrides = composer.get_config().get("platform");
+ let platform_overrides = composer.get_config().borrow_mut().get("platform");
// initialize self.repos as it is used by the PackageDiscoveryTrait
let platform_repo = PlatformRepository::new(vec![], platform_overrides);
let mut combined: Vec<
@@ -447,14 +423,10 @@ impl RequireCommand {
let version_parser = VersionParser::new();
for (package, constraint) in &requirements {
if strtolower(package) == composer.get_package().get_name() {
- io.write_error(
- PhpMixed::String(sprintf(
- "<error>Root package '%s' cannot require itself in its composer.json</error>",
- &[PhpMixed::String(package.clone())],
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3(&sprintf(
+ "<error>Root package '%s' cannot require itself in its composer.json</error>",
+ &[PhpMixed::String(package.clone())],
+ ), true, io_interface::NORMAL);
return Ok(1);
}
@@ -529,6 +501,7 @@ impl RequireCommand {
let sort_packages = input.get_option("sort-packages").as_bool().unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("sort-packages")
.as_bool()
.unwrap_or(false);
@@ -561,8 +534,8 @@ impl RequireCommand {
);
}
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<info>{} has been {}</info>",
self.file,
if self.newly_created {
@@ -570,7 +543,7 @@ impl RequireCommand {
} else {
"updated"
}
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -730,9 +703,9 @@ impl RequireCommand {
let new_links = loader.parse_links(
root_package.get_name(),
root_package.get_pretty_version(),
- BasePackage::supported_link_types(require_key)
- .get("method")
- .cloned()
+ base_package::SUPPORTED_LINK_TYPES
+ .get(require_key)
+ .map(|t| t.method)
.unwrap_or_default(),
requirements,
);
@@ -770,6 +743,7 @@ impl RequireCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("optimize-autoloader")
.as_bool()
.unwrap_or(false);
@@ -779,6 +753,7 @@ impl RequireCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false);
@@ -793,6 +768,7 @@ impl RequireCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("apcu-autoloader")
.as_bool()
.unwrap_or(false);
@@ -802,6 +778,7 @@ impl RequireCommand {
.unwrap_or(false)
|| composer
.get_config()
+ .borrow()
.get("update-with-minimal-changes")
.as_bool()
.unwrap_or(false);
@@ -833,8 +810,8 @@ impl RequireCommand {
flags += " --with-dependencies";
}
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<info>Running composer update {}{}</info>",
implode(
" ",
@@ -843,7 +820,7 @@ impl RequireCommand {
.collect::<Vec<String>>()
),
flags,
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -859,9 +836,8 @@ impl RequireCommand {
let install = Installer::create(io, &composer);
- let (prefer_source, prefer_dist) = self
- .inner
- .get_preferred_install_options(composer.get_config(), input)?;
+ let (prefer_source, prefer_dist) =
+ self.get_preferred_install_options(&*composer.get_config().borrow(), input)?;
install
.set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false))
@@ -875,10 +851,14 @@ 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.get_platform_requirement_filter(input)?)
+ .set_platform_requirement_filter(BaseCommand::get_platform_requirement_filter(
+ self, 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.create_audit_config(composer.get_config(), input)?)
+ .set_audit_config(
+ self.create_audit_config(&mut *composer.get_config().borrow_mut(), input)?,
+ )
.set_minimal_update(minimal_changes);
// if no lock is present, or the file is brand new, we do not do a
@@ -894,7 +874,8 @@ 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.normalize_requirements(
+ for req in BaseCommand::normalize_requirements(
+ self,
input
.get_argument("packages")
.as_list()
@@ -906,15 +887,11 @@ impl RequireCommand {
.unwrap_or_default(),
)? {
if !req.contains_key("version") {
- io.write_error(
- PhpMixed::String(format!(
- "You can also try re-running composer require with an explicit version constraint, e.g. \"composer require {}:*\" to figure out if any version is installable, or \"composer require {}:^2.1\" if you know which you need.",
- req.get("name").cloned().unwrap_or_default(),
- req.get("name").cloned().unwrap_or_default(),
- )),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3(&format!(
+ "You can also try re-running composer require with an explicit version constraint, e.g. \"composer require {}:*\" to figure out if any version is installable, or \"composer require {}:^2.1\" if you know which you need.",
+ req.get("name").cloned().unwrap_or_default(),
+ req.get("name").cloned().unwrap_or_default(),
+ ), true, io_interface::NORMAL);
break;
}
}
@@ -969,8 +946,8 @@ impl RequireCommand {
version_selector.find_recommended_require_version(&*package),
);
}
- self.get_io().write_error(
- PhpMixed::String(sprintf(
+ self.get_io().write_error3(
+ &sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
PhpMixed::String(
@@ -978,7 +955,7 @@ impl RequireCommand {
),
PhpMixed::String(package_name.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1018,7 +995,14 @@ impl RequireCommand {
remove_key,
sort_packages,
);
- if locker.is_locked() && composer.get_config().get("lock").as_bool().unwrap_or(false) {
+ if locker.is_locked()
+ && composer
+ .get_config()
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(false)
+ {
let stability_flags = RootPackageLoader::extract_stability_flags(
&requirements,
composer.get_package().get_minimum_stability(),
@@ -1083,7 +1067,7 @@ impl RequireCommand {
composer_definition.shift_remove(remove_key);
}
}
- self.json.as_ref().unwrap().write(&PhpMixed::Array(
+ let _ = self.json.as_ref().unwrap().write(PhpMixed::Array(
composer_definition
.into_iter()
.map(|(k, v)| (k, Box::new(v)))
@@ -1126,11 +1110,11 @@ impl RequireCommand {
let io = self.get_io();
if self.newly_created {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"\n<error>Installation failed, deleting {}.</error>",
self.file
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1144,11 +1128,11 @@ impl RequireCommand {
} else {
" to its ".to_string()
};
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"\n<error>Installation failed, reverting {}{}original content.</error>",
self.file, msg
- )),
+ ),
true,
io_interface::NORMAL,
);
diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs
index 5e761f2..6fa7646 100644
--- a/crates/shirabe/src/command/run_script_command.rs
+++ b/crates/shirabe/src/command/run_script_command.rs
@@ -1,8 +1,8 @@
//! ref: composer/src/Composer/Command/RunScriptCommand.php
use anyhow::Result;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, RuntimeException};
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
@@ -47,48 +47,60 @@ impl RunScriptCommand {
self.set_name("run-script")
.set_aliases(&["run".to_string()])
.set_description("Runs the scripts defined in composer.json")
- .set_definition(vec![
+ .set_definition(&[
// 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,
- ),
+ )
+ .unwrap()
+ .into(),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"timeout",
None,
Some(InputOption::VALUE_REQUIRED),
"Sets script timeout in seconds, or 0 for never.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"dev",
None,
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-dev",
None,
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"list",
Some(PhpMixed::String("l".to_string())),
Some(InputOption::VALUE_NONE),
"List scripts.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"The <info>run-script</info> command runs scripts defined in composer.json:\n\n\
@@ -238,7 +250,6 @@ impl RunScriptCommand {
let mut result: Vec<(String, String)> = vec![];
for (name, _script) in scripts {
let description = self
- .inner
.get_application()
.find(&name)
.map(|cmd| cmd.get_description().unwrap_or("").to_string())
diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs
index e077bc7..982ed84 100644
--- a/crates/shirabe/src/command/script_alias_command.rs
+++ b/crates/shirabe/src/command/script_alias_command.rs
@@ -8,8 +8,8 @@ use crate::io::io_interface::IOInterface;
use crate::util::platform::Platform;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, LogicException, PhpMixed, is_string};
#[derive(Debug)]
@@ -53,27 +53,33 @@ impl ScriptAliasCommand {
self.set_name(&self.script)
.set_description(&self.description)
.set_aliases(self.aliases.clone())
- .set_definition(vec![
+ .set_definition(&[
InputOption::new(
"dev",
None,
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-dev",
None,
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"The <info>run-script</info> command runs scripts defined in composer.json:\n\n\
@@ -108,7 +114,7 @@ impl ScriptAliasCommand {
Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" });
- let script_alias_input = Preg::replace_limit(r"^\S+ ?", "", &input.to_string(), 1);
+ let script_alias_input = Preg::replace4(r"{^\S+ ?}", "", &input.to_string(), 1)?;
let mut flags = indexmap::IndexMap::new();
flags.insert(
"script-alias-input".to_string(),
diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs
index 64727fc..2532f87 100644
--- a/crates/shirabe/src/command/search_command.rs
+++ b/crates/shirabe/src/command/search_command.rs
@@ -13,9 +13,9 @@ use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::{self, RepositoryInterface};
use anyhow::Result;
use indexmap::IndexMap;
+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::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::{InvalidArgumentException, PhpMixed, implode, in_array, preg_quote};
#[derive(Debug)]
@@ -28,12 +28,12 @@ impl SearchCommand {
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),
- 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_definition(&[
+ InputOption::new("only-name", Some(PhpMixed::String("N".to_string())), Some(InputOption::VALUE_NONE), "Search only in package names", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("type", Some(PhpMixed::String("t".to_string())), Some(InputOption::VALUE_REQUIRED), "Search for a specific package type", None).unwrap().into(),
+ 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()))).unwrap().into(),
+ InputArgument::new("tokens", Some(InputArgument::IS_ARRAY | InputArgument::REQUIRED), "tokens to search for", None).unwrap().into(),
])
.set_help(
"The search command searches for packages by its name\n\
@@ -83,14 +83,7 @@ impl SearchCommand {
let repos = CompositeRepository::new(all_repos);
// TODO(plugin): dispatch CommandEvent for search command
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "search".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "search", input, output);
composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None);
diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs
index 7e79529..ae1f217 100644
--- a/crates/shirabe/src/command/self_update_command.rs
+++ b/crates/shirabe/src/command/self_update_command.rs
@@ -2,7 +2,8 @@
use crate::io::io_interface;
use anyhow::Result;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
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;
@@ -45,19 +46,19 @@ impl SelfUpdateCommand {
.set_name("self-update")
.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),
- 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_definition(&[
+ InputOption::new("rollback", Some(PhpMixed::String("r".to_string())), Some(InputOption::VALUE_NONE), "Revert to an older installation of composer", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "The version to update to", None).unwrap().into(),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(),
+ InputOption::new("update-keys", None, Some(InputOption::VALUE_NONE), "Prompt user for a key update", None).unwrap().into(),
+ InputOption::new("stable", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel", None).unwrap().into(),
+ InputOption::new("preview", None, Some(InputOption::VALUE_NONE), "Force an update to the preview channel", None).unwrap().into(),
+ InputOption::new("snapshot", None, Some(InputOption::VALUE_NONE), "Force an update to the snapshot channel", None).unwrap().into(),
+ InputOption::new("1", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 1.x versions", None).unwrap().into(),
+ InputOption::new("2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.x versions", None).unwrap().into(),
+ 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).unwrap().into(),
+ InputOption::new("set-channel-only", None, Some(InputOption::VALUE_NONE), "Only store the channel as the default one and then exit", None).unwrap().into(),
])
.set_help(
"The <info>self-update</info> command checks getcomposer.org for newer\n\
@@ -132,18 +133,23 @@ impl SelfUpdateCommand {
class_exists("Composer\\Util\\Platform");
class_exists("Composer\\Downloader\\FilesystemException");
- let config = Factory::create_config(None, None)?;
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?));
- let base_url = if config.get("disable-tls").as_bool() == Some(true) {
+ let base_url = if config.borrow_mut().get("disable-tls").as_bool() == Some(true) {
format!("http://{}", Self::HOMEPAGE)
} else {
format!("https://{}", Self::HOMEPAGE)
};
let io = self.get_io();
- let http_downloader = Factory::create_http_downloader(io, &config)?;
+ let http_downloader = std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, &config, indexmap::IndexMap::new())?,
+ ));
- let mut versions_util = Versions::new(config.clone(), http_downloader.clone());
+ let mut versions_util = Versions::new(
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&http_downloader),
+ );
// switch channel if requested
let mut requested_channel: Option<String> = None;
@@ -164,12 +170,23 @@ impl SelfUpdateCommand {
}
let cache_dir = config
+ .borrow_mut()
.get("cache-dir")
.as_string()
.unwrap_or("")
.to_string();
- let rollback_dir = config.get("data-dir").as_string().unwrap_or("").to_string();
- let home = config.get("home").as_string().unwrap_or("").to_string();
+ let rollback_dir = config
+ .borrow_mut()
+ .get("data-dir")
+ .as_string()
+ .unwrap_or("")
+ .to_string();
+ let home = config
+ .borrow_mut()
+ .get("home")
+ .as_string()
+ .unwrap_or("")
+ .to_string();
let local_filename = Phar::running(false);
if local_filename.is_empty() {
return Err(RuntimeException {
@@ -180,7 +197,7 @@ impl SelfUpdateCommand {
}
if input.get_option("update-keys").as_bool().unwrap_or(false) {
- self.fetch_keys(io, &config)?;
+ self.fetch_keys(io, &*config.borrow())?;
return Ok(0);
}
@@ -275,17 +292,16 @@ impl SelfUpdateCommand {
.as_string()
.map(|s| s.to_string())
.unwrap_or_else(|| latest_version.clone());
- let current_major_version = Preg::replace(r"{^(\d+).*}", "$1", Composer::get_version());
- let update_major_version = Preg::replace(r"{^(\d+).*}", "$1", update_version.clone());
+ let current_major_version = Preg::replace(r"{^(\d+).*}", "$1", &Composer::get_version())?;
+ let update_major_version = Preg::replace(r"{^(\d+).*}", "$1", &update_version)?;
let preview_major_version = Preg::replace(
r"{^(\d+).*}",
"$1",
latest_preview
.get("version")
.and_then(|v| v.as_string())
- .unwrap_or("")
- .to_string(),
- );
+ .unwrap_or(""),
+ )?;
if versions_util.get_channel()? == "stable" && input.get_argument("version").is_null() {
// if requesting stable channel and no specific version, avoid automatically upgrading to the next major
@@ -414,8 +430,8 @@ impl SelfUpdateCommand {
PhpMixed::String(Preg::replace(
r"{^([0-9a-f]{7})[0-9a-f]{33}$}",
"$1",
- Composer::VERSION.to_string(),
- )),
+ &Composer::VERSION,
+ )?),
PhpMixed::String(Self::OLD_INSTALL_EXT.to_string()),
],
);
@@ -423,17 +439,13 @@ impl SelfUpdateCommand {
let updating_to_tag =
!Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false);
- 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,
- );
+ io.write(&sprintf(
+ "Upgrading to version <info>%s</info> (%s channel).",
+ &[
+ PhpMixed::String(update_version.clone()),
+ PhpMixed::String(channel_string.clone()),
+ ],
+ ));
let remote_filename = format!(
"{}{}",
base_url,
@@ -443,7 +455,7 @@ impl SelfUpdateCommand {
"/composer.phar".to_string()
}
);
- let signature = match http_downloader.get(
+ let signature = match http_downloader.borrow_mut().get(
&format!("{}.sig", remote_filename),
&PhpMixed::Array(indexmap::IndexMap::new()),
) {
@@ -460,7 +472,9 @@ impl SelfUpdateCommand {
}
};
io.write_error3(" ", false, io_interface::NORMAL);
- http_downloader.copy(&remote_filename, &temp_filename)?;
+ http_downloader
+ .borrow_mut()
+ .copy(&remote_filename, &temp_filename)?;
io.write_error3("", true, io_interface::NORMAL);
if !file_exists(&temp_filename) || signature.is_none() || signature.as_deref() == Some("") {
@@ -475,7 +489,9 @@ impl SelfUpdateCommand {
let signature = signature.unwrap_or_default();
// verify phar signature
- if !extension_loaded("openssl") && config.get("disable-tls").as_bool() == Some(true) {
+ if !extension_loaded("openssl")
+ && config.borrow_mut().get("disable-tls").as_bool() == Some(true)
+ {
io.write_error3(
"<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>",
true,
@@ -655,10 +671,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
.into());
}
- io.write3(
+ io.write(
"Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys",
- true,
- io_interface::NORMAL,
);
// TODO(phase-b): closure captures none; PHP throws inside the closure on bad input
@@ -681,11 +695,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
let mut dev_key = String::new();
let mut match_: Option<String> = None;
loop {
- let m = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)}s",
&dev_key,
- );
- match_ = m.and_then(|m| m.get(0).cloned());
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ match_ = m.get(&CaptureKey::ByIndex(0)).cloned();
+ }
if match_.is_some() {
break;
}
@@ -717,23 +736,24 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write3(
- &format!(
- "Stored key with fingerprint: {}",
- Keys::fingerprint(&key_path)?
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "Stored key with fingerprint: {}",
+ Keys::fingerprint(&key_path)?
+ ));
let mut tags_key = String::new();
let mut match_: Option<String> = None;
loop {
- let m = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)}s",
&tags_key,
- );
- match_ = m.and_then(|m| m.get(0).cloned());
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ match_ = m.get(&CaptureKey::ByIndex(0)).cloned();
+ }
if match_.is_some() {
break;
}
@@ -764,23 +784,15 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write3(
- &format!(
- "Stored key with fingerprint: {}",
- Keys::fingerprint(&key_path)?
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "Stored key with fingerprint: {}",
+ Keys::fingerprint(&key_path)?
+ ));
- io.write3(
- &format!(
- "Public keys stored in {}",
- config.get("home").as_string().unwrap_or("")
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write(&format!(
+ "Public keys stored in {}",
+ config.get("home").as_string().unwrap_or("")
+ ));
Ok(())
}
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 79164df..66b3e9f 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -1,13 +1,13 @@
//! ref: composer/src/Composer/Command/ShowCommand.php
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, 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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
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;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, array_search,
date, extension_loaded, in_array, realpath, strtolower, version_compare,
@@ -62,7 +62,7 @@ impl ShowCommand {
self.set_name("show")
.set_aliases(&["info".to_string()])
.set_description("Shows information about packages")
- .set_definition(vec![
+ .set_definition(&[
// TODO(cli-completion): wire up suggest_package_based_on_mode / suggest_installed_package closures here.
])
.set_help(
@@ -173,12 +173,18 @@ impl ShowCommand {
return Ok(1);
}
- let platform_req_filter = self.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();
if let Some(ref composer) = composer {
- if let Some(p) = composer.get_config().get("platform").as_array().cloned() {
+ if let Some(p) = composer
+ .get_config()
+ .borrow()
+ .get("platform")
+ .as_array()
+ .cloned()
+ {
platform_overrides = p.into_iter().map(|(k, v)| (k, *v)).collect();
}
}
@@ -261,7 +267,7 @@ impl ShowCommand {
]));
}
let mut composite_input: Vec<Box<dyn RepositoryInterface>> = vec![Box::new(
- FilterRepository::new(installed_repo.as_repository_interface().clone_box(), {
+ FilterRepository::new(installed_repo.clone_box(), {
let mut m = IndexMap::new();
m.insert("canonical".to_string(), PhpMixed::Bool(false));
m
@@ -282,7 +288,7 @@ impl ShowCommand {
platform_repo.clone(),
)]));
let mut composite_input: Vec<Box<dyn RepositoryInterface>> =
- vec![installed_repo.as_repository_interface().clone_box()];
+ vec![installed_repo.clone_box()];
for (_k, v) in default_repos.into_iter() {
composite_input.push(v);
}
@@ -319,7 +325,7 @@ impl ShowCommand {
if input.get_option("self").as_bool() == Some(true) {
Box::new(RootPackageRepository::new(root_pkg.clone_box()))
} else {
- Box::new(InstalledArrayRepository::new())
+ Box::new(InstalledArrayRepository::new()?)
};
if input.get_option("no-dev").as_bool() == Some(true) {
let packages = RepositoryUtils::filter_required_packages(
@@ -333,11 +339,11 @@ impl ShowCommand {
packages.into_iter().map(|p| p.clone_box()).collect();
installed_repo = Box::new(InstalledRepository::new(vec![
root_repo.clone_box(),
- Box::new(InstalledArrayRepository::new_with_packages(cloned)),
+ Box::new(InstalledArrayRepository::new_with_packages(cloned)?),
]));
repos = Box::new(InstalledRepository::new(vec![
root_repo,
- Box::new(InstalledArrayRepository::new_with_packages(Vec::new())),
+ Box::new(InstalledArrayRepository::new_with_packages(Vec::new())?),
]));
} else {
let lr = composer_local
@@ -365,12 +371,12 @@ impl ShowCommand {
}
if let Some(ref composer) = composer {
- let mut command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "show".to_string(),
+ let mut command_event = CommandEvent::new6(
+ PluginEvents::COMMAND,
+ "show",
input,
output,
- None,
+ vec![],
IndexMap::new(),
);
composer
@@ -514,8 +520,11 @@ impl ShowCommand {
if input.get_option("outdated").as_bool() == Some(true)
&& input.get_option("strict").as_bool() == Some(true)
&& latest_package.is_some()
- && latest_package.as_ref().unwrap().get_full_pretty_version()
- != package.get_full_pretty_version()
+ && latest_package
+ .as_ref()
+ .unwrap()
+ .get_full_pretty_version(true, 0)
+ != package.get_full_pretty_version(true, 0)
&& (latest_package
.as_ref()
.unwrap()
@@ -815,7 +824,8 @@ impl ShowCommand {
// Determine if Composer is checking outdated dependencies and if current package should trigger non-default exit code
let mut package_is_up_to_date = if let Some(latest) = latest_package {
- latest.get_full_pretty_version() == package.get_full_pretty_version()
+ latest.get_full_pretty_version(true, 0)
+ == package.get_full_pretty_version(true, 0)
&& latest
.as_complete_package_interface()
.map_or(true, |c| !c.is_abandoned())
@@ -878,7 +888,8 @@ impl ShowCommand {
}
name_length = name_length.max(package.get_pretty_name().len());
if write_version {
- let mut version_str = package.get_full_pretty_version().to_string();
+ let mut version_str =
+ package.get_full_pretty_version(true, 0).to_string();
if format == "text" {
version_str = version_str.trim_start_matches('v').to_string();
}
@@ -915,7 +926,7 @@ impl ShowCommand {
if write_latest && latest_package.is_some() {
let latest = latest_package.unwrap();
let mut latest_version_str =
- latest.get_full_pretty_version().to_string();
+ latest.get_full_pretty_version(true, 0).to_string();
if format == "text" {
latest_version_str =
latest_version_str.trim_start_matches('v').to_string();
@@ -1097,13 +1108,15 @@ impl ShowCommand {
let write_latest = meta.write_latest;
let write_release_date = meta.write_release_date;
- let version_fits = name_length + version_length + 3 <= width;
- let latest_fits = name_length + version_length + latest_length + 3 <= width;
+ let width_usize = width as usize;
+ let version_fits = name_length + version_length + 3 <= width_usize;
+ let latest_fits = name_length + version_length + latest_length + 3 <= width_usize;
let release_date_fits =
- name_length + version_length + latest_length + release_date_length + 3 <= width;
+ name_length + version_length + latest_length + release_date_length + 3
+ <= width_usize;
let description_fits =
name_length + version_length + latest_length + release_date_length + 24
- <= width;
+ <= width_usize;
if latest_fits && !io.is_decorated() {
latest_length += 2;
@@ -1586,7 +1599,7 @@ impl ShowCommand {
}
io.write(&format!(
"<info>names</info> : {}",
- package.get_names().join(", ")
+ package.get_names(true).join(", ")
));
if let Some(c) = latest.as_complete_package_interface() {
@@ -1778,7 +1791,7 @@ impl ShowCommand {
"names".to_string(),
PhpMixed::List(
package
- .get_names()
+ .get_names(true)
.into_iter()
.map(|n| Box::new(PhpMixed::String(n)))
.collect(),
@@ -1842,7 +1855,6 @@ impl ShowCommand {
&& installed_repo.has_package(package.as_package_interface())
{
let path = self
- .inner
.require_composer(None, None)?
.get_installation_manager()
.get_install_path(package.as_package_interface());
@@ -2412,7 +2424,9 @@ impl ShowCommand {
latest_package: &dyn PackageInterface,
package: &dyn PackageInterface,
) -> String {
- if latest_package.get_full_pretty_version() == package.get_full_pretty_version() {
+ if latest_package.get_full_pretty_version(true, 0)
+ == package.get_full_pretty_version(true, 0)
+ {
return "up-to-date".to_string();
}
@@ -2463,9 +2477,9 @@ impl ShowCommand {
let mut stability = composer.get_package().get_minimum_stability().to_string();
let flags = composer.get_package().get_stability_flags();
if let Some(flag_value) = flags.get(name) {
- let key_map: IndexMap<String, String> = BasePackage::STABILITIES
+ let key_map: IndexMap<String, String> = base_package::STABILITIES
.iter()
- .map(|(k, v)| (k.clone(), v.to_string()))
+ .map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let needle = flag_value.to_string();
if let Some(found_key) = array_search(&needle, &key_map) {
@@ -2489,17 +2503,20 @@ impl ShowCommand {
}
if target_version.is_none() {
- let mut groups: Vec<String> = Vec::new();
+ let mut groups: IndexMap<CaptureKey, String> = IndexMap::new();
if major_only
- && Preg::is_match_with_matches(
+ && Preg::is_match3(
r"{^(?P<zero_major>(?:0\.)+)?(?P<first_meaningful>\d+)\.}",
package.get_version(),
- &mut groups,
+ Some(&mut groups),
)?
{
- let zero_major = groups.get(1).cloned().unwrap_or_default();
+ let zero_major = groups
+ .get(&CaptureKey::ByName("zero_major".to_string()))
+ .cloned()
+ .unwrap_or_default();
let first_meaningful = groups
- .get(2)
+ .get(&CaptureKey::ByName("first_meaningful".to_string()))
.cloned()
.unwrap_or_default()
.parse::<i64>()
diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs
index fb63269..bc81a21 100644
--- a/crates/shirabe/src/command/status_command.rs
+++ b/crates/shirabe/src/command/status_command.rs
@@ -2,8 +2,8 @@
use anyhow::Result;
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_external_packages::symfony::component::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
@@ -31,8 +31,8 @@ impl StatusCommand {
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),
+ .set_definition(&[
+ 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).unwrap().into(),
])
.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"
@@ -43,14 +43,7 @@ impl StatusCommand {
let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "status".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "status", input, output);
composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None);
@@ -93,9 +86,14 @@ impl StatusCommand {
.get_loop()
.borrow()
.get_process_executor()
- .cloned()
- .unwrap_or_else(|| ProcessExecutor::new(io));
- let guesser = VersionGuesser::new(composer.get_config(), &process_executor, &parser, io);
+ .map(std::rc::Rc::clone)
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io))));
+ let guesser = VersionGuesser::new(
+ std::rc::Rc::clone(composer.get_config()),
+ std::rc::Rc::clone(&process_executor),
+ parser.clone(),
+ Some(io.clone_box()),
+ );
let dumper = ArrayDumper::new();
for package in installed_repo.get_canonical_packages() {
diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs
index e488f78..0350dd1 100644
--- a/crates/shirabe/src/command/suggests_command.rs
+++ b/crates/shirabe/src/command/suggests_command.rs
@@ -10,8 +10,8 @@ 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::console::input::input_interface::InputInterface;
-use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+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, empty, in_array};
#[derive(Debug)]
@@ -25,13 +25,13 @@ impl SuggestsCommand {
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),
- 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_definition(&[
+ InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None).unwrap().into(),
+ InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None).unwrap().into(),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None).unwrap().into(),
+ InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None).unwrap().into(),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None).unwrap().into(),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None).unwrap().into(),
])
.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",
@@ -61,7 +61,7 @@ impl SuggestsCommand {
} else {
installed_repos.push(Box::new(PlatformRepository::new(
vec![],
- composer.get_config().get("platform"),
+ composer.get_config().borrow().get("platform"),
)));
installed_repos.push(Box::new(
composer.get_repository_manager().get_local_repository(),
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index de4ee76..405b164 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -6,8 +6,8 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::component::console::helper::table::Table;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, RuntimeException, array_filter, array_intersect,
array_keys, array_merge, array_search, count, empty, in_array, sprintf, strtolower,
@@ -50,7 +50,7 @@ impl UpdateCommand {
.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")
// TODO(phase-b): set_definition with InputArgument/InputOption (see PHP UpdateCommand)
- .set_definition(vec![])
+ .set_definition(&[])
.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\
@@ -78,31 +78,25 @@ impl UpdateCommand {
) -> Result<i64> {
let io = self.get_io();
if input.get_option("dev").as_bool().unwrap_or(false) {
- io.write_error(
- PhpMixed::String(
- "<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>".to_string(),
- ),
+ io.write_error3(
+ "<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>",
true,
io_interface::NORMAL,
);
}
if input.get_option("no-suggest").as_bool().unwrap_or(false) {
- io.write_error(
- PhpMixed::String(
- "<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>".to_string(),
- ),
+ io.write_error3(
+ "<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>",
true,
io_interface::NORMAL,
);
}
- let composer = self.require_composer(None, None);
+ let composer = self.require_composer(None, None)?;
if !HttpDownloader::is_curl_enabled() {
- io.write_error(
- PhpMixed::String(
- "<warning>Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.</warning>".to_string(),
- ),
+ io.write_error3(
+ "<warning>Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.</warning>",
true,
io_interface::NORMAL,
);
@@ -133,11 +127,10 @@ impl UpdateCommand {
if packages.len() > 0 {
let allowlist_packages_with_requirements: Vec<String> =
array_filter(&packages, |pkg: &String| -> bool {
- Preg::is_match(r"{\S+[ =:]\S+}", pkg)
+ Preg::is_match(r"{\S+[ =:]\S+}", pkg).unwrap_or(false)
});
- for (package, constraint) in self
- .inner
- .format_requirements(allowlist_packages_with_requirements.clone())
+ for (package, constraint) in
+ self.format_requirements(allowlist_packages_with_requirements.clone())
{
reqs.insert(package, constraint);
}
@@ -184,26 +177,22 @@ impl UpdateCommand {
let intersected = todo!("Intervals::haveIntersections check");
if let Some(_root_req) = todo!("root_requirements.get(&package)") {
if !intersected {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<error>The temporary constraint \"{}\" for \"{}\" must be a subset of the constraint in your composer.json ({})</error>",
constraint,
package,
todo!("root_requirements[package].get_pretty_constraint()"),
- )),
- true,
- io_interface::NORMAL,
- );
- io.write(
- PhpMixed::String(format!(
- "<info>Run `composer require {}` or `composer require {}:{}` instead to replace the constraint</info>",
- package, package, constraint,
- )),
+ ),
true,
io_interface::NORMAL,
);
+ io.write(&format!(
+ "<info>Run `composer require {}` or `composer require {}:{}` instead to replace the constraint</info>",
+ package, package, constraint,
+ ));
- return Ok(BaseCommand::FAILURE);
+ return Ok(crate::command::base_command::FAILURE);
}
}
}
@@ -297,14 +286,7 @@ impl UpdateCommand {
packages = filtered_packages;
if update_mirrors && !packages.is_empty() {
- io.write_error(
- PhpMixed::String(
- "<error>You cannot simultaneously update only a selection of packages and regenerate the lock file metadata.</error>"
- .to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
+ io.write_error3("<error>You cannot simultaneously update only a selection of packages and regenerate the lock file metadata.</error>", true, io_interface::NORMAL);
return Ok(-1);
}
@@ -321,20 +303,23 @@ impl UpdateCommand {
let mut install = Installer::create(io, &composer);
let config = composer.get_config();
- let (prefer_source, prefer_dist) = self
- .inner
- .get_preferred_install_options(config, input, false);
+ let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input, false);
let optimize = input
.get_option("optimize-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("optimize-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("optimize-autoloader")
+ .as_bool()
+ .unwrap_or(false);
let authoritative = input
.get_option("classmap-authoritative")
.as_bool()
.unwrap_or(false)
|| config
+ .borrow_mut()
.get("classmap-authoritative")
.as_bool()
.unwrap_or(false);
@@ -344,12 +329,17 @@ impl UpdateCommand {
.get_option("apcu-autoloader")
.as_bool()
.unwrap_or(false)
- || config.get("apcu-autoloader").as_bool().unwrap_or(false);
+ || config
+ .borrow_mut()
+ .get("apcu-autoloader")
+ .as_bool()
+ .unwrap_or(false);
let minimal_changes = input
.get_option("minimal-changes")
.as_bool()
.unwrap_or(false)
|| config
+ .borrow_mut()
.get("update-with-minimal-changes")
.as_bool()
.unwrap_or(false);
@@ -403,12 +393,12 @@ impl UpdateCommand {
let mut bump_after_update = input.get_option("bump-after-update");
// PHP: false === $bumpAfterUpdate (strict)
if matches!(bump_after_update, PhpMixed::Bool(false)) {
- bump_after_update = composer.get_config().get("bump-after-update");
+ bump_after_update = composer.get_config().borrow().get("bump-after-update");
}
if !matches!(bump_after_update, PhpMixed::Bool(false)) {
- io.write_error(
- PhpMixed::String("<info>Bumping dependencies</info>".to_string()),
+ io.write_error3(
+ "<info>Bumping dependencies</info>",
true,
io_interface::NORMAL,
);
@@ -469,17 +459,16 @@ impl UpdateCommand {
None
};
- io.write_error(
- PhpMixed::String("<info>Loading packages that can be updated...</info>".to_string()),
+ io.write_error3(
+ "<info>Loading packages that can be updated...</info>",
true,
io_interface::NORMAL,
);
let mut autocompleter_values: IndexMap<String, String> = IndexMap::new();
let installed_packages = if composer.get_locker().is_locked() {
- composer
- .get_locker()
- .get_locked_repository(true)?
- .get_packages()
+ CanonicalPackagesTrait::get_packages(
+ &composer.get_locker().get_locked_repository(true)?,
+ )
} else {
composer
.get_repository_manager()
@@ -489,7 +478,7 @@ impl UpdateCommand {
let version_selector = self.create_version_selector(composer);
for package in &installed_packages {
if let Some(filter) = &filter {
- if !Preg::is_match(filter, package.get_name()) {
+ if !Preg::is_match(filter, package.get_name()).unwrap_or(false) {
continue;
}
}
diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs
index b2e3328..af8a5ce 100644
--- a/crates/shirabe/src/command/validate_command.rs
+++ b/crates/shirabe/src/command/validate_command.rs
@@ -1,8 +1,8 @@
//! ref: composer/src/Composer/Command/ValidateCommand.php
use anyhow::Result;
-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::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
@@ -25,62 +25,78 @@ impl ValidateCommand {
pub fn configure(&mut self) {
self.set_name("validate")
.set_description("Validates a composer.json and composer.lock")
- .set_definition(vec![
+ .set_definition(&[
InputOption::new(
"no-check-all",
None,
Some(InputOption::VALUE_NONE),
"Do not validate requires for overly strict/loose constraints",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"check-lock",
None,
Some(InputOption::VALUE_NONE),
"Check if lock file is up to date (even when config.lock is false)",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-check-lock",
None,
Some(InputOption::VALUE_NONE),
"Do not check if lock file is up to date",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-check-publish",
None,
Some(InputOption::VALUE_NONE),
"Do not check for publish errors",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"no-check-version",
None,
Some(InputOption::VALUE_NONE),
"Do not report a warning if the version field is present",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"with-dependencies",
Some(shirabe_php_shim::PhpMixed::String("A".to_string())),
Some(InputOption::VALUE_NONE),
"Also validate the composer.json of all installed dependencies",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputOption::new(
"strict",
None,
Some(InputOption::VALUE_NONE),
"Return a non-zero exit code for warnings as well as errors",
None,
- ),
+ )
+ .unwrap()
+ .into(),
InputArgument::new(
"file",
Some(InputArgument::OPTIONAL),
"path to composer.json file",
None,
- ),
+ )
+ .unwrap()
+ .into(),
])
.set_help(
"The validate command validates a given composer.json and composer.lock\n\n\
@@ -136,15 +152,20 @@ impl ValidateCommand {
let mut lock_errors: Vec<String> = 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))
+ && composer
+ .get_config()
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(true))
|| input.get_option("check-lock").as_bool().unwrap_or(false);
let locker = composer.get_locker();
- if locker.is_locked() && !locker.is_fresh() {
+ if locker.is_locked() && !locker.is_fresh()? {
lock_errors.push("- The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.".to_string());
}
if locker.is_locked() {
- lock_errors.extend(locker.get_missing_requirement_info(composer.get_package(), true));
+ lock_errors.extend(locker.get_missing_requirement_info(composer.get_package(), true)?);
}
self.output_result(
@@ -214,14 +235,7 @@ impl ValidateCommand {
}
// TODO(plugin): dispatch CommandEvent
- let command_event = CommandEvent::new(
- PluginEvents::COMMAND.to_string(),
- "validate".to_string(),
- input,
- output,
- vec![],
- vec![],
- );
+ let command_event = CommandEvent::new(PluginEvents::COMMAND, "validate", input, output);
let event_code = composer
.get_event_dispatcher()
.dispatch(Some(command_event.get_name()), None)?;
diff --git a/crates/shirabe/src/compiler.rs b/crates/shirabe/src/compiler.rs
index 4185292..db18a66 100644
--- a/crates/shirabe/src/compiler.rs
+++ b/crates/shirabe/src/compiler.rs
@@ -45,13 +45,13 @@ impl Compiler {
shirabe_php_shim::unlink(phar_file);
}
- let process = ProcessExecutor::new(None, None);
+ let process = ProcessExecutor::new(None);
let command = Git::build_rev_list_command(&process, &["-n1", "--format=%H", "HEAD"]);
let mut output = String::new();
// PHP: dirname(__DIR__, 2) - going up 2 levels from src/Composer to the repo root
let repo_root = shirabe_php_shim::dirname_levels(file!(), 2);
- if process.execute(&command, &mut output, Some(&repo_root)) != 0 {
+ if process.execute_args(&command, &mut output, Some(&repo_root)) != 0 {
return Err(RuntimeException {
message: "Can't run git rev-list. You must ensure to run compile from composer git repository clone and that git binary is available.".to_string(),
code: 0,
@@ -63,7 +63,7 @@ impl Compiler {
let command = Git::build_rev_list_command(&process, &["-n1", "--format=%ci", "HEAD"]);
let mut output = String::new();
- if process.execute(&command, &mut output, Some(&repo_root)) != 0 {
+ if process.execute_args(&command, &mut output, Some(&repo_root)) != 0 {
return Err(RuntimeException {
message: "Can't run git rev-list. You must ensure to run compile from composer git repository clone and that git binary is available.".to_string(),
code: 0,
@@ -77,7 +77,7 @@ impl Compiler {
.unwrap_or_else(|_| chrono::Utc::now());
let mut git_describe_output = String::new();
- if process.execute(
+ if process.execute_args(
&[
"git".to_string(),
"describe".to_string(),
@@ -225,11 +225,11 @@ impl Compiler {
for file in finder.iter() {
if let Some(index) = array_search(file.get_real_path(), &extra_files) {
extra_files.shift_remove(&index);
- } else if !Preg::is_match(r"{(^LICENSE(?:\.txt)?$|\.php$)}", file.get_filename())? {
+ } else if !Preg::is_match(r"{(^LICENSE(?:\.txt)?$|\.php$)}", &file.get_filename())? {
unexpected_files.push(file.to_string());
}
- if Preg::is_match(r"{\.php[\d.]*$}", file.get_filename())? {
+ if Preg::is_match(r"{\.php[\d.]*$}", &file.get_filename())? {
self.add_file(&mut phar, &file, true)?;
} else {
self.add_file(&mut phar, &file, false)?;
diff --git a/crates/shirabe/src/composer.rs b/crates/shirabe/src/composer.rs
index d5a9997..2841d12 100644
--- a/crates/shirabe/src/composer.rs
+++ b/crates/shirabe/src/composer.rs
@@ -28,6 +28,17 @@ impl Composer {
pub const SOURCE_VERSION: &'static str = "";
pub const RUNTIME_API_VERSION: &'static str = "2.2.2";
+ pub fn new() -> Self {
+ Self {
+ inner: PartialComposer::default(),
+ locker: None,
+ download_manager: None,
+ plugin_manager: None,
+ autoload_generator: None,
+ archive_manager: None,
+ }
+ }
+
pub fn get_version() -> String {
if Self::VERSION == "@package_version@" {
return Self::SOURCE_VERSION.to_string();
@@ -89,10 +100,16 @@ impl Composer {
self.inner.get_package()
}
- pub fn get_config(&self) -> &crate::config::Config {
+ pub fn get_config(&self) -> &std::rc::Rc<std::cell::RefCell<crate::config::Config>> {
self.inner.get_config()
}
+ pub fn get_config_mut(
+ &mut self,
+ ) -> &mut std::rc::Rc<std::cell::RefCell<crate::config::Config>> {
+ self.inner.get_config_mut()
+ }
+
pub fn get_repository_manager(
&self,
) -> &crate::repository::repository_manager::RepositoryManager {
diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs
index f2e784a..cabd68f 100644
--- a/crates/shirabe/src/config.rs
+++ b/crates/shirabe/src/config.rs
@@ -6,13 +6,14 @@ pub mod json_config_source;
use crate::io::io_interface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
E_USER_DEPRECATED, FILTER_VALIDATE_URL, PHP_URL_HOST, PHP_URL_SCHEME, PhpMixed,
RuntimeException, array_key_exists, array_merge_recursive, array_reverse, array_search_mixed,
array_unique, current, empty, filter_var, implode, in_array, is_array, is_int, is_string, key,
max, parse_url, reset, rtrim, strtolower, strtoupper, strtr, substr, trigger_error,
};
+use std::cell::RefCell;
use crate::advisory::auditor::Auditor;
use crate::config::config_source_interface::ConfigSourceInterface;
@@ -38,7 +39,8 @@ pub struct Config {
/// @var array<string, true>
ssl_verify_warned_hosts: IndexMap<String, bool>,
/// @var array<string, string>
- source_of_config_value: IndexMap<String, String>,
+ // TODO(phase-b): RefCell to allow `&self` access from Config::get / get_with_flags.
+ source_of_config_value: RefCell<IndexMap<String, String>>,
}
impl Config {
@@ -233,7 +235,7 @@ impl Config {
local_auth_config_source: None,
warned_hosts: IndexMap::new(),
ssl_verify_warned_hosts: IndexMap::new(),
- source_of_config_value: IndexMap::new(),
+ source_of_config_value: RefCell::new(IndexMap::new()),
};
let config_clone = this.config.clone();
@@ -380,6 +382,7 @@ impl Config {
m.insert("*".to_string(), Box::new(existing));
self.config.insert(key.clone(), PhpMixed::Array(m));
self.source_of_config_value
+ .borrow_mut()
.insert(format!("{}*", key), source.to_string());
}
let cur = self.config.get(key).cloned().unwrap_or(PhpMixed::Null);
@@ -549,11 +552,20 @@ impl Config {
/// @throws \RuntimeException
///
/// @return mixed
- pub fn get(&mut self, key: &str) -> PhpMixed {
+ pub fn get(&self, key: &str) -> PhpMixed {
self.get_with_flags(key, 0).unwrap_or(PhpMixed::Null)
}
- pub fn get_with_flags(&mut self, key: &str, flags: i64) -> Result<PhpMixed> {
+ // TODO(phase-b): typed convenience; PHP's Config::get() returns mixed.
+ pub fn get_str(&self, key: &str) -> Result<String> {
+ Ok(self
+ .get_with_flags(key, 0)?
+ .as_string()
+ .unwrap_or_default()
+ .to_string())
+ }
+
+ pub fn get_with_flags(&self, key: &str, flags: i64) -> Result<PhpMixed> {
match key {
// strings/paths with env var and {$refs} support
"vendor-dir" | "bin-dir" | "process-timeout" | "data-dir" | "cache-dir"
@@ -645,27 +657,27 @@ impl Config {
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- let matches = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match3(
r"/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i",
&raw,
- );
- let matches = match matches {
- Some(m) => m,
- None => {
- return Err(RuntimeException {
- message: format!("Could not parse the value of '{}': {}", key, raw),
- code: 0,
- }
- .into());
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return Err(RuntimeException {
+ message: format!("Could not parse the value of '{}': {}", key, raw),
+ code: 0,
}
- };
+ .into());
+ }
let mut size = matches
- .get(1)
+ .get(&CaptureKey::ByIndex(1))
.cloned()
.unwrap_or_default()
.parse::<f64>()
.unwrap_or(0.0);
- let unit = matches.get(2).cloned();
+ let unit = matches.get(&CaptureKey::ByIndex(2)).cloned();
if let Some(unit) = unit {
match strtolower(&unit).as_str() {
"g" => {
@@ -980,8 +992,9 @@ impl Config {
}
/// @param mixed $configValue
- fn set_source_of_config_value(&mut self, config_value: &PhpMixed, path: &str, source: &str) {
+ fn set_source_of_config_value(&self, config_value: &PhpMixed, path: &str, source: &str) {
self.source_of_config_value
+ .borrow_mut()
.insert(path.to_string(), source.to_string());
if is_array(config_value.clone()) {
@@ -1033,7 +1046,7 @@ impl Config {
/// @param int $flags Options (see class constants)
///
/// @return string|mixed
- fn process(&mut self, value: PhpMixed, flags: i64) -> PhpMixed {
+ fn process(&self, value: PhpMixed, flags: i64) -> PhpMixed {
if !is_string(&value) {
return value;
}
@@ -1041,8 +1054,10 @@ impl Config {
let value_str = value.as_string().unwrap_or("").to_string();
// TODO(phase-b): Preg::replace_callback with a closure that calls &mut self.get_with_flags
let mut result = value_str.clone();
- if let Some(m) = Preg::is_match_strict_groups(r"#\{\$(.+)\}#", &value_str) {
- let key_match = m.get(1).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"#\{\$(.+)\}#", &value_str, Some(&mut m)).unwrap_or(false)
+ {
+ let key_match = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
let replacement = self
.get_with_flags(&key_match, flags)
.ok()
@@ -1164,12 +1179,12 @@ impl Config {
if let Some(io) = io {
if let Some(ref hostname) = hostname {
if !self.warned_hosts.contains_key(hostname) {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: Accessing {} over {} which is an insecure protocol.</warning>",
hostname,
scheme.as_deref().unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1207,11 +1222,11 @@ impl Config {
}
if let Some(w) = warning {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: Accessing {} with {} disabled.</warning>",
hostname, w
- )),
+ ),
true,
io_interface::NORMAL,
);
diff --git a/crates/shirabe/src/config/config_source_interface.rs b/crates/shirabe/src/config/config_source_interface.rs
index 2a48635..5a23282 100644
--- a/crates/shirabe/src/config/config_source_interface.rs
+++ b/crates/shirabe/src/config/config_source_interface.rs
@@ -3,7 +3,7 @@
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
-pub trait ConfigSourceInterface {
+pub trait ConfigSourceInterface: std::fmt::Debug {
fn add_repository(
&mut self,
name: &str,
diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs
index 675f327..13eb9b3 100644
--- a/crates/shirabe/src/config/json_config_source.rs
+++ b/crates/shirabe/src/config/json_config_source.rs
@@ -199,7 +199,7 @@ impl JsonConfigSource {
}
}
}
- self.file.write(
+ self.file.write2(
config,
shirabe_php_shim::JSON_UNESCAPED_SLASHES
| shirabe_php_shim::JSON_PRETTY_PRINT
diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs
index 13489fd..8d399a6 100644
--- a/crates/shirabe/src/console/application.rs
+++ b/crates/shirabe/src/console/application.rs
@@ -15,7 +15,9 @@ use shirabe_external_packages::symfony::component::console::input::input_definit
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::console_output_interface::ConsoleOutputInterface;
-use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::{
+ self as output_interface, OutputInterface,
+};
use shirabe_external_packages::symfony::component::console::single_command_application::SingleCommandApplication;
use shirabe_external_packages::symfony::component::process::exception::process_timed_out_exception::ProcessTimedOutException;
use shirabe_php_shim::{
@@ -202,7 +204,7 @@ impl Application {
let mut helpers: Vec<
Box<dyn shirabe_external_packages::symfony::component::console::helper::helper::Helper>,
> = vec![];
- helpers.push(Box::new(QuestionHelper::new()));
+ helpers.push(Box::new(QuestionHelper));
let console_io = ConsoleIO::new(input, output, HelperSet::new(helpers));
self.io = Box::new(console_io);
let io = &mut *self.io;
@@ -339,10 +341,11 @@ impl Application {
// Silently clobber any sudo credentials on the invoking user to avoid privilege escalations later on
// ref. https://github.com/composer/composer/issues/5119
let _ = Silencer::call(|| {
- shirabe_php_shim::exec(&format!(
- "sudo -u \\#{} sudo -K > /dev/null 2>&1",
- uid
- ));
+ shirabe_php_shim::exec(
+ &format!("sudo -u \\#{} sudo -K > /dev/null 2>&1", uid),
+ None,
+ None,
+ );
Ok(())
});
}
@@ -350,7 +353,7 @@ impl Application {
// Silently clobber any remaining sudo leases on the current user as well to avoid privilege escalations
let _ = Silencer::call(|| {
- shirabe_php_shim::exec("sudo -K > /dev/null 2>&1");
+ shirabe_php_shim::exec("sudo -K > /dev/null 2>&1", None, None);
Ok(())
});
}
@@ -608,6 +611,7 @@ impl Application {
&map,
composer
.get_config()
+ .borrow()
.get("vendor-dir")
.as_string()
.map(|s| s.to_string()),
@@ -805,8 +809,8 @@ impl Application {
let io = self.get_io();
let is_logic_or_error = exception.downcast_ref::<ShimLogicException>().is_some();
- if is_logic_or_error && output.get_verbosity() < OutputInterface::VERBOSITY_VERBOSE {
- output.set_verbosity(OutputInterface::VERBOSITY_VERBOSE);
+ if is_logic_or_error && output.get_verbosity() < output_interface::VERBOSITY_VERBOSE {
+ output.set_verbosity(output_interface::VERBOSITY_VERBOSE);
}
Silencer::suppress(None);
@@ -817,11 +821,17 @@ impl Application {
let config = composer.get_config();
let min_space_free: f64 = 100.0 * 1024.0 * 1024.0;
- let mut dir = config.get("home").as_string().unwrap_or("").to_string();
+ let mut dir = config
+ .borrow_mut()
+ .get("home")
+ .as_string()
+ .unwrap_or("")
+ .to_string();
let df = disk_free_space(&dir);
let mut hit = df.map(|d| d < min_space_free).unwrap_or(false);
if !hit {
dir = config
+ .borrow_mut()
.get("vendor-dir")
.as_string()
.unwrap_or("")
@@ -898,7 +908,7 @@ impl Application {
.downcast_ref::<ProcessTimedOutException>()
.is_some()
{
- io.write_error(
+ io.write_error3(
"<error>The following exception is caused by a process timeout</error>",
true,
io_interface::QUIET,
diff --git a/crates/shirabe/src/console/html_output_formatter.rs b/crates/shirabe/src/console/html_output_formatter.rs
index c7be6ee..a0099e5 100644
--- a/crates/shirabe/src/console/html_output_formatter.rs
+++ b/crates/shirabe/src/console/html_output_formatter.rs
@@ -1,7 +1,7 @@
//! ref: composer/src/Composer/Console/HtmlOutputFormatter.php
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::formatter::output_formatter_style::OutputFormatterStyle;
@@ -58,12 +58,18 @@ impl HtmlOutputFormatter {
clear_escape_codes, clear_escape_codes
);
- Preg::replace_callback(&pattern, |matches| self.format_html(matches), formatted).ok()
+ Preg::replace_callback(&pattern, |matches| self.format_html(matches), &formatted).ok()
}
- fn format_html(&self, matches: Vec<Option<String>>) -> String {
- let codes_str = matches[1].as_deref().unwrap_or("");
- let content = matches[2].as_deref().unwrap_or("");
+ fn format_html(&self, matches: &IndexMap<CaptureKey, String>) -> String {
+ let codes_str = matches
+ .get(&CaptureKey::ByIndex(1))
+ .map(|s| s.as_str())
+ .unwrap_or("");
+ let content = matches
+ .get(&CaptureKey::ByIndex(2))
+ .map(|s| s.as_str())
+ .unwrap_or("");
let mut out = String::from("<span style=\"");
for code_str in codes_str.split(';') {
diff --git a/crates/shirabe/src/console/input/mod.rs b/crates/shirabe/src/console/input/mod.rs
index f4451af..54f33a8 100644
--- a/crates/shirabe/src/console/input/mod.rs
+++ b/crates/shirabe/src/console/input/mod.rs
@@ -1,2 +1,19 @@
pub mod input_argument;
pub mod input_option;
+
+pub enum InputDefinitionItem {
+ Argument(input_argument::InputArgument),
+ Option(input_option::InputOption),
+}
+
+impl From<input_argument::InputArgument> for InputDefinitionItem {
+ fn from(value: input_argument::InputArgument) -> Self {
+ Self::Argument(value)
+ }
+}
+
+impl From<input_option::InputOption> for InputDefinitionItem {
+ fn from(value: input_option::InputOption) -> Self {
+ Self::Option(value)
+ }
+}
diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs
index 9d8f96c..5d37855 100644
--- a/crates/shirabe/src/dependency_resolver/default_policy.rs
+++ b/crates/shirabe/src/dependency_resolver/default_policy.rs
@@ -51,12 +51,8 @@ impl DefaultPolicy {
ignore_replace: bool,
) -> i64 {
if PackageInterface::get_name(a) == PackageInterface::get_name(b) {
- let a_aliased = (a.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
- let b_aliased = (b.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
+ let a_aliased = a.as_any().downcast_ref::<AliasPackage>().is_some();
+ let b_aliased = b.as_any().downcast_ref::<AliasPackage>().is_some();
if a_aliased && !b_aliased {
return -1;
}
@@ -87,11 +83,11 @@ impl DefaultPolicy {
}
}
- if a.id == b.id {
+ if a.id() == b.id() {
return 0;
}
- if a.id < b.id { -1 } else { 1 }
+ if a.id() < b.id() { -1 } else { 1 }
}
pub(crate) fn group_literals_by_name(
@@ -147,7 +143,7 @@ impl DefaultPolicy {
for &literal in &literals {
let package = pool.literal_to_package(literal);
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
if alias_pkg.is_root_package_alias() {
has_local_alias = true;
break;
@@ -162,7 +158,7 @@ impl DefaultPolicy {
let mut selected = vec![];
for &literal in &literals {
let package = pool.literal_to_package(literal);
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
if alias_pkg.is_root_package_alias() {
selected.push(literal);
}
@@ -221,7 +217,7 @@ impl PolicyInterface for DefaultPolicy {
CompilingMatcher::r#match(
&Constraint::new(operator, b.get_version()),
Constraint::OP_EQ,
- a.get_version(),
+ a.get_version().to_string(),
)
}
diff --git a/crates/shirabe/src/dependency_resolver/generic_rule.rs b/crates/shirabe/src/dependency_resolver/generic_rule.rs
index 19974b6..c0381ef 100644
--- a/crates/shirabe/src/dependency_resolver/generic_rule.rs
+++ b/crates/shirabe/src/dependency_resolver/generic_rule.rs
@@ -6,6 +6,7 @@ use shirabe_php_shim::{PHP_VERSION_ID, PhpMixed, RuntimeException, hash_raw, imp
use super::{request::Request, rule::ReasonData};
+#[derive(Debug)]
pub struct GenericRule {
inner: RuleBase,
pub(crate) literals: Vec<i64>,
@@ -13,7 +14,7 @@ pub struct GenericRule {
impl GenericRule {
pub fn new(mut literals: Vec<i64>, reason: PhpMixed, reason_data: PhpMixed) -> Self {
- let inner = RuleBase::new(reason, reason_data);
+ let inner = RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data));
literals.sort();
Self { inner, literals }
}
@@ -70,6 +71,20 @@ pub trait RuleLiterals {
fn is_multi_conflict_rule(&self) -> bool {
false
}
+ fn is_assertion(&self) -> bool {
+ false
+ }
+ fn is_disabled(&self) -> bool {
+ false
+ }
+ fn as_any(&self) -> &dyn std::any::Any {
+ todo!()
+ }
+ /// Clone this rule into an owned `Box<dyn Rule>` so callers like
+ /// `RuleWatchGraph::propagate_literal` can hand it to `Decisions::decide`.
+ fn clone_rule_box(&self) -> Box<dyn Rule> {
+ todo!()
+ }
}
impl RuleLiterals for GenericRule {
diff --git a/crates/shirabe/src/dependency_resolver/lock_transaction.rs b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
index db9eb90..aa49d17 100644
--- a/crates/shirabe/src/dependency_resolver/lock_transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/lock_transaction.rs
@@ -116,10 +116,7 @@ impl LockTransaction {
.map(|v| v.as_slice())
.unwrap_or_default();
for package in source {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
continue;
}
@@ -152,9 +149,7 @@ impl LockTransaction {
continue;
}
- if let Some(concrete_pkg) =
- (present_package.as_any() as &dyn Any).downcast_ref::<Package>()
- {
+ if let Some(concrete_pkg) = present_package.as_any().downcast_ref::<Package>() {
concrete_pkg.set_source_url(package.get_source_url());
concrete_pkg.set_source_mirrors(package.get_source_mirrors());
}
@@ -171,9 +166,9 @@ impl LockTransaction {
r"{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i",
present_package.get_dist_reference().unwrap(),
package.get_dist_url().unwrap(),
- -1,
- ).unwrap_or_else(|_| package.get_dist_url().unwrap().to_string());
- present_package.set_dist_url(&new_dist_url);
+ )
+ .unwrap_or_else(|_| package.get_dist_url().unwrap().to_string());
+ present_package.set_dist_url(Some(new_dist_url));
}
present_package.set_dist_mirrors(package.get_dist_mirrors());
@@ -192,10 +187,7 @@ impl LockTransaction {
if let Some(all_packages) = self.result_packages.get("all") {
for package in all_packages {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
let mut i = 0;
while i < remaining_aliases.len() {
if remaining_aliases[i].get("package").map(|s| s.as_str())
diff --git a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs
index d198a27..14ece50 100644
--- a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs
+++ b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs
@@ -32,7 +32,7 @@ impl MultiConflictRule {
literals.sort();
Ok(Self {
- inner: RuleBase::new(reason, reason_data),
+ inner: RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data)),
literals,
})
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
index 1835636..53b5f99 100644
--- a/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/install_operation.rs
@@ -34,6 +34,10 @@ impl SolverOperation for InstallOperation {
}
impl OperationInterface for InstallOperation {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_operation_type(&self) -> String {
Self::TYPE.to_string()
}
@@ -45,4 +49,8 @@ impl OperationInterface for InstallOperation {
fn to_string(&self) -> String {
self.show(true)
}
+
+ fn as_install_operation(&self) -> Option<&InstallOperation> {
+ Some(self)
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs
index e5a7df9..fa6f13d 100644
--- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_installed_operation.rs
@@ -25,6 +25,10 @@ impl SolverOperation for MarkAliasInstalledOperation {
}
impl OperationInterface for MarkAliasInstalledOperation {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_operation_type(&self) -> String {
Self::TYPE.to_string()
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs
index 21e257d..b9d5d26 100644
--- a/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/mark_alias_uninstalled_operation.rs
@@ -25,6 +25,10 @@ impl SolverOperation for MarkAliasUninstalledOperation {
}
impl OperationInterface for MarkAliasUninstalledOperation {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_operation_type(&self) -> String {
Self::TYPE.to_string()
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
index d93bd0f..f90649d 100644
--- a/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/operation_interface.rs
@@ -1,6 +1,12 @@
//! ref: composer/src/Composer/DependencyResolver/Operation/OperationInterface.php
+use crate::dependency_resolver::operation::install_operation::InstallOperation;
+use crate::dependency_resolver::operation::uninstall_operation::UninstallOperation;
+use crate::dependency_resolver::operation::update_operation::UpdateOperation;
+
pub trait OperationInterface: std::fmt::Debug {
+ fn as_any(&self) -> &dyn std::any::Any;
+
fn get_operation_type(&self) -> String;
fn show(&self, lock: bool) -> String;
@@ -10,4 +16,16 @@ pub trait OperationInterface: std::fmt::Debug {
fn clone_box(&self) -> Box<dyn OperationInterface> {
todo!()
}
+
+ fn as_install_operation(&self) -> Option<&InstallOperation> {
+ None
+ }
+
+ fn as_update_operation(&self) -> Option<&UpdateOperation> {
+ None
+ }
+
+ fn as_uninstall_operation(&self) -> Option<&UninstallOperation> {
+ None
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
index 138a8c8..be7f6f1 100644
--- a/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/uninstall_operation.rs
@@ -33,6 +33,10 @@ impl SolverOperation for UninstallOperation {
}
impl OperationInterface for UninstallOperation {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_operation_type(&self) -> String {
Self::TYPE.to_string()
}
@@ -44,4 +48,8 @@ impl OperationInterface for UninstallOperation {
fn to_string(&self) -> String {
self.show(true)
}
+
+ fn as_uninstall_operation(&self) -> Option<&UninstallOperation> {
+ Some(self)
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
index c1498ad..9adb248 100644
--- a/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
+++ b/crates/shirabe/src/dependency_resolver/operation/update_operation.rs
@@ -79,6 +79,10 @@ impl SolverOperation for UpdateOperation {
}
impl OperationInterface for UpdateOperation {
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn get_operation_type(&self) -> String {
Self::TYPE.to_string()
}
@@ -94,4 +98,8 @@ impl OperationInterface for UpdateOperation {
fn to_string(&self) -> String {
self.show(true)
}
+
+ fn as_update_operation(&self) -> Option<&UpdateOperation> {
+ Some(self)
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/policy_interface.rs b/crates/shirabe/src/dependency_resolver/policy_interface.rs
index f3cc1a0..606386f 100644
--- a/crates/shirabe/src/dependency_resolver/policy_interface.rs
+++ b/crates/shirabe/src/dependency_resolver/policy_interface.rs
@@ -3,7 +3,7 @@
use crate::dependency_resolver::pool::Pool;
use crate::package::package_interface::PackageInterface;
-pub trait PolicyInterface {
+pub trait PolicyInterface: std::fmt::Debug {
fn version_compare(
&self,
a: &dyn PackageInterface,
diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs
index 96e608b..a194d15 100644
--- a/crates/shirabe/src/dependency_resolver/pool.rs
+++ b/crates/shirabe/src/dependency_resolver/pool.rs
@@ -193,7 +193,7 @@ impl Pool {
let mut id: i64 = 1;
for mut package in packages {
- package.id = id;
+ *package.id_mut() = id;
id += 1;
for provided in package.get_names(true) {
@@ -285,7 +285,7 @@ impl Pool {
) -> String {
let package = self.literal_to_package(literal);
- let prefix = if installed_map.contains_key(&package.id) {
+ let prefix = if installed_map.contains_key(&package.id()) {
if literal > 0 { "keep" } else { "remove" }
} else {
if literal > 0 {
@@ -391,7 +391,7 @@ impl fmt::Display for Pool {
for package in &self.packages {
str.push_str(&format!(
"- {}: {}\n",
- str_pad(&package.id.to_string(), 6, " ", STR_PAD_LEFT),
+ str_pad(&package.id().to_string(), 6, " ", STR_PAD_LEFT),
package.get_name()
));
}
diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs
index ea860f2..3043aaa 100644
--- a/crates/shirabe/src/dependency_resolver/pool_builder.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs
@@ -152,19 +152,12 @@ impl PoolBuilder {
.into());
}
- for locked_package in request.get_locked_repository().unwrap().get_packages() {
+ for locked_package in
+ CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap())
+ {
if !self.is_update_allowed(&*locked_package) {
- // remember which packages we skipped loading remote content for in this partial update
- self.skipped_load
- .entry(locked_package.get_name().to_string())
- .or_insert_with(Vec::new)
- .push(locked_package.clone_box());
- for (_k, link) in &locked_package.get_replaces() {
- self.skipped_load
- .entry(link.get_target().to_string())
- .or_insert_with(Vec::new)
- .push(locked_package.clone_box());
- }
+ // TODO(phase-b): PackageInterface lacks clone_box; PHP shares references.
+ // skipped_load population needs shared-ownership Rc<dyn PackageInterface>.
// Path repo packages are never loaded from lock, to force them to always remain in sync
// unless symlinking is disabled in which case we probably should rather treat them like
@@ -188,7 +181,7 @@ impl PoolBuilder {
}
}
- for package in request.get_fixed_or_locked_packages() {
+ for (_, package) in request.get_fixed_or_locked_packages() {
// using MatchAllConstraint here because fixed packages do not need to retrigger
// loading any packages
self.loaded_packages.insert(
@@ -369,8 +362,9 @@ impl PoolBuilder {
&mut self,
request: &Request,
name: &str,
- constraint: Box<dyn ConstraintInterface>,
+ constraint: &dyn ConstraintInterface,
) {
+ let constraint = constraint.clone_box();
// Skip platform requires at this stage
if PlatformRepository::is_platform_package(name) {
return;
@@ -389,7 +383,7 @@ impl PoolBuilder {
let root_requires = request.get_requires();
let mut constraint = constraint;
if let Some(root_constraint) = root_requires.get(name) {
- if !Intervals::is_subset_of(&*constraint, &**root_constraint) {
+ if !Intervals::is_subset_of(&*constraint, &**root_constraint)? {
constraint = root_constraint.clone_box();
}
}
@@ -401,7 +395,7 @@ impl PoolBuilder {
// MultiConstraint::create() will optimize anyway)
if let Some(existing) = self.packages_to_load.get(name) {
// Already marked for loading and this does not expand the constraint to be loaded, nothing to do
- if Intervals::is_subset_of(&*constraint, &**existing) {
+ if Intervals::is_subset_of(&*constraint, &**existing).unwrap_or(false) {
return;
}
@@ -419,7 +413,9 @@ impl PoolBuilder {
// No need to load this package with this constraint because it is
// a subset of the constraint with which we have already loaded packages
- if Intervals::is_subset_of(&*constraint, &**self.loaded_packages.get(name).unwrap()) {
+ if Intervals::is_subset_of(&*constraint, &**self.loaded_packages.get(name).unwrap())
+ .unwrap_or(false)
+ {
return;
}
@@ -762,7 +758,7 @@ impl PoolBuilder {
fn is_update_allowed(&self, package: &dyn BasePackage) -> bool {
for pattern in &self.update_allow_list {
let pattern_regexp = base_package::package_name_to_regexp(pattern);
- if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None)
+ if Preg::is_match3(&pattern_regexp, PackageInterface::get_name(package), None)
.unwrap_or(false)
{
return true;
@@ -786,8 +782,10 @@ impl PoolBuilder {
let pattern_regexp = base_package::package_name_to_regexp(pattern);
// update pattern matches a locked package? => all good
- for package in request.get_locked_repository().unwrap().get_packages() {
- if Preg::is_match(&pattern_regexp, PackageInterface::get_name(package), None)
+ for package in
+ CanonicalPackagesTrait::get_packages(request.get_locked_repository().unwrap())
+ {
+ if Preg::is_match3(&pattern_regexp, PackageInterface::get_name(package), None)
.unwrap_or(false)
{
continue 'outer;
@@ -795,7 +793,7 @@ impl PoolBuilder {
}
// update pattern matches a root require? => all good, probably a new package
for (package_name, _constraint) in &request.get_requires() {
- if Preg::is_match(&pattern_regexp, package_name, None).unwrap_or(false) {
+ if Preg::is_match3(&pattern_regexp, package_name, None).unwrap_or(false) {
if PlatformRepository::is_platform_package(package_name) {
matched_platform_package = true;
continue;
@@ -855,7 +853,7 @@ impl PoolBuilder {
self.mark_package_name_for_loading(
request,
&replacer_name,
- Box::new(MatchAllConstraint::new()),
+ &MatchAllConstraint::new(),
);
} else {
let pkgs: Vec<Box<dyn BasePackage>> =
@@ -895,7 +893,7 @@ impl PoolBuilder {
// remove locked package by this name which was already initialized
let locked_packages: Vec<Box<dyn BasePackage>> = request
.get_locked_packages()
- .iter()
+ .values()
.map(|p| p.clone_box())
.collect();
for locked_package in &locked_packages {
@@ -914,7 +912,7 @@ impl PoolBuilder {
// that we load that replaced package in case an update to this package removes the replacement
let fixed_or_locked: Vec<Box<dyn BasePackage>> = request
.get_fixed_or_locked_packages()
- .iter()
+ .values()
.map(|p| p.clone_box())
.collect();
for fixed_or_locked_package in &fixed_or_locked {
@@ -1049,7 +1047,7 @@ impl PoolBuilder {
return pool;
}
- self.io.write_with_verbosity(
+ self.io.write3(
&sprintf(
"Pool optimizer completed in %.3f seconds",
&[(microtime(true) - before).into()],
@@ -1057,7 +1055,7 @@ impl PoolBuilder {
true,
io_interface::VERY_VERBOSE,
);
- self.io.write_with_verbosity(
+ self.io.write3(
&sprintf(
"<info>Found %s package versions referenced in your dependency graph. %s (%d%%) were optimized away.</info>",
&[
@@ -1100,7 +1098,7 @@ impl PoolBuilder {
return pool;
}
- self.io.write_with_verbosity(
+ self.io.write3(
&sprintf(
"Security advisory pool filter completed in %.3f seconds",
&[(microtime(true) - before).into()],
@@ -1108,7 +1106,7 @@ impl PoolBuilder {
true,
io_interface::VERY_VERBOSE,
);
- self.io.write_with_verbosity(
+ self.io.write3(
&sprintf(
"<info>Found %s package versions referenced in your dependency graph. %s (%d%%) were filtered away.</info>",
&[
diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
index 5307a1c..3da36b1 100644
--- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
+++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs
@@ -130,9 +130,9 @@ impl PoolOptimizer {
// Keep track of alias packages for every package so if either the alias or aliased is kept
// we keep the others as they are a unit of packages really
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
self.aliases_per_package
- .entry(alias_pkg.get_alias_of().id)
+ .entry(alias_pkg.get_alias_of().id())
.or_insert_with(Vec::new)
.push(package.clone_box());
}
@@ -175,8 +175,8 @@ impl PoolOptimizer {
}
fn mark_package_irremovable(&mut self, package: &dyn BasePackage) {
- self.irremovable_packages.insert(package.id, true);
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ self.irremovable_packages.insert(package.id(), true);
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
// recursing here so aliasesPerPackage for the aliasOf can be checked
// and all its aliases marked as irremovable as well
self.mark_package_irremovable(alias_pkg.get_alias_of());
@@ -184,8 +184,8 @@ impl PoolOptimizer {
// PHP: foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage)
let alias_ids: Vec<i64> = self
.aliases_per_package
- .get(&package.id)
- .map(|aliases| aliases.iter().map(|a| a.id).collect())
+ .get(&package.id())
+ .map(|aliases| aliases.iter().map(|a| a.id()).collect())
.unwrap_or_default();
for alias_id in alias_ids {
self.irremovable_packages.insert(alias_id, true);
@@ -197,7 +197,7 @@ impl PoolOptimizer {
let mut packages: Vec<Box<dyn BasePackage>> = vec![];
let mut removed_versions: IndexMap<String, IndexMap<String, String>> = IndexMap::new();
for package in pool.get_packages() {
- if !self.packages_to_remove.contains_key(&package.id) {
+ if !self.packages_to_remove.contains_key(&package.id()) {
packages.push(package.clone_box());
} else {
removed_versions
@@ -238,11 +238,11 @@ impl PoolOptimizer {
for package in pool.get_packages() {
// If that package was already marked irremovable, we can skip
// the entire process for it
- if self.irremovable_packages.contains_key(&package.id) {
+ if self.irremovable_packages.contains_key(&package.id()) {
continue;
}
- self.mark_package_for_removal(package.id)?;
+ self.mark_package_for_removal(package.id())?;
let dependency_hash = self.calculate_dependency_hash(package.as_ref());
@@ -311,7 +311,7 @@ impl PoolOptimizer {
.or_insert_with(Vec::new)
.push(package.clone_box());
package_identical_definition_lookup
- .entry(package.id)
+ .entry(package.id())
.or_insert_with(IndexMap::new)
.insert(
package_name.clone(),
@@ -344,7 +344,7 @@ impl PoolOptimizer {
let mut literals: Vec<i64> = vec![];
for package in packages {
- literals.push(package.id);
+ literals.push(package.id());
}
for preferred_literal in
@@ -439,13 +439,13 @@ impl PoolOptimizer {
>,
) {
// Already marked to keep
- if !self.packages_to_remove.contains_key(&package.id) {
+ if !self.packages_to_remove.contains_key(&package.id()) {
return;
}
- self.packages_to_remove.shift_remove(&package.id);
+ self.packages_to_remove.shift_remove(&package.id());
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
// recursing here so aliasesPerPackage for the aliasOf can be checked
// and all its aliases marked to be kept as well
self.keep_package(
@@ -457,7 +457,7 @@ impl PoolOptimizer {
// record all the versions of the package group so we can list them later in Problem output
for name in package.get_names(false) {
- if let Some(per_name) = package_identical_definition_lookup.get(&package.id) {
+ if let Some(per_name) = package_identical_definition_lookup.get(&package.id()) {
if let Some(package_group_pointers) = per_name.get(&name) {
let package_group = identical_definitions_per_package
.get(&name)
@@ -466,7 +466,7 @@ impl PoolOptimizer {
if let Some(package_group) = package_group {
for pkg in package_group {
let pkg = if let Some(alias_pkg) =
- (pkg.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
+ pkg.as_any().downcast_ref::<AliasPackage>()
{
if alias_pkg.get_pretty_version()
== VersionParser::DEFAULT_BRANCH_ALIAS
@@ -493,8 +493,13 @@ impl PoolOptimizer {
let alias_info: Vec<(i64, Vec<String>)> = self
.aliases_per_package
- .get(&package.id)
- .map(|aliases| aliases.iter().map(|a| (a.id, a.get_names(false))).collect())
+ .get(&package.id())
+ .map(|aliases| {
+ aliases
+ .iter()
+ .map(|a| (a.id(), a.get_names(false)))
+ .collect()
+ })
.unwrap_or_default();
for (alias_id, alias_names) in alias_info {
self.packages_to_remove.shift_remove(&alias_id);
@@ -510,7 +515,7 @@ impl PoolOptimizer {
if let Some(package_group) = package_group {
for pkg in package_group {
let pkg = if let Some(alias_pkg) =
- (pkg.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
+ pkg.as_any().downcast_ref::<AliasPackage>()
{
if alias_pkg.get_pretty_version()
== VersionParser::DEFAULT_BRANCH_ALIAS
@@ -549,7 +554,7 @@ impl PoolOptimizer {
IndexMap::new();
for package in pool.get_packages() {
- let id = package.id;
+ let id = package.id();
// Do not remove irremovable packages
if self.irremovable_packages.contains_key(&id) {
@@ -557,9 +562,7 @@ impl PoolOptimizer {
}
// Do not remove a package aliased by another package, nor aliases
if self.aliases_per_package.contains_key(&id)
- || (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
+ || package.as_any().downcast_ref::<AliasPackage>().is_some()
{
continue;
}
@@ -573,7 +576,7 @@ impl PoolOptimizer {
package_index
.entry(PackageInterface::get_name(package.as_ref()).to_string())
.or_insert_with(IndexMap::new)
- .insert(package.id, package.clone_box());
+ .insert(package.id(), package.clone_box());
}
for (_, package) in request.get_locked_packages() {
@@ -671,10 +674,9 @@ impl PoolOptimizer {
&self,
constraint: Box<dyn ConstraintInterface>,
) -> Vec<Box<dyn ConstraintInterface>> {
- // TODO(phase-b): Intervals::compactConstraint expects/returns ConstraintInterface
- let constraint = Intervals::compact_constraint(constraint);
+ let constraint = Intervals::compact_constraint(&*constraint).unwrap_or(constraint);
- if let Some(multi) = (constraint.as_ref() as &dyn Any).downcast_ref::<MultiConstraint>() {
+ if let Some(multi) = constraint.as_any().downcast_ref::<MultiConstraint>() {
if multi.is_disjunctive() {
// No need to call ourselves recursively here because Intervals::compactConstraint() ensures that there
// are no nested disjunctive MultiConstraint instances possible
diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs
index 8424941..e78ee14 100644
--- a/crates/shirabe/src/dependency_resolver/problem.rs
+++ b/crates/shirabe/src/dependency_resolver/problem.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_php_shim::{
LogicException, PhpMixed, defined, extension_loaded, implode, in_array, php_to_string,
@@ -212,7 +212,8 @@ impl Problem {
installed_map,
learned_pool,
);
- let m_opt = if in_array(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ let matched = if in_array(
PhpMixed::Int(rule.get_reason()),
&PhpMixed::List(
deduplicatable_rule_types
@@ -222,27 +223,29 @@ impl Problem {
),
true,
) {
- Preg::is_match_strict_groups(
+ Preg::is_match_strict_groups3(
r"{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}",
&message,
+ Some(&mut m),
)
- .unwrap_or(None)
+ .unwrap_or(false)
} else {
- None
+ false
};
- if let Some(m) = m_opt {
+ if matched {
message = str_replace("%", "%%", &message);
let template =
Preg::replace(r"{^\S+ \S+ }", "%s%s ", &message).unwrap_or(message.clone());
messages.push(template.clone());
- let pkg_key = m[1].clone();
- let version_key = parser.normalize(&m[2], "").unwrap_or_default();
+ let pkg_key = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
+ let version_key = parser.normalize(&m2, Some("")).unwrap_or_default();
templates
.entry(template.clone())
.or_insert_with(IndexMap::new)
.entry(pkg_key.clone())
.or_insert_with(IndexMap::new)
- .insert(version_key, m[2].clone());
+ .insert(version_key, m2.clone());
let source_package = rule.get_source_package(pool);
for (version, pretty_version) in
pool.get_removed_versions_by_package(&spl_object_hash(&source_package))
@@ -533,7 +536,7 @@ impl Problem {
}
}
- let mut locked_package: Option<BasePackage> = None;
+ let mut locked_package: Option<Box<dyn BasePackage>> = None;
for package in request.get_locked_packages() {
if package.get_name() == package_name {
locked_package = Some(package.clone());
@@ -554,7 +557,7 @@ impl Problem {
if let Some(c) = constraint {
if c.is_constraint()
&& c.get_operator() == Constraint::STR_OP_EQ
- && Preg::is_match(r"{^dev-.*#.*}", &c.get_pretty_string(), None).unwrap_or(false)
+ && Preg::is_match3(r"{^dev-.*#.*}", &c.get_pretty_string(), None).unwrap_or(false)
{
let new_constraint =
Preg::replace(r"{ +as +([^,\s|]+)$}", "", &c.get_pretty_string())
@@ -606,7 +609,7 @@ impl Problem {
let filtered: Vec<&Box<dyn PackageInterface>> = packages
.iter()
.filter(|p| {
- root_reqs[package_name].matches(&Constraint::new("==", &p.get_version()))
+ root_reqs[package_name].matches(&Constraint::new("==", p.get_version()))
})
.collect();
if filtered.len() == 0 {
@@ -638,12 +641,12 @@ impl Problem {
let temp_reqs = repository_set.get_temporary_constraints();
let first_pkg = packages.first().unwrap();
- for name in first_pkg.get_names() {
+ for name in first_pkg.get_names(true) {
if temp_reqs.contains_key(&name) {
let filtered: Vec<&Box<dyn PackageInterface>> = packages
.iter()
.filter(|p| {
- temp_reqs[&name].matches(&Constraint::new("==", &p.get_version()))
+ temp_reqs[&name].matches(&Constraint::new("==", p.get_version()))
})
.collect();
if filtered.len() == 0 {
@@ -676,10 +679,10 @@ impl Problem {
}
if let Some(ref lp) = locked_package {
- let fixed_constraint = Constraint::new("==", &lp.get_version());
+ let fixed_constraint = Constraint::new("==", lp.get_version());
let filtered: Vec<&Box<dyn PackageInterface>> = packages
.iter()
- .filter(|p| fixed_constraint.matches(&Constraint::new("==", &p.get_version())))
+ .filter(|p| fixed_constraint.matches(&Constraint::new("==", p.get_version())))
.collect();
if filtered.len() == 0 {
return (
@@ -763,22 +766,22 @@ impl Problem {
return format!(
"<href={}>{}</>",
OutputFormatter::escape(advisory.link.as_ref().unwrap()),
- advisory.advisory_id
+ advisory.inner.advisory_id
);
}
- if str_starts_with(&advisory.advisory_id, "PKSA-") {
+ if str_starts_with(&advisory.inner.advisory_id, "PKSA-") {
return format!(
"<href={}>{}</>",
OutputFormatter::escape(&format!(
"https://packagist.org/security-advisories/{}",
- advisory.advisory_id
+ advisory.inner.advisory_id
)),
- advisory.advisory_id
+ advisory.inner.advisory_id
);
}
- advisory.advisory_id.clone()
+ advisory.inner.advisory_id.clone()
})
.collect()
} else {
@@ -961,7 +964,7 @@ impl Problem {
);
}
- if !Preg::is_match(r"{^[A-Za-z0-9_./-]+$}", package_name, None).unwrap_or(false) {
+ if !Preg::is_match3(r"{^[A-Za-z0-9_./-]+$}", package_name, None).unwrap_or(false) {
let illegal_chars =
Preg::replace(r"{[A-Za-z0-9_./-]+}", "", package_name).unwrap_or_default();
@@ -1365,7 +1368,7 @@ impl Problem {
&& c.get_operator() == Constraint::STR_OP_EQ
&& !str_starts_with(&c.get_version(), "dev-")
{
- if !Preg::is_match(r"{^\d+(?:\.\d+)*$}", &c.get_pretty_string(), None)
+ if !Preg::is_match3(r"{^\d+(?:\.\d+)*$}", &c.get_pretty_string(), None)
.unwrap_or(false)
{
return format!(" {} (exact version match)", c.get_pretty_string());
diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs
index 23a5981..e54df18 100644
--- a/crates/shirabe/src/dependency_resolver/rule.rs
+++ b/crates/shirabe/src/dependency_resolver/rule.rs
@@ -37,6 +37,21 @@ pub enum ReasonData {
Fixed {
package: Box<dyn BasePackage>,
},
+ /// Phase B placeholder for an arbitrary PHP-side value not yet mapped to a real variant.
+ Mixed(PhpMixed),
+}
+
+impl From<PhpMixed> for ReasonData {
+ fn from(value: PhpMixed) -> Self {
+ // TODO(phase-b): callers should construct the appropriate variant directly;
+ // this catch-all keeps the rule constructors building while reason_data threading
+ // through PhpMixed in the resolver is still in transition.
+ match value {
+ PhpMixed::String(s) => ReasonData::String(s),
+ PhpMixed::Int(i) => ReasonData::Int(i),
+ other => ReasonData::Mixed(other),
+ }
+ }
}
// reason constants and // their reason data contents
@@ -54,7 +69,7 @@ pub const BITFIELD_TYPE: i64 = 0;
pub const BITFIELD_REASON: i64 = 8;
pub const BITFIELD_DISABLED: i64 = 16;
-pub trait Rule: std::fmt::Display {
+pub trait Rule: std::fmt::Display + std::fmt::Debug {
fn bitfield(&self) -> i64;
fn bitfield_mut(&mut self) -> &mut i64;
fn request(&self) -> Option<&Request>;
@@ -67,14 +82,19 @@ pub trait Rule: std::fmt::Display {
fn equals(&self, rule: &dyn Rule) -> bool;
fn is_assertion(&self) -> bool;
+ fn clone_box(&self) -> Box<dyn Rule> {
+ todo!()
+ }
+
/// @return self::RULE_*
fn get_reason(&self) -> i64 {
- (self.bitfield & (255 << Self::BITFIELD_REASON)) >> Self::BITFIELD_REASON
+ (self.bitfield() & (255 << BITFIELD_REASON)) >> BITFIELD_REASON
}
/// @phpstan-return ReasonData
fn get_reason_data(&self) -> &ReasonData {
- &self.reason_data()
+ // TODO(phase-b): reason_data() returns Option; PHP getReasonData unconditional
+ self.reason_data().unwrap()
}
fn get_required_package(&self) -> Option<String> {
@@ -97,29 +117,29 @@ pub trait Rule: std::fmt::Display {
/// @param RuleSet::TYPE_* $type
fn set_type(&mut self, r#type: i64) {
- self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_TYPE))
- | ((255 & r#type) << Self::BITFIELD_TYPE);
+ *self.bitfield_mut() =
+ (self.bitfield() & !(255i64 << BITFIELD_TYPE)) | ((255 & r#type) << BITFIELD_TYPE);
}
fn get_type(&self) -> i64 {
- (self.bitfield & (255 << Self::BITFIELD_TYPE)) >> Self::BITFIELD_TYPE
+ (self.bitfield() & (255 << BITFIELD_TYPE)) >> BITFIELD_TYPE
}
fn disable(&mut self) {
- self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_DISABLED))
- | (1i64 << Self::BITFIELD_DISABLED);
+ *self.bitfield_mut() =
+ (self.bitfield() & !(255i64 << BITFIELD_DISABLED)) | (1i64 << BITFIELD_DISABLED);
}
fn enable(&mut self) {
- self.bitfield &= !(255i64 << Self::BITFIELD_DISABLED);
+ *self.bitfield_mut() &= !(255i64 << BITFIELD_DISABLED);
}
fn is_disabled(&self) -> bool {
- 0 != ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED)
+ 0 != ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED)
}
fn is_enabled(&self) -> bool {
- 0 == ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED)
+ 0 == ((self.bitfield() & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED)
}
fn is_caused_by_lock(
@@ -195,10 +215,12 @@ pub trait Rule: std::fmt::Display {
match self.get_reason() {
r if r == Self::RULE_PACKAGE_CONFLICT => {
- let mut package1 =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0]));
- let mut package2 =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1]));
+ let mut package1 = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[0]).clone_box(),
+ );
+ let mut package2 = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[1]).clone_box(),
+ );
let reason_data = self.get_reason_data();
// swap literals if they are not in the right order with package2 being the conflicter
@@ -213,8 +235,9 @@ pub trait Rule: std::fmt::Display {
r if r == Self::RULE_PACKAGE_REQUIRES => {
let source_literal = literals[0];
- let source_package =
- self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal));
+ let source_package = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(source_literal).clone_box(),
+ );
Ok(source_package)
}
@@ -264,11 +287,7 @@ pub trait Rule: std::fmt::Display {
// PHP: array_values(array_filter($packages, fn ($p) => !($p instanceof AliasPackage)))
let packages_non_alias: Vec<Box<dyn BasePackage>> = packages
.iter()
- .filter(|p| {
- (p.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_none()
- })
+ .filter(|p| p.as_any().downcast_ref::<AliasPackage>().is_none())
.map(|p| p.clone_box())
.collect();
if packages_non_alias.len() == 1 {
@@ -320,10 +339,12 @@ pub trait Rule: std::fmt::Display {
}
r if r == Self::RULE_PACKAGE_CONFLICT => {
- let mut package1 =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0]));
- let mut package2 =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1]));
+ let mut package1 = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[0]).clone_box(),
+ );
+ let mut package2 = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[1]).clone_box(),
+ );
let mut conflict_target = package1.get_pretty_string();
let reason_data = self.get_reason_data();
@@ -386,8 +407,9 @@ pub trait Rule: std::fmt::Display {
r if r == Self::RULE_PACKAGE_REQUIRES => {
assert!(literals.len() > 0);
let source_literal = array_shift(&mut literals).unwrap();
- let source_package =
- self.deduplicate_default_branch_alias(pool.literal_to_package(source_literal));
+ let source_package = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(source_literal).clone_box(),
+ );
let reason_data = self.get_reason_data();
let link = match reason_data {
ReasonData::Link(l) => l,
@@ -522,7 +544,7 @@ pub trait Rule: std::fmt::Display {
let mut groups: IndexMap<String, Vec<Box<dyn BasePackage>>> = IndexMap::new();
for literal in &literals {
let package = pool.literal_to_package(*literal);
- let group = if installed_map.contains_key(&package.id) {
+ let group = if installed_map.contains_key(&package.id()) {
if *literal > 0 { "keep" } else { "remove" }
} else {
if *literal > 0 {
@@ -565,8 +587,9 @@ pub trait Rule: std::fmt::Display {
if alias_package.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
return String::new();
}
- let package =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[1]));
+ let package = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[1]).clone_box(),
+ );
format!(
"{} is an alias of {} and thus requires it to be installed too.",
@@ -582,8 +605,9 @@ pub trait Rule: std::fmt::Display {
if alias_package.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
return String::new();
}
- let package =
- self.deduplicate_default_branch_alias(pool.literal_to_package(literals[0]));
+ let package = self.deduplicate_default_branch_alias(
+ pool.literal_to_package(literals[0]).clone_box(),
+ );
format!(
"{} is an alias of {} and must be installed with it.",
@@ -656,7 +680,7 @@ pub trait Rule: std::fmt::Display {
&self,
package: Box<dyn BasePackage>,
) -> Box<dyn BasePackage> {
- if let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
if alias_pkg.get_pretty_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
return alias_pkg.get_alias_of().clone_box();
}
@@ -666,21 +690,29 @@ pub trait Rule: std::fmt::Display {
}
}
+#[derive(Debug)]
pub struct RuleBase {
- bitfield: i64,
- request: Option<Request>,
- reason_data: Option<ReasonData>,
+ pub(crate) bitfield: i64,
+ pub(crate) request: Option<Request>,
+ pub(crate) reason_data: Option<ReasonData>,
}
impl RuleBase {
- fn new(reason: i64, reason_data: ReasonData) -> Self {
- let bitfield = (0i64 << Self::BITFIELD_DISABLED)
- | (reason << Self::BITFIELD_REASON)
- | (255i64 << Self::BITFIELD_TYPE);
+ pub const BITFIELD_DISABLED: i64 = BITFIELD_DISABLED;
+ pub const BITFIELD_REASON: i64 = BITFIELD_REASON;
+ pub const BITFIELD_TYPE: i64 = BITFIELD_TYPE;
+
+ pub fn new(reason: i64, reason_data: ReasonData) -> Self {
+ let bitfield =
+ (0i64 << BITFIELD_DISABLED) | (reason << BITFIELD_REASON) | (255i64 << BITFIELD_TYPE);
Self {
bitfield,
request: None,
reason_data: Some(reason_data),
}
}
+
+ pub fn is_disabled(&self) -> bool {
+ 0 != ((self.bitfield & (255 << BITFIELD_DISABLED)) >> BITFIELD_DISABLED)
+ }
}
diff --git a/crates/shirabe/src/dependency_resolver/rule2_literals.rs b/crates/shirabe/src/dependency_resolver/rule2_literals.rs
index cdd1a9c..1c98200 100644
--- a/crates/shirabe/src/dependency_resolver/rule2_literals.rs
+++ b/crates/shirabe/src/dependency_resolver/rule2_literals.rs
@@ -28,7 +28,7 @@ impl Rule2Literals {
};
Self {
- inner: RuleBase::new(reason, reason_data),
+ inner: RuleBase::new(reason.as_int().unwrap_or(0), ReasonData::from(reason_data)),
literal1,
literal2,
literals: vec![literal1, literal2],
diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs
index 46ee4d2..032790e 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set.rs
@@ -92,6 +92,10 @@ impl RuleSet {
&*self.rule_by_id[&id]
}
+ pub fn rule_by_id_mut(&mut self, id: i64) -> &mut dyn Rule {
+ &mut *self.rule_by_id.get_mut(&id).unwrap()
+ }
+
pub fn get_rules(&self) -> &IndexMap<i64, Vec<Box<dyn Rule>>> {
&self.rules
}
diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
index d5bb1cd..6cbe93b 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs
@@ -123,16 +123,15 @@ impl RuleSetGenerator {
if literals.len() == 2 {
// Rule2Literals and MultiConflictRule both implement Rule (Phase B: define Rule type)
- Rule::from(Rule2Literals::new(
+ Box::new(Rule2Literals::new(
literals[0],
literals[1],
PhpMixed::Int(reason),
reason_data,
- ))
+ )) as Box<dyn Rule>
} else {
- Rule::from(
- MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap(),
- )
+ Box::new(MultiConflictRule::new(literals, PhpMixed::Int(reason), reason_data).unwrap())
+ as Box<dyn Rule>
}
}
@@ -159,42 +158,45 @@ impl RuleSetGenerator {
continue;
}
- self.added_map.insert(package.get_id(), package.clone_box());
+ self.added_map
+ .insert(package.get_id(), package.clone_package_box());
- let is_alias = (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
+ let is_alias = package.as_any().downcast_ref::<AliasPackage>().is_some();
if !is_alias {
for name in package.get_names(false) {
self.added_packages_by_names
.entry(name)
.or_default()
- .push(package.clone_box());
+ .push(package.clone_package_box());
}
} else {
- let alias_pkg = (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .unwrap();
+ let alias_pkg = package.as_any().downcast_ref::<AliasPackage>().unwrap();
- work_queue.push_back(alias_pkg.get_alias_of().clone_box());
+ work_queue.push_back(alias_pkg.get_alias_of().clone_package_box());
let alias_of = alias_pkg.get_alias_of();
let rule = self.create_require_rule(
&*package,
- &[alias_of.clone_box()],
+ &[alias_of.clone_package_box()],
rule::RULE_PACKAGE_ALIAS,
PhpMixed::Null, // reasonData: $package (BasePackage)
);
- self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from));
+ self.add_rule(
+ RuleSet::TYPE_PACKAGE,
+ rule.map(|r| Box::new(r) as Box<dyn Rule>),
+ );
// aliases must be installed with their main package, so create a rule the other way around as well
let inverse_rule = self.create_require_rule(
alias_of,
- &[package.clone_box()],
+ &[package.clone_package_box()],
rule::RULE_PACKAGE_INVERSE_ALIAS,
PhpMixed::Null, // reasonData: $package->getAliasOf() (BasePackage)
);
- self.add_rule(RuleSet::TYPE_PACKAGE, inverse_rule.map(Rule::from));
+ self.add_rule(
+ RuleSet::TYPE_PACKAGE,
+ inverse_rule.map(|r| Box::new(r) as Box<dyn Rule>),
+ );
// if alias package has no self.version requires, its requirements do not
// need to be added as the aliased package processing will take care of it
@@ -207,7 +209,8 @@ impl RuleSetGenerator {
let mut constraint = link.get_constraint().clone_box();
if platform_requirement_filter.is_ignored(link.get_target()) {
continue;
- } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any)
+ } else if let Some(ignore_list_filter) = platform_requirement_filter
+ .as_any()
.downcast_ref::<IgnoreListPlatformRequirementFilter>(
) {
constraint = ignore_list_filter
@@ -223,7 +226,10 @@ impl RuleSetGenerator {
rule::RULE_PACKAGE_REQUIRES,
PhpMixed::Null, // reasonData: $link (Link)
);
- self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from));
+ self.add_rule(
+ RuleSet::TYPE_PACKAGE,
+ rule.map(|r| Box::new(r) as Box<dyn Rule>),
+ );
for require in possible_requires {
work_queue.push_back(require);
@@ -252,7 +258,8 @@ impl RuleSetGenerator {
let mut constraint = link.get_constraint().clone_box();
if platform_requirement_filter.is_ignored(link.get_target()) {
continue;
- } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any)
+ } else if let Some(ignore_list_filter) = platform_requirement_filter
+ .as_any()
.downcast_ref::<IgnoreListPlatformRequirementFilter>(
) {
constraint = ignore_list_filter
@@ -266,9 +273,8 @@ impl RuleSetGenerator {
// define the conflict rule for regular packages, for alias packages it's only needed if the name
// matches the conflict exactly, otherwise the name match is by provide/replace which means the
// package which this is an alias of will conflict anyway, so no need to create additional rules
- let conflict_is_alias = (conflict.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
+ let conflict_is_alias =
+ conflict.as_any().downcast_ref::<AliasPackage>().is_some();
let conflict_name_matches = conflict.get_name() == link.get_target();
if !conflict_is_alias || conflict_name_matches {
let rule = self.create_rule2_literals(
@@ -277,7 +283,10 @@ impl RuleSetGenerator {
rule::RULE_PACKAGE_CONFLICT,
PhpMixed::Null, // reasonData: $link (Link)
);
- self.add_rule(RuleSet::TYPE_PACKAGE, rule.map(Rule::from));
+ self.add_rule(
+ RuleSet::TYPE_PACKAGE,
+ rule.map(|r| Box::new(r) as Box<dyn Rule>),
+ );
}
}
}
@@ -336,14 +345,15 @@ impl RuleSetGenerator {
rule::RULE_FIXED,
PhpMixed::Array(reason_data),
);
- self.add_rule(RuleSet::TYPE_REQUEST, Some(Rule::from(rule)));
+ self.add_rule(RuleSet::TYPE_REQUEST, Some(Box::new(rule) as Box<dyn Rule>));
}
for (package_name, constraint) in request.get_requires() {
let mut constraint = constraint.clone_box();
if platform_requirement_filter.is_ignored(package_name) {
continue;
- } else if let Some(ignore_list_filter) = (platform_requirement_filter as &dyn Any)
+ } else if let Some(ignore_list_filter) = platform_requirement_filter
+ .as_any()
.downcast_ref::<IgnoreListPlatformRequirementFilter>(
) {
constraint = ignore_list_filter
@@ -371,7 +381,7 @@ impl RuleSetGenerator {
rule::RULE_ROOT_REQUIRE,
PhpMixed::Array(reason_data),
);
- self.add_rule(RuleSet::TYPE_REQUEST, Some(Rule::from(rule)));
+ self.add_rule(RuleSet::TYPE_REQUEST, Some(Box::new(rule) as Box<dyn Rule>));
}
}
@@ -387,7 +397,7 @@ impl RuleSetGenerator {
// even if the alias itself isn't required, otherwise a package could be installed without its alias which
// leads to unexpected behavior
let is_not_added = !self.added_map.contains_key(&package.get_id());
- let as_alias = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>();
+ let as_alias = package.as_any().downcast_ref::<AliasPackage>();
if is_not_added {
if let Some(alias_pkg) = as_alias {
if alias_pkg.is_root_package_alias()
diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
index 0524fcb..a8176ee 100644
--- a/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
@@ -27,7 +27,10 @@ impl RuleWatchGraph {
return;
}
- let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any)
+ let is_multi_conflict = node
+ .borrow()
+ .get_rule()
+ .as_any()
.downcast_ref::<MultiConflictRule>()
.is_some();
@@ -72,7 +75,10 @@ impl RuleWatchGraph {
self.watch_chains.get_mut(&literal).unwrap().rewind();
while self.watch_chains.get(&literal).unwrap().valid() {
let node = self.watch_chains.get(&literal).unwrap().current().clone();
- let is_multi_conflict = (node.borrow().get_rule().as_any() as &dyn Any)
+ let is_multi_conflict = node
+ .borrow()
+ .get_rule()
+ .as_any()
.downcast_ref::<MultiConflictRule>()
.is_some();
if !is_multi_conflict {
diff --git a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs
index 12dd83f..6c1bada 100644
--- a/crates/shirabe/src/dependency_resolver/rule_watch_node.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_watch_node.rs
@@ -57,6 +57,12 @@ impl RuleWatchNode {
self.rule.as_ref()
}
+ /// Owned clone for callers that need a `Box<dyn Rule>`. Default impl in
+ /// `RuleLiterals` returns `todo!()`; concrete rule impls override it.
+ pub fn get_rule_boxed(&self) -> Box<dyn crate::dependency_resolver::rule::Rule> {
+ self.rule.clone_rule_box()
+ }
+
pub fn get_other_watch(&self, literal: i64) -> i64 {
if self.watch1 == literal {
return self.watch2;
diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs
index 2160b97..b4b878b 100644
--- a/crates/shirabe/src/dependency_resolver/solver.rs
+++ b/crates/shirabe/src/dependency_resolver/solver.rs
@@ -89,7 +89,7 @@ impl Solver {
let rules_count = self.rules.count();
let mut rule_index = 0_i64;
while rule_index < rules_count {
- let rule = self.rules.rule_by_id(rule_index).clone();
+ let rule = self.rules.rule_by_id(rule_index).clone_box();
if !rule.is_assertion() || rule.is_disabled() {
rule_index += 1;
@@ -100,7 +100,7 @@ impl Solver {
let literal = literals[0];
if !self.decisions.decided(literal) {
- self.decisions.decide(literal, 1, rule.clone());
+ self.decisions.decide(literal, 1, rule.clone_box());
rule_index += 1;
continue;
}
@@ -118,12 +118,12 @@ impl Solver {
continue;
}
- let conflict = self.decisions.decision_rule(literal).clone();
+ let conflict = self.decisions.decision_rule(literal).clone_box();
if RuleSet::TYPE_PACKAGE == conflict.get_type() {
let mut problem = Problem::new();
- problem.add_rule(rule.clone());
+ problem.add_rule(rule.clone_box());
problem.add_rule(conflict);
self.rules.rule_by_id_mut(rule_index).disable()?;
self.problems.push(problem);
@@ -144,7 +144,7 @@ impl Solver {
.ids()
.collect();
for assert_rule_id in request_rules {
- let assert_rule = self.rules.rule_by_id(assert_rule_id).clone();
+ let assert_rule = self.rules.rule_by_id(assert_rule_id).clone_box();
if assert_rule.is_disabled() || !assert_rule.is_assertion() {
continue;
}
@@ -204,7 +204,7 @@ impl Solver {
// TODO(phase-b): store the constraint inside reason_data; PhpMixed needs to
// accept a `dyn ConstraintInterface` wrapper.
reason_data.insert("constraint".to_string(), PhpMixed::Null);
- problem.add_rule(Rule::generic(GenericRule::new(
+ problem.add_rule(Box::new(GenericRule::new(
Vec::new(),
PhpMixed::Int(rule::RULE_ROOT_REQUIRE),
PhpMixed::Array(
@@ -213,7 +213,7 @@ impl Solver {
.map(|(k, v)| (k, Box::new(v)))
.collect(),
),
- )));
+ )) as Box<dyn Rule>);
self.problems.push(problem);
}
}
@@ -229,11 +229,8 @@ impl Solver {
self.setup_fixed_map(request);
- self.io.write_error(
- PhpMixed::String("Generating rules".to_string()),
- true,
- crate::io::io_interface::DEBUG,
- );
+ self.io
+ .write_error3("Generating rules", true, crate::io::io_interface::DEBUG);
let mut rule_set_generator =
RuleSetGenerator::new(self.policy.clone_box(), self.pool.clone());
self.rules =
@@ -253,23 +250,20 @@ impl Solver {
// make decisions based on root require/fix assertions
self.make_assertion_rule_decisions()?;
- self.io.write_error(
- PhpMixed::String("Resolving dependencies through SAT".to_string()),
+ self.io.write_error3(
+ "Resolving dependencies through SAT",
true,
crate::io::io_interface::DEBUG,
);
let before = microtime(true);
self.run_sat()?;
- self.io.write_error(
- PhpMixed::String("".to_string()),
- true,
- crate::io::io_interface::DEBUG,
- );
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io
+ .write_error3("", true, crate::io::io_interface::DEBUG);
+ self.io.write_error3(
+ &sprintf(
"Dependency resolution completed in %.3f seconds",
&[PhpMixed::Float(microtime(true) - before)],
- )),
+ ),
true,
crate::io::io_interface::VERBOSE,
);
@@ -767,8 +761,8 @@ impl Solver {
let mut rules_count = self.rules.count();
let mut pass = 1_i64;
- self.io.write_error(
- PhpMixed::String("Looking at all rules.".to_string()),
+ self.io.write_error3(
+ "Looking at all rules.",
true,
crate::io::io_interface::DEBUG,
);
@@ -777,20 +771,20 @@ impl Solver {
while n < rules_count {
if i == rules_count {
if 1 == pass {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Something's changed, looking at all rules again (pass #{})",
pass
- )),
+ ),
false,
crate::io::io_interface::DEBUG,
);
} else {
- self.io.overwrite_error(
- PhpMixed::String(format!(
+ self.io.overwrite_error4(
+ &format!(
"Something's changed, looking at all rules again (pass #{})",
pass
- )),
+ ),
false,
None,
crate::io::io_interface::DEBUG,
@@ -801,7 +795,7 @@ impl Solver {
pass += 1;
}
- let rule = self.rules.rule_by_id(i).clone();
+ let rule = self.rules.rule_by_id(i).clone_box();
let literals = rule.get_literals().clone();
if rule.is_disabled() {
diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs
index 4ab5c6b..8c4595f 100644
--- a/crates/shirabe/src/dependency_resolver/transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/transaction.rs
@@ -37,6 +37,17 @@ pub struct Transaction {
pub(crate) result_packages_by_name: IndexMap<String, Vec<Box<dyn PackageInterface>>>,
}
+impl Default for Transaction {
+ fn default() -> Self {
+ Self {
+ operations: vec![],
+ present_packages: vec![],
+ result_package_map: IndexMap::new(),
+ result_packages_by_name: IndexMap::new(),
+ }
+ }
+}
+
impl Transaction {
/// @param PackageInterface[] $presentPackages
/// @param PackageInterface[] $resultPackages
@@ -67,12 +78,8 @@ impl Transaction {
let _package_sort = |a: &dyn PackageInterface, b: &dyn PackageInterface| -> i64 {
// sort alias packages by the same name behind their non alias version
if a.get_name() == b.get_name() {
- let a_is_alias = (a.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
- let b_is_alias = (b.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some();
+ let a_is_alias = a.as_any().downcast_ref::<AliasPackage>().is_some();
+ let b_is_alias = b.as_any().downcast_ref::<AliasPackage>().is_some();
if a_is_alias != b_is_alias {
return if a_is_alias { -1 } else { 1 };
}
@@ -119,10 +126,7 @@ impl Transaction {
let mut present_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
let mut remove_alias_map: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
for package in &self.present_packages {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
let key = format!("{}::{}", package.get_name(), package.get_version());
present_alias_map.insert(key.clone(), package.clone_package_box());
remove_alias_map.insert(key, package.clone_package_box());
@@ -151,7 +155,7 @@ impl Transaction {
visited.insert(spl_object_hash(package.as_ref()), true);
stack.push(package.clone_package_box());
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
stack.push(alias.get_alias_of().clone_package_box());
} else {
for link in package.get_requires().values() {
@@ -165,10 +169,7 @@ impl Transaction {
} else if !processed.contains_key(&spl_object_hash(package.as_ref())) {
processed.insert(spl_object_hash(package.as_ref()), true);
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
let alias_key = format!("{}::{}", package.get_name(), package.get_version());
if present_alias_map.contains_key(&alias_key) {
remove_alias_map.shift_remove(&alias_key);
@@ -308,12 +309,10 @@ impl Transaction {
let op = &operations[idx];
let package: Box<dyn PackageInterface> = if let Some(install_op) =
- (op.as_ref() as &dyn Any).downcast_ref::<InstallOperation>()
+ op.as_ref().as_any().downcast_ref::<InstallOperation>()
{
install_op.get_package().clone_package_box()
- } else if let Some(update_op) =
- (op.as_ref() as &dyn Any).downcast_ref::<UpdateOperation>()
- {
+ } else if let Some(update_op) = op.as_ref().as_any().downcast_ref::<UpdateOperation>() {
update_op.get_target_package().clone_package_box()
} else {
continue;
@@ -415,10 +414,14 @@ impl Transaction {
let mut uninst_ops: Vec<Box<dyn OperationInterface>> = vec![];
let mut to_remove: Vec<usize> = vec![];
for (idx, op) in operations.iter().enumerate() {
- let is_uninstall = (op.as_ref() as &dyn Any)
+ let is_uninstall = op
+ .as_ref()
+ .as_any()
.downcast_ref::<UninstallOperation>()
.is_some()
- || (op.as_ref() as &dyn Any)
+ || op
+ .as_ref()
+ .as_any()
.downcast_ref::<MarkAliasUninstalledOperation>()
.is_some();
if is_uninstall {
diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs
index 02cd8a9..937add0 100644
--- a/crates/shirabe/src/downloader/archive_downloader.rs
+++ b/crates/shirabe/src/downloader/archive_downloader.rs
@@ -69,7 +69,7 @@ pub trait ArchiveDownloader {
));
}
- let vendor_dir = self.inner().config.get("vendor-dir");
+ let vendor_dir = self.inner().config.borrow_mut().get("vendor-dir");
// clean up the target directory, unless it contains the vendor dir, as the vendor dir contains
// the archive to be extracted. This is the case when installing with create-project in the current directory
@@ -77,15 +77,20 @@ pub trait ArchiveDownloader {
if !self
.inner()
.filesystem
+ .borrow()
.normalize_path(&vendor_dir)
.contains(
&self
.inner()
.filesystem
+ .borrow()
.normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR)),
)
{
- self.inner_mut().filesystem.empty_directory(path);
+ self.inner_mut()
+ .filesystem
+ .borrow_mut()
+ .empty_directory(path);
}
let temporary_dir;
@@ -105,6 +110,7 @@ pub trait ArchiveDownloader {
self.inner_mut()
.filesystem
+ .borrow_mut()
.ensure_directory_exists(&temporary_dir);
let file_name = self.inner().get_file_name(package, path);
@@ -115,9 +121,9 @@ pub trait ArchiveDownloader {
self.inner_mut().clear_last_cache_write(package);
// clean up
- filesystem.remove_directory(&temporary_dir);
+ filesystem.borrow_mut().remove_directory(&temporary_dir);
if is_dir(path) && realpath(path) != Platform::get_cwd(false).unwrap_or_default() {
- filesystem.remove_directory(path);
+ filesystem.borrow_mut().remove_directory(path);
}
self.inner_mut()
.remove_cleanup_path(package, &temporary_dir);
@@ -138,7 +144,7 @@ pub trait ArchiveDownloader {
Ok(promise.then(
Box::new(move || -> Result<Box<dyn PromiseInterface>> {
if file_exists(&file_name) {
- filesystem.unlink(&file_name);
+ filesystem.borrow_mut().unlink(&file_name);
}
let get_folder_content = |dir: &str| -> Vec<std::path::PathBuf> {
@@ -177,7 +183,7 @@ pub trait ArchiveDownloader {
&format!("{}/{}", to, file_basename),
)?;
} else {
- filesystem.rename(&file, &format!("{}/{}", to, file_basename));
+ filesystem.borrow_mut().rename(&file, &format!("{}/{}", to, file_basename));
}
}
@@ -187,8 +193,8 @@ pub trait ArchiveDownloader {
let mut rename_as_one = false;
if !file_exists(path) {
rename_as_one = true;
- } else if filesystem.is_dir_empty(path) {
- match filesystem.remove_directory_php(path) {
+ } else if filesystem.borrow().is_dir_empty(path) {
+ match filesystem.borrow_mut().remove_directory_php(path) {
Ok(true) => {
rename_as_one = true;
}
@@ -210,7 +216,7 @@ pub trait ArchiveDownloader {
} else {
temporary_dir.clone()
};
- filesystem.rename(&extracted_dir, path);
+ filesystem.borrow_mut().rename(&extracted_dir, path);
} else {
// only one dir in the archive, extract its contents out of it
let from = if single_dir_at_top_level {
@@ -222,7 +228,7 @@ pub trait ArchiveDownloader {
rename_recursively.as_ref().unwrap()(&from, path)?;
}
- let promise = filesystem.remove_directory_async(&temporary_dir);
+ let promise = filesystem.borrow_mut().remove_directory_async(&temporary_dir);
Ok(promise.then(
Box::new(move || -> Result<()> {
diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs
index db3484e..ea2ee1e 100644
--- a/crates/shirabe/src/downloader/download_manager.rs
+++ b/crates/shirabe/src/downloader/download_manager.rs
@@ -29,7 +29,7 @@ pub struct DownloadManager {
/// @var array<string, string>
package_preferences: IndexMap<String, String>,
/// @var Filesystem
- filesystem: Filesystem,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
/// @var array<string, DownloaderInterface>
downloaders: IndexMap<String, Box<dyn DownloaderInterface>>,
}
@@ -43,9 +43,10 @@ impl DownloadManager {
pub fn new(
io: Box<dyn IOInterface>,
prefer_source: bool,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Self {
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))));
Self {
io,
prefer_source,
@@ -202,6 +203,7 @@ impl DownloadManager {
) -> Result<Box<dyn PromiseInterface>> {
let target_dir = self.normalize_target_dir(target_dir);
self.filesystem
+ .borrow_mut()
.ensure_directory_exists(&dirname(&target_dir));
let mut sources = self.get_available_sources(package, prev_package)?;
@@ -217,11 +219,11 @@ impl DownloadManager {
}
};
if retry_state {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" <warning>Now trying to download from {}</warning>",
source,
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -237,7 +239,7 @@ impl DownloadManager {
};
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
- let result = match downloader.download(package, &target_dir, prev_package) {
+ let result = match downloader.download3(package, &target_dir, prev_package) {
Ok(r) => r,
Err(e) => {
// PHP closure handleError: rethrow if not RuntimeException or if IrrecoverableDownloadException
@@ -250,13 +252,13 @@ impl DownloadManager {
return Err(e);
}
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" <warning>Failed to download {} from {}: {}</warning>",
package.get_pretty_name(),
source,
e,
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -316,7 +318,7 @@ impl DownloadManager {
) -> Result<Box<dyn PromiseInterface>> {
let target_dir = self.normalize_target_dir(target_dir);
if let Some(downloader) = self.get_downloader_for_package(package)? {
- return downloader.install(package, &target_dir);
+ return downloader.install2(package, &target_dir);
}
Ok(shirabe_external_packages::react::promise::resolve(None))
@@ -347,7 +349,7 @@ impl DownloadManager {
// if we have a downloader present before, but not after, the package became a metapackage and its files should be removed
if downloader.is_none() {
- return initial_downloader.unwrap().remove(initial, &target_dir);
+ return initial_downloader.unwrap().remove2(initial, &target_dir);
}
let initial_type = self.get_downloader_type(initial_downloader.unwrap());
@@ -362,8 +364,8 @@ impl DownloadManager {
if !self.io.is_interactive() {
return Err(e);
}
- self.io.write_error(
- PhpMixed::String(format!("<error> Update failed ({})</error>", e,)),
+ self.io.write_error3(
+ &format!("<error> Update failed ({})</error>", e,),
true,
io_interface::NORMAL,
);
@@ -379,7 +381,7 @@ impl DownloadManager {
// if downloader type changed, or update failed and user asks for reinstall,
// we wipe the dir and do a new install instead of updating it
- let promise = initial_downloader.unwrap().remove(initial, &target_dir)?;
+ let promise = initial_downloader.unwrap().remove2(initial, &target_dir)?;
let target_dir_owned = target_dir.clone();
// TODO(phase-b): capture self and target into the closure
@@ -402,7 +404,7 @@ impl DownloadManager {
) -> Result<Box<dyn PromiseInterface>> {
let target_dir = self.normalize_target_dir(target_dir);
if let Some(downloader) = self.get_downloader_for_package(package)? {
- return downloader.remove(package, &target_dir);
+ return downloader.remove2(package, &target_dir);
}
Ok(shirabe_external_packages::react::promise::resolve(None))
@@ -442,7 +444,7 @@ impl DownloadManager {
"{{^{}$}}i",
str_replace("\\*", ".*", &preg_quote(pattern, None)),
);
- if Preg::is_match(&pattern_regex, package.get_name()) {
+ if Preg::is_match(&pattern_regex, package.get_name()).unwrap_or(false) {
if "dist" == preference || (!package.is_dev() && "auto" == preference) {
return "dist".to_string();
}
diff --git a/crates/shirabe/src/downloader/downloader_interface.rs b/crates/shirabe/src/downloader/downloader_interface.rs
index 6662799..b72d80e 100644
--- a/crates/shirabe/src/downloader/downloader_interface.rs
+++ b/crates/shirabe/src/downloader/downloader_interface.rs
@@ -4,7 +4,7 @@ use shirabe_external_packages::react::promise::promise_interface::PromiseInterfa
use crate::package::package_interface::PackageInterface;
-pub trait DownloaderInterface {
+pub trait DownloaderInterface: std::fmt::Debug {
fn get_installation_source(&self) -> String;
fn download(
@@ -15,6 +15,16 @@ pub trait DownloaderInterface {
output: bool,
) -> anyhow::Result<Box<dyn PromiseInterface>>;
+ /// Convenience for the PHP default `$output = true` overload.
+ fn download3(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> anyhow::Result<Box<dyn PromiseInterface>> {
+ self.download(package, path, prev_package, true)
+ }
+
fn prepare(
&self,
r#type: &str,
@@ -30,6 +40,15 @@ pub trait DownloaderInterface {
output: bool,
) -> anyhow::Result<Box<dyn PromiseInterface>>;
+ /// Convenience for the PHP default `$output = true` overload.
+ fn install2(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ ) -> anyhow::Result<Box<dyn PromiseInterface>> {
+ self.install(package, path, true)
+ }
+
fn update(
&self,
initial: &dyn PackageInterface,
@@ -44,6 +63,15 @@ pub trait DownloaderInterface {
output: bool,
) -> anyhow::Result<Box<dyn PromiseInterface>>;
+ /// Convenience for the PHP default `$output = true` overload.
+ fn remove2(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ ) -> anyhow::Result<Box<dyn PromiseInterface>> {
+ self.remove(package, path, true)
+ }
+
fn cleanup(
&self,
r#type: &str,
diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs
index 85e43fc..c814baa 100644
--- a/crates/shirabe/src/downloader/file_downloader.rs
+++ b/crates/shirabe/src/downloader/file_downloader.rs
@@ -59,17 +59,17 @@ pub struct FileDownloader {
/// @var IOInterface
pub(crate) io: Box<dyn IOInterface>,
/// @var Config
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var HttpDownloader
- pub(crate) http_downloader: HttpDownloader,
+ pub(crate) http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
/// @var Filesystem
- pub(crate) filesystem: Filesystem,
+ pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
/// @var ?Cache
pub(crate) cache: Option<Cache>,
/// @var ?EventDispatcher
pub(crate) event_dispatcher: Option<EventDispatcher>,
/// @var ProcessExecutor
- pub(crate) process: ProcessExecutor,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var array<string, string> Map of package name to cache key
last_cache_writes: IndexMap<String, String>,
/// @var array<string, string[]> Map of package name to list of paths
@@ -80,15 +80,23 @@ impl FileDownloader {
/// Constructor.
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Option<Filesystem>,
- process: Option<ProcessExecutor>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(Some(Box::new(&*io)), None));
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(Some(process.clone())));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ Box::new(&*io),
+ ))))
+ });
+ let filesystem = filesystem.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(Some(
+ std::rc::Rc::clone(&process),
+ ))))
+ });
let mut this = Self {
io,
@@ -106,8 +114,16 @@ impl FileDownloader {
// PHP: writeError('Running cache garbage collection', true, io_interface::VERY_VERBOSE)
this.io.write_error("Running cache garbage collection");
this.cache.as_mut().unwrap().gc(
- this.config.get("cache-files-ttl").as_int().unwrap_or(0),
- this.config.get("cache-files-maxsize").as_int().unwrap_or(0),
+ this.config
+ .borrow_mut()
+ .get("cache-files-ttl")
+ .as_int()
+ .unwrap_or(0),
+ this.config
+ .borrow_mut()
+ .get("cache-files-maxsize")
+ .as_int()
+ .unwrap_or(0),
);
}
@@ -168,9 +184,11 @@ impl DownloaderInterface for FileDownloader {
debug_assert!(urls.len() > 0);
let file_name = self.get_file_name(package, path);
- self.filesystem.ensure_directory_exists(path)?;
+ self.filesystem.borrow_mut().ensure_directory_exists(path)?;
let dir_of_file = shirabe_php_shim::dirname(&file_name, 1);
- self.filesystem.ensure_directory_exists(&dir_of_file)?;
+ self.filesystem
+ .borrow_mut()
+ .ensure_directory_exists(&dir_of_file)?;
// TODO(plugin): inline closures rely on captured $accept/$reject/$urls/$retries. In Rust
// we'd need a struct holding shared state — left as a phase-b refactor.
@@ -204,11 +222,12 @@ impl DownloaderInterface for FileDownloader {
) -> Result<Box<dyn PromiseInterface>> {
let file_name = self.get_file_name(package, path);
if file_exists(&file_name) {
- self.filesystem.unlink(&file_name)?;
+ self.filesystem.borrow_mut().unlink(&file_name)?;
}
let vendor_dir = self
.config
+ .borrow_mut()
.get("vendor-dir")
.as_string()
.unwrap_or("")
@@ -232,16 +251,16 @@ impl DownloaderInterface for FileDownloader {
.cloned()
{
for path_to_clean in &paths {
- self.filesystem.remove(path_to_clean)?;
+ self.filesystem.borrow_mut().remove(path_to_clean)?;
}
}
for dir in &dirs_to_clean_up {
if is_dir(dir)
- && self.filesystem.is_dir_empty(dir)?
+ && self.filesystem.borrow_mut().is_dir_empty(dir)?
&& realpath(dir).as_deref() != Some(&Platform::get_cwd(false).unwrap_or_default())
{
- self.filesystem.remove_directory_php(dir)?;
+ self.filesystem.borrow_mut().remove_directory_php(dir)?;
}
}
@@ -262,6 +281,7 @@ impl DownloaderInterface for FileDownloader {
let vendor_dir = self
.config
+ .borrow_mut()
.get("vendor-dir")
.as_string()
.unwrap_or("")
@@ -271,16 +291,17 @@ impl DownloaderInterface for FileDownloader {
// the file to be installed. This is the case when installing with create-project in the current directory
// but in that case we ensure the directory is empty already in ProjectInstaller so no need to empty it here.
if false == {
- let normalized_vendor = self.filesystem.normalize_path(&vendor_dir);
+ let normalized_vendor = self.filesystem.borrow_mut().normalize_path(&vendor_dir);
let normalized_path = self
.filesystem
+ .borrow()
.normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR));
strpos(&normalized_vendor, &normalized_path).is_some()
} {
- self.filesystem.empty_directory(path, true)?;
+ self.filesystem.borrow_mut().empty_directory(path, true)?;
}
- self.filesystem.ensure_directory_exists(path)?;
- self.filesystem.rename(
+ self.filesystem.borrow_mut().ensure_directory_exists(path)?;
+ self.filesystem.borrow_mut().rename(
&self.get_file_name(package, path),
&format!(
"{}/{}",
@@ -338,7 +359,7 @@ impl DownloaderInterface for FileDownloader {
UninstallOperation::format(package, false)
));
}
- let _promise = self.filesystem.remove_directory_async(path)?;
+ let _promise = self.filesystem.borrow_mut().remove_directory_async(path)?;
// TODO(phase-b): chain `.then(|result| if !result { throw RuntimeException })`
let _ = path;
@@ -357,7 +378,7 @@ impl ChangeReportInterface for FileDownloader {
// TODO(phase-b): swap self.io to NullIO and restore — needs a take/swap helper
let mut null_io = NullIO::new();
- null_io.load_configuration(&self.config);
+ null_io.load_configuration(&mut *self.config.borrow_mut())?;
let mut e: Option<anyhow::Error> = None;
let mut output: String = String::new();
@@ -365,7 +386,8 @@ impl ChangeReportInterface for FileDownloader {
let result: Result<()> = (|| -> Result<()> {
if is_dir(&format!("{}_compare", target_dir)) {
self.filesystem
- .remove_directory(&format!("{}_compare", target_dir), false)?;
+ .borrow_mut()
+ .remove_directory(&format!("{}_compare", target_dir))?;
}
let promise =
@@ -377,7 +399,7 @@ impl ChangeReportInterface for FileDownloader {
PhpMixed::Null
})),
);
- self.http_downloader.wait()?;
+ self.http_downloader.borrow_mut().wait()?;
if e.is_some() {
return Err(e.unwrap());
}
@@ -389,7 +411,7 @@ impl ChangeReportInterface for FileDownloader {
PhpMixed::Null
})),
);
- self.process.wait()?;
+ self.process.borrow_mut().wait()?;
if e.is_some() {
return Err(e.unwrap());
}
@@ -400,6 +422,7 @@ impl ChangeReportInterface for FileDownloader {
comparer.do_compare();
output = comparer.get_changed_as_string(true, false);
self.filesystem
+ .borrow_mut()
.remove_directory(&format!("{}_compare", target_dir))?;
Ok(())
})();
@@ -491,7 +514,11 @@ impl FileDownloader {
rtrim(
&format!(
"{}/composer/tmp-{}.{}",
- self.config.get("vendor-dir").as_string().unwrap_or(""),
+ self.config
+ .borrow_mut()
+ .get("vendor-dir")
+ .as_string()
+ .unwrap_or(""),
hash(
"md5",
&format!("{}{}", package, spl_object_hash(&PhpMixed::Null))
@@ -525,10 +552,10 @@ impl FileDownloader {
let mut url = url.to_string();
if package.get_dist_reference().is_some() {
url = UrlUtil::update_dist_reference(
- &self.config,
+ &*self.config.borrow(),
&url,
package.get_dist_reference().unwrap(),
- )?;
+ );
}
Ok(url)
diff --git a/crates/shirabe/src/downloader/fossil_downloader.rs b/crates/shirabe/src/downloader/fossil_downloader.rs
index 5314e10..53b4315 100644
--- a/crates/shirabe/src/downloader/fossil_downloader.rs
+++ b/crates/shirabe/src/downloader/fossil_downloader.rs
@@ -29,9 +29,11 @@ impl FossilDownloader {
path: String,
url: String,
) -> Result<Box<dyn PromiseInterface>> {
- self.inner
- .config
- .prohibit_url_by_config(&url, &self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &url,
+ Some(&self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
let repo_file = format!("{}.fossil", path);
let real_path = shirabe_php_shim::realpath(&path);
@@ -85,9 +87,11 @@ impl FossilDownloader {
path: String,
url: String,
) -> Result<Box<dyn PromiseInterface>> {
- self.inner
- .config
- .prohibit_url_by_config(&url, &self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &url,
+ Some(&self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
self.inner.io.write_error(&format!(
" Updating to {}",
@@ -135,7 +139,7 @@ impl FossilDownloader {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil".to_string(), "changes".to_string()],
&mut output,
shirabe_php_shim::realpath(&path),
@@ -196,12 +200,18 @@ impl FossilDownloader {
cwd: Option<String>,
output: &mut String,
) -> Result<()> {
- if self.inner.process.execute(&command, output, cwd) != 0 {
+ if self
+ .inner
+ .process
+ .borrow_mut()
+ .execute(&command, output, cwd)
+ != 0
+ {
return Err(RuntimeException {
message: format!(
"Failed to execute {}\n\n{}",
command.join(" "),
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index fb41b0c..d451727 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -3,7 +3,7 @@
use crate::io::io_interface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
@@ -38,12 +38,17 @@ pub struct GitDownloader {
impl GitDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- fs: Option<Filesystem>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
+ fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Self {
let inner = VcsDownloaderBase::new(io, config, process, fs);
- let git_util = GitUtil::new(&*inner.io, &inner.config, &inner.process, &inner.filesystem);
+ let git_util = GitUtil::new(
+ inner.io.clone_box(),
+ std::rc::Rc::clone(&inner.config),
+ std::rc::Rc::clone(&inner.process),
+ std::rc::Rc::clone(&inner.filesystem),
+ );
Self {
inner,
has_stashed_changes: IndexMap::new(),
@@ -71,10 +76,11 @@ impl GitDownloader {
"{}/{}/",
self.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or(""),
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())),
+ Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(url.to_string()))?,
);
let git_version = GitUtil::get_version(&self.inner.process);
@@ -83,20 +89,20 @@ impl GitDownloader {
&& version_compare(git_version.as_deref().unwrap_or(""), "2.3.0-rc0", ">=")
&& Cache::is_usable(&cache_path)
{
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" - Syncing <info>{}</info> (<comment>{}</comment>) into cache",
package.get_name(),
package.get_full_pretty_version(),
- )),
+ ),
true,
io_interface::NORMAL,
);
- self.inner.io.write_error(
- PhpMixed::String(sprintf(
+ self.inner.io.write_error3(
+ &sprintf(
" Cloning to cache at %s",
&[PhpMixed::String(cache_path.clone())],
- )),
+ ),
true,
io_interface::DEBUG,
);
@@ -136,10 +142,11 @@ impl GitDownloader {
"{}/{}/",
self.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or(""),
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())),
+ Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(url.to_string()))?,
);
let r#ref = package.get_source_reference().unwrap_or("").to_string();
@@ -252,7 +259,8 @@ impl GitDownloader {
self.inner.io.write_error3(&msg, true, io_interface::NORMAL);
- self.git_util.run_commands(commands, url, &path, true);
+ self.git_util
+ .run_commands(commands, url, Some(&path), true, None)?;
let source_url = package.get_source_url();
if url != source_url.unwrap_or("") && source_url.is_some() {
@@ -299,10 +307,11 @@ impl GitDownloader {
"{}/{}/",
self.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or(""),
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(url.to_string())),
+ Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(url.to_string()))?,
);
let r#ref = target.get_source_reference().unwrap_or("").to_string();
@@ -335,7 +344,7 @@ impl GitDownloader {
self.inner.io.write_error3(&msg, true, io_interface::NORMAL);
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"rev-parse".to_string(),
@@ -369,7 +378,8 @@ impl GitDownloader {
],
];
- self.git_util.run_commands(commands, url, &path, false);
+ self.git_util
+ .run_commands(commands, url, Some(&path), false, None)?;
}
let command = vec![
@@ -380,7 +390,8 @@ impl GitDownloader {
"--".to_string(),
"%sanitizedUrl%".to_string(),
];
- self.git_util.run_commands(vec![command], url, &path, false);
+ self.git_util
+ .run_commands(vec![command], url, Some(&path), false, None)?;
if let Some(new_ref) =
self.update_to_commit(target, &path, &r#ref, target.get_pretty_version())?
@@ -395,18 +406,35 @@ impl GitDownloader {
let mut update_origin_url = false;
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "remote".to_string(), "-v".to_string()],
&mut output,
Some(path.clone()),
) == 0
{
- let origin_match = Preg::is_match_strict_groups(r"{^origin\s+(?P<url>\S+)}m", &output);
- let composer_match =
- Preg::is_match_strict_groups(r"{^composer\s+(?P<url>\S+)}m", &output);
- if let (Some(origin_match), Some(composer_match)) = (origin_match, composer_match) {
- let origin_url = origin_match.get("url").cloned().unwrap_or_default();
- let composer_url = composer_match.get("url").cloned().unwrap_or_default();
+ let mut origin_match: IndexMap<CaptureKey, String> = IndexMap::new();
+ let mut composer_match: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ r"{^origin\s+(?P<url>\S+)}m",
+ &output,
+ Some(&mut origin_match),
+ )
+ .unwrap_or(false)
+ && Preg::is_match3(
+ r"{^composer\s+(?P<url>\S+)}m",
+ &output,
+ Some(&mut composer_match),
+ )
+ .unwrap_or(false)
+ {
+ let origin_url = origin_match
+ .get(&CaptureKey::ByName("url".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let composer_url = composer_match
+ .get(&CaptureKey::ByName("url".to_string()))
+ .cloned()
+ .unwrap_or_default();
if origin_url == composer_url
&& Some(composer_url.as_str()) != target.get_source_url()
{
@@ -434,11 +462,11 @@ impl GitDownloader {
"--untracked-files=no".to_string(),
];
let mut output = String::new();
- if self
- .inner
- .process
- .execute(&command, &mut output, Some(path.to_string()))
- != 0
+ if self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(path.to_string()),
+ ) != 0
{
// TODO(phase-b): cannot throw from &self / non-Result fn; bubble error via Result later
panic!(
@@ -446,7 +474,7 @@ impl GitDownloader {
format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
)
);
}
@@ -481,7 +509,8 @@ impl GitDownloader {
if self
.inner
.process
- .execute(&command, &mut output, Some(path.clone()))
+ .borrow_mut()
+ .execute_args(&command, &mut output, Some(path.clone()))
!= 0
{
// TODO(phase-b): bubble error via Result later
@@ -490,26 +519,39 @@ impl GitDownloader {
format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
)
);
}
let mut refs = trim(&output, None);
- let head_ref = match Preg::is_match_strict_groups(r"{^([a-f0-9]+) HEAD$}mi", &refs) {
- Some(m) => m.get(1).cloned().unwrap_or_default(),
+ let mut head_match: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(r"{^([a-f0-9]+) HEAD$}mi", &refs, Some(&mut head_match))
+ .unwrap_or(false)
+ {
// could not match the HEAD for some reason
- None => return None,
- };
+ return None;
+ }
+ let head_ref = head_match
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
- let candidate_branches: Vec<String> = match Preg::is_match_all_strict_groups(
+ let mut branches_match: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
+ if !Preg::is_match_all_strict_groups3(
&format!("{{^{} refs/heads/(.+)$}}mi", preg_quote(&head_ref, None)),
&refs,
- ) {
- Some(m) => m.get(1).cloned().unwrap_or_default(),
+ Some(&mut branches_match),
+ )
+ .unwrap_or(false)
+ {
// not on a branch, we are either on a not-modified tag or some sort of detached head, so skip this
- None => return None,
- };
+ return None;
+ }
+ let candidate_branches: Vec<String> = branches_match
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
// use the first match as branch name for now
let mut branch = candidate_branches[0].clone();
@@ -522,14 +564,19 @@ impl GitDownloader {
// try to find matching branch names in remote repos
for candidate in &candidate_branches {
- if let Some(m) = Preg::is_match_all_strict_groups(
+ let mut m: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
+ if Preg::is_match_all_strict_groups3(
&format!(
"{{^[a-f0-9]+ refs/remotes/((?:[^/]+)/{})$}}mi",
preg_quote(candidate, None)
),
&refs,
- ) {
- let matches: Vec<String> = m.get(1).cloned().unwrap_or_default();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let matches: Vec<String> =
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
for match_ in matches {
branch = candidate.clone();
remote_branches.push(match_);
@@ -562,11 +609,11 @@ impl GitDownloader {
"--".to_string(),
];
let mut output = String::new();
- if self
- .inner
- .process
- .execute(&command, &mut output, Some(path.clone()))
- != 0
+ if self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(path.clone()),
+ ) != 0
{
// TODO(phase-b): bubble error via Result later
panic!(
@@ -574,7 +621,7 @@ impl GitDownloader {
format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
)
);
}
@@ -593,7 +640,7 @@ impl GitDownloader {
// remotes and then try again as outdated remotes can sometimes cause false-positives
if unpushed_changes.is_some() && i == 0 {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "fetch".to_string(), "--all".to_string()],
&mut output,
Some(path.clone()),
@@ -607,11 +654,11 @@ impl GitDownloader {
"-d".to_string(),
];
let mut output = String::new();
- if self
- .inner
- .process
- .execute(&command, &mut output, Some(path.clone()))
- != 0
+ if self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(path.clone()),
+ ) != 0
{
// TODO(phase-b): bubble error via Result later
panic!(
@@ -619,7 +666,7 @@ impl GitDownloader {
format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
)
);
}
@@ -647,7 +694,13 @@ impl GitDownloader {
let unpushed = self.get_unpushed_changes(package, &path);
if let Some(unpushed) = unpushed.as_deref() {
if self.inner.io.is_interactive()
- || self.inner.config.get("discard-changes").as_bool() != Some(true)
+ || self
+ .inner
+ .config
+ .borrow_mut()
+ .get("discard-changes")
+ .as_bool()
+ != Some(true)
{
return Err(RuntimeException {
message: format!(
@@ -666,7 +719,7 @@ impl GitDownloader {
};
if !self.inner.io.is_interactive() {
- let discard_changes = self.inner.config.get("discard-changes");
+ let discard_changes = self.inner.config.borrow_mut().get("discard-changes");
if discard_changes.as_bool() == Some(true) {
return self.discard_changes(&path);
}
@@ -685,16 +738,16 @@ impl GitDownloader {
|elem: &String| format!(" {}", elem),
&Preg::split(r"{\s*\r?\n\s*}", &changes),
);
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" <error>{} has modified files:</error>",
package.get_pretty_name()
- )),
+ ),
true,
io_interface::NORMAL,
);
let slice_end = 10_usize.min(changes.len());
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(
changes[..slice_end]
.iter()
@@ -705,11 +758,11 @@ impl GitDownloader {
io_interface::NORMAL,
);
if (changes.len() as i64) > 10 {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" <info>{} more files modified, choose \"v\" to view the full list</info>",
changes.len() as i64 - 10
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -751,7 +804,7 @@ impl GitDownloader {
.into());
}
Some("v") => {
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(
changes
.iter()
@@ -773,7 +826,7 @@ impl GitDownloader {
if do_help {
// help:
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(format!(
" y - discard changes and apply the {}",
@@ -792,20 +845,15 @@ impl GitDownloader {
io_interface::NORMAL,
);
if update {
- self.inner.io.write_error(
- PhpMixed::String(
- " s - stash changes and try to reapply them after the update"
- .to_string(),
- ),
+ self.inner.io.write_error3(
+ " s - stash changes and try to reapply them after the update",
true,
io_interface::NORMAL,
);
}
- self.inner.io.write_error(
- PhpMixed::String(" ? - print help".to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.inner
+ .io
+ .write_error3(" ? - print help", true, io_interface::NORMAL);
}
}
@@ -821,13 +869,13 @@ impl GitDownloader {
.unwrap_or(false)
{
self.has_stashed_changes.shift_remove(&path);
- self.inner.io.write_error(
- PhpMixed::String(" <info>Re-applying stashed changes</info>".to_string()),
+ self.inner.io.write_error3(
+ " <info>Re-applying stashed changes</info>",
true,
io_interface::NORMAL,
);
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "stash".to_string(), "pop".to_string()],
&mut output,
Some(path.clone()),
@@ -836,7 +884,7 @@ impl GitDownloader {
return Err(RuntimeException {
message: format!(
"Failed to apply stashed changes:\n\n{}",
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
@@ -877,11 +925,7 @@ impl GitDownloader {
// If the non-existent branch is actually the name of a file, the file
// is checked out.
- let mut branch = Preg::replace(
- r"{(?:^dev-|(?:\.x)?-dev$)}i",
- "",
- pretty_version.to_string(),
- );
+ let mut branch = Preg::replace(r"{(?:^dev-|(?:\.x)?-dev$)}i", "", &pretty_version);
// Closure equivalent: $execute = function(array $command) use (&$output, $path) { ... };
// Inlined below at each call site.
@@ -889,7 +933,7 @@ impl GitDownloader {
let mut branches: Option<String> = None;
{
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "branch".to_string(), "-r".to_string()],
&mut output,
Some(path.to_string()),
@@ -926,17 +970,18 @@ impl GitDownloader {
];
let mut output = String::new();
- let ok1 = self
- .inner
- .process
- .execute(&command1, &mut output, Some(path.to_string()))
- == 0;
+ let ok1 = self.inner.process.borrow_mut().execute_args(
+ &command1,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0;
let ok2 = if ok1 {
let mut output = String::new();
- self.inner
- .process
- .execute(&command2, &mut output, Some(path.to_string()))
- == 0
+ self.inner.process.borrow_mut().execute_args(
+ &command2,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0
} else {
false
};
@@ -986,26 +1031,28 @@ impl GitDownloader {
];
let mut output = String::new();
- let ok_command =
- self.inner
- .process
- .execute(&command, &mut output, Some(path.to_string()))
- == 0;
+ let ok_command = self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0;
let ok_fallback = if !ok_command {
let mut output = String::new();
- self.inner
- .process
- .execute(&fallback_command, &mut output, Some(path.to_string()))
- == 0
+ self.inner.process.borrow_mut().execute_args(
+ &fallback_command,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0
} else {
false
};
let ok_reset = if ok_command || ok_fallback {
let mut output = String::new();
- self.inner
- .process
- .execute(&reset_command, &mut output, Some(path.to_string()))
- == 0
+ self.inner.process.borrow_mut().execute_args(
+ &reset_command,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0
} else {
false
};
@@ -1026,17 +1073,18 @@ impl GitDownloader {
];
{
let mut output = String::new();
- let ok1 = self
- .inner
- .process
- .execute(&command1, &mut output, Some(path.to_string()))
- == 0;
+ let ok1 = self.inner.process.borrow_mut().execute_args(
+ &command1,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0;
let ok2 = if ok1 {
let mut output = String::new();
- self.inner
- .process
- .execute(&command2, &mut output, Some(path.to_string()))
- == 0
+ self.inner.process.borrow_mut().execute_args(
+ &command2,
+ &mut output,
+ Some(path.to_string()),
+ ) == 0
} else {
false
};
@@ -1048,12 +1096,12 @@ impl GitDownloader {
let mut exception_extra = String::new();
// reference was not found (prints "fatal: reference is not a tree: $ref")
- if strpos(self.inner.process.get_error_output(), reference).is_some() {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ if strpos(self.inner.process.borrow().get_error_output(), reference).is_some() {
+ self.inner.io.write_error3(
+ &format!(
" <warning>{} is gone (history was rewritten?)</warning>",
reference
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1074,7 +1122,7 @@ impl GitDownloader {
message: Url::sanitize(format!(
"Failed to execute {}\n\n{}{}",
command,
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
exception_extra,
)),
code: 0,
@@ -1084,7 +1132,7 @@ impl GitDownloader {
pub(crate) fn update_origin_url(&mut self, path: &str, url: &str) {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"remote".to_string(),
@@ -1101,17 +1149,30 @@ impl GitDownloader {
pub(crate) fn set_push_url(&mut self, path: &str, url: &str) {
// set push url for github projects
- if let Some(match_) = Preg::is_match_strict_groups(
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
&format!(
"{{^(?:https?|git)://{}/([^/]+)/([^/]+?)(?:\\.git)?$}}",
- GitUtil::get_github_domains_regex(&self.inner.config)
+ GitUtil::get_github_domains_regex(&*self.inner.config.borrow())
),
url,
- ) {
- let protocols = self.inner.config.get("github-protocols");
- let m1 = match_.get(1).cloned().unwrap_or_default();
- let m2 = match_.get(2).cloned().unwrap_or_default();
- let m3 = match_.get(3).cloned().unwrap_or_default();
+ Some(&mut match_),
+ )
+ .unwrap_or(false)
+ {
+ let protocols = self.inner.config.borrow_mut().get("github-protocols");
+ let m1 = match_
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ let m2 = match_
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default();
+ let m3 = match_
+ .get(&CaptureKey::ByIndex(3))
+ .cloned()
+ .unwrap_or_default();
let mut push_url = format!("git@{}:{}/{}.git", m1, m2, m3);
if !in_array(PhpMixed::String("ssh".to_string()), &protocols, true) {
push_url = format!("https://{}/{}/{}.git", m1, m2, m3);
@@ -1126,9 +1187,11 @@ impl GitDownloader {
push_url,
];
let mut ignored_output = String::new();
- self.inner
- .process
- .execute(&cmd, &mut ignored_output, Some(path.to_string()));
+ self.inner.process.borrow_mut().execute_args(
+ &cmd,
+ &mut ignored_output,
+ Some(path.to_string()),
+ );
}
}
@@ -1150,14 +1213,15 @@ impl GitDownloader {
if self
.inner
.process
- .execute(&command, &mut output, Some(path.clone()))
+ .borrow_mut()
+ .execute_args(&command, &mut output, Some(path.clone()))
!= 0
{
return Err(RuntimeException {
message: format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
),
code: 0,
}
@@ -1172,7 +1236,7 @@ impl GitDownloader {
pub(crate) fn discard_changes(&mut self, path: &str) -> Result<Box<dyn PromiseInterface>> {
let path = self.normalize_path(path);
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "clean".to_string(), "-df".to_string()],
&mut output,
Some(path.clone()),
@@ -1185,7 +1249,7 @@ impl GitDownloader {
.into());
}
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "reset".to_string(), "--hard".to_string()],
&mut output,
Some(path.clone()),
@@ -1208,7 +1272,7 @@ impl GitDownloader {
pub(crate) fn stash_changes(&mut self, path: &str) -> Result<Box<dyn PromiseInterface>> {
let path = self.normalize_path(path);
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"stash".to_string(),
@@ -1234,7 +1298,7 @@ impl GitDownloader {
pub(crate) fn view_diff(&mut self, path: &str) {
let path = self.normalize_path(path);
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&vec!["git".to_string(), "diff".to_string(), "HEAD".to_string()],
&mut output,
Some(path.clone()),
diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs
index 2a485de..4ee9d33 100644
--- a/crates/shirabe/src/downloader/gzip_downloader.rs
+++ b/crates/shirabe/src/downloader/gzip_downloader.rs
@@ -29,12 +29,12 @@ pub struct GzipDownloader {
impl GzipDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Filesystem,
- process: ProcessExecutor,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
Self {
inner: FileDownloader::new(
@@ -80,7 +80,7 @@ impl GzipDownloader {
];
let mut process_output = PhpMixed::Null;
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute(
PhpMixed::List(
command
.iter()
@@ -102,7 +102,7 @@ impl GzipDownloader {
let process_error = format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
);
return Err(anyhow::anyhow!(RuntimeException {
message: process_error,
diff --git a/crates/shirabe/src/downloader/hg_downloader.rs b/crates/shirabe/src/downloader/hg_downloader.rs
index 1b4204f..161eb7e 100644
--- a/crates/shirabe/src/downloader/hg_downloader.rs
+++ b/crates/shirabe/src/downloader/hg_downloader.rs
@@ -37,7 +37,11 @@ impl HgDownloader {
path: String,
url: String,
) -> Result<Box<dyn PromiseInterface>> {
- let hg_utils = HgUtils::new(&self.inner.io, &self.inner.config, &self.inner.process);
+ let hg_utils = HgUtils::new(
+ &*self.inner.io,
+ &*self.inner.config.borrow(),
+ &self.inner.process,
+ );
let path_clone = path.clone();
let clone_command = move |url: String| -> Vec<String> {
@@ -58,7 +62,7 @@ impl HgDownloader {
package.get_source_reference().unwrap_or_default(),
];
let mut ignored_output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&command,
&mut ignored_output,
shirabe_php_shim::realpath(&path),
@@ -68,7 +72,7 @@ impl HgDownloader {
message: format!(
"Failed to execute {}\n\n{}",
command.join(" "),
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
@@ -85,7 +89,11 @@ impl HgDownloader {
path: String,
url: String,
) -> Result<Box<dyn PromiseInterface>> {
- let hg_utils = HgUtils::new(&self.inner.io, &self.inner.config, &self.inner.process);
+ let hg_utils = HgUtils::new(
+ &*self.inner.io,
+ &*self.inner.config.borrow(),
+ &self.inner.process,
+ );
let ref_ = target.get_source_reference().unwrap_or_default();
self.inner.io.write_error(&format!(
@@ -132,7 +140,7 @@ impl HgDownloader {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg".to_string(), "st".to_string()],
&mut output,
shirabe_php_shim::realpath(&path),
@@ -163,17 +171,17 @@ impl HgDownloader {
];
let mut output = String::new();
- if self
- .inner
- .process
- .execute(&command, &mut output, shirabe_php_shim::realpath(&path))
- != 0
+ if self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ shirabe_php_shim::realpath(&path),
+ ) != 0
{
return Err(RuntimeException {
message: format!(
"Failed to execute {}\n\n{}",
command.join(" "),
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs
index a525df0..26795f3 100644
--- a/crates/shirabe/src/downloader/path_downloader.rs
+++ b/crates/shirabe/src/downloader/path_downloader.rs
@@ -117,12 +117,12 @@ impl PathDownloader {
if realpath(&path).as_deref() == Some(&real_url) {
if output {
let appendix = self.get_install_operation_appendix(package, &path)?;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" - {}{}",
InstallOperation::format(package, false),
appendix
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -141,14 +141,11 @@ impl PathDownloader {
self.compute_allowed_strategies(&transport_options)?;
let symfony_filesystem = SymfonyFilesystem::new(None);
- self.inner.filesystem.remove_directory(&path);
+ self.inner.filesystem.borrow_mut().remove_directory(&path);
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!(
- " - {}: ",
- InstallOperation::format(package, false)
- )),
+ self.inner.io.write_error3(
+ &format!(" - {}: ", InstallOperation::format(package, false)),
false,
io_interface::NORMAL,
);
@@ -160,18 +157,22 @@ impl PathDownloader {
if Platform::is_windows() {
// Implement symlinks as NTFS junctions on Windows
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!("Junctioning from {}", url)),
+ self.inner.io.write_error3(
+ &format!("Junctioning from {}", url),
false,
io_interface::NORMAL,
);
}
- Ok(self.inner.filesystem.junction(&real_url, &path))
+ Ok(self
+ .inner
+ .filesystem
+ .borrow_mut()
+ .junction(&real_url, &path))
} else {
let path = path.trim_end_matches('/').to_string();
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!("Symlinking from {}", url)),
+ self.inner.io.write_error3(
+ &format!("Symlinking from {}", url),
false,
io_interface::NORMAL,
);
@@ -181,17 +182,18 @@ impl PathDownloader {
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
- let absolute_path = if !self.inner.filesystem.is_absolute_path(&path) {
- format!(
- "{}{}{}",
- Platform::get_cwd(false),
- DIRECTORY_SEPARATOR,
- path
- )
- } else {
- path.clone()
- };
- let shortest_path = self.inner.filesystem.find_shortest_path(
+ let absolute_path =
+ if !self.inner.filesystem.borrow_mut().is_absolute_path(&path) {
+ format!(
+ "{}{}{}",
+ Platform::get_cwd(false),
+ DIRECTORY_SEPARATOR,
+ path
+ )
+ } else {
+ path.clone()
+ };
+ let shortest_path = self.inner.filesystem.borrow_mut().find_shortest_path(
&absolute_path,
&real_url,
false,
@@ -209,16 +211,9 @@ impl PathDownloader {
Err(_e) => {
if allowed_strategies.contains(&Self::STRATEGY_MIRROR) {
if output {
- self.inner.io.write_error(
- PhpMixed::String("".to_string()),
- true,
- io_interface::NORMAL,
- );
- self.inner.io.write_error(
- PhpMixed::String(
- " <error>Symlink failed, fallback to use mirroring!</error>"
- .to_string(),
- ),
+ self.inner.io.write_error3("", true, io_interface::NORMAL);
+ self.inner.io.write_error3(
+ " <error>Symlink failed, fallback to use mirroring!</error>",
true,
io_interface::NORMAL,
);
@@ -241,15 +236,15 @@ impl PathDownloader {
// Fallback if symlink failed or if symlink is not allowed for the package
if Self::STRATEGY_MIRROR == current_strategy {
- let real_url = self.inner.filesystem.normalize_path(&real_url);
+ let real_url = self.inner.filesystem.borrow_mut().normalize_path(&real_url);
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"{}Mirroring from {}",
if is_fallback { " " } else { "" },
url
- )),
+ ),
false,
io_interface::NORMAL,
);
@@ -280,24 +275,24 @@ impl PathDownloader {
// process inadvertently locks the file the removal will fail, but it would fall back to recursive
// delete which is disastrous within a junction. So in that case we have no other real choice but
// to fail hard.
- if Platform::is_windows() && self.inner.filesystem.is_junction(&path) {
+ if Platform::is_windows() && self.inner.filesystem.borrow_mut().is_junction(&path) {
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" - {}, source is still present in {}",
UninstallOperation::format(package, false),
path
- )),
+ ),
true,
io_interface::NORMAL,
);
}
- if !self.inner.filesystem.remove_junction(&path) {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ if !self.inner.filesystem.borrow_mut().remove_junction(&path)? {
+ self.inner.io.write_error3(
+ &format!(
" <warning>Could not remove junction at {} - is another process locking it?</warning>",
path
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -339,12 +334,12 @@ impl PathDownloader {
};
if fs.normalize_path(&abs_path) == fs.normalize_path(&abs_dist_url) {
if output {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" - {}, source is still present in {}",
UninstallOperation::format(package, false),
path
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -360,10 +355,10 @@ impl PathDownloader {
let path = Filesystem::trim_trailing_slash(path);
let parser = VersionParser::new();
let guesser = VersionGuesser::new(
- &self.inner.config,
- &self.inner.process,
- &parser,
- Some(&*self.inner.io),
+ std::rc::Rc::clone(&self.inner.config),
+ std::rc::Rc::clone(&self.inner.process),
+ parser.clone(),
+ Some(self.inner.io.clone_box()),
);
let dumper = ArrayDumper::new();
diff --git a/crates/shirabe/src/downloader/perforce_downloader.rs b/crates/shirabe/src/downloader/perforce_downloader.rs
index b10e27f..fe5e9e5 100644
--- a/crates/shirabe/src/downloader/perforce_downloader.rs
+++ b/crates/shirabe/src/downloader/perforce_downloader.rs
@@ -74,7 +74,7 @@ impl PerforceDownloader {
let repository = package.get_repository();
let repo_config: Option<IndexMap<String, PhpMixed>> = if let Some(repo) = repository {
- if let Some(vcs_repo) = (repo.as_any() as &dyn Any).downcast_ref::<VcsRepository>() {
+ if let Some(vcs_repo) = repo.as_any().downcast_ref::<VcsRepository>() {
Some(self.get_repo_config(vcs_repo))
} else {
None
diff --git a/crates/shirabe/src/downloader/phar_downloader.rs b/crates/shirabe/src/downloader/phar_downloader.rs
index 649841d..19777fb 100644
--- a/crates/shirabe/src/downloader/phar_downloader.rs
+++ b/crates/shirabe/src/downloader/phar_downloader.rs
@@ -25,12 +25,12 @@ pub struct PharDownloader {
impl PharDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Filesystem,
- process: ProcessExecutor,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
Self {
inner: FileDownloader::new(
diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs
index 51feadb..afc2f12 100644
--- a/crates/shirabe/src/downloader/rar_downloader.rs
+++ b/crates/shirabe/src/downloader/rar_downloader.rs
@@ -28,12 +28,12 @@ pub struct RarDownloader {
impl RarDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Filesystem,
- process: ProcessExecutor,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
Self {
inner: FileDownloader::new(
@@ -67,7 +67,7 @@ impl RarDownloader {
];
let mut process_output = PhpMixed::Null;
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute(
PhpMixed::List(
command
.iter()
@@ -84,7 +84,7 @@ impl RarDownloader {
process_error = Some(format!(
"Failed to execute {}\n\n{}",
implode(" ", &command),
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
));
}
diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs
index eb4de65..c228379 100644
--- a/crates/shirabe/src/downloader/svn_downloader.rs
+++ b/crates/shirabe/src/downloader/svn_downloader.rs
@@ -1,7 +1,8 @@
//! ref: composer/src/Composer/Downloader/SvnDownloader.php
use crate::io::io_interface;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{PhpMixed, RuntimeException, is_dir, version_compare};
@@ -29,9 +30,9 @@ impl SvnDownloader {
SvnUtil::clean_env();
let util = SvnUtil::new(
url,
- &*self.inner.io,
- &self.inner.config,
- &self.inner.process,
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
);
if util.binary_version().is_none() {
return Err(RuntimeException {
@@ -68,8 +69,8 @@ impl SvnDownloader {
}
}
- self.inner.io.write_error(
- PhpMixed::String(format!(" Checking out {}", package.get_source_reference())),
+ self.inner.io.write_error3(
+ &format!(" Checking out {}", package.get_source_reference()),
true,
io_interface::NORMAL,
);
@@ -108,17 +109,17 @@ impl SvnDownloader {
let util = SvnUtil::new(
url,
- &*self.inner.io,
- &self.inner.config,
- &self.inner.process,
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
);
let mut flags: Vec<String> = vec![];
if version_compare(&util.binary_version().unwrap_or_default(), "1.7.0", ">=") {
flags.push("--ignore-ancestry".to_string());
}
- self.inner.io.write_error(
- PhpMixed::String(format!(" Checking out {}", r#ref)),
+ self.inner.io.write_error3(
+ &format!(" Checking out {}", r#ref),
true,
io_interface::NORMAL,
);
@@ -142,7 +143,7 @@ impl SvnDownloader {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["svn", "status", "--ignore-externals"]
.map(|s| s.to_string())
.to_vec(),
@@ -168,9 +169,9 @@ impl SvnDownloader {
) -> anyhow::Result<String> {
let mut util = SvnUtil::new(
base_url,
- &*self.inner.io,
- &self.inner.config,
- &self.inner.process,
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
);
util.set_cache_credentials(self.cache_credentials);
util.execute(command, url, cwd, path, self.inner.io.is_verbose())
@@ -195,7 +196,14 @@ impl SvnDownloader {
}
if !self.inner.io.is_interactive() {
- if self.inner.config.get("discard-changes").as_bool() == Some(true) {
+ if self
+ .inner
+ .config
+ .borrow_mut()
+ .get("discard-changes")
+ .as_bool()
+ == Some(true)
+ {
return self.discard_changes(path);
}
@@ -208,17 +216,17 @@ impl SvnDownloader {
.map(|elem| format!(" {}", elem))
.collect();
let count_changes = changes.len() as i64;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" <error>{} has modified file{}:</error>",
package.get_pretty_name(),
if count_changes == 1 { "" } else { "s" }
- )),
+ ),
true,
io_interface::NORMAL,
);
let slice_end = 10_usize.min(changes.len());
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(
changes[..slice_end]
.iter()
@@ -230,12 +238,12 @@ impl SvnDownloader {
);
if count_changes > 10 {
let remaining_changes = count_changes - 10;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
" <info>{} more file{} modified, choose \"v\" to view the full list</info>",
remaining_changes,
if remaining_changes == 1 { "" } else { "s" }
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -263,7 +271,7 @@ impl SvnDownloader {
.into());
}
Some("v") => {
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(
changes
.iter()
@@ -275,7 +283,7 @@ impl SvnDownloader {
);
}
_ => {
- self.inner.io.write_error(
+ self.inner.io.write_error3(
PhpMixed::List(vec![
Box::new(PhpMixed::String(format!(
" y - discard changes and apply the {}",
@@ -317,17 +325,17 @@ impl SvnDownloader {
path.to_string(),
];
let mut output = String::new();
- if self
- .inner
- .process
- .execute(&command, &mut output, Some(path.to_string()))
- != 0
+ if self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(path.to_string()),
+ ) != 0
{
return Err(RuntimeException {
message: format!(
"Failed to execute {}\n\n{}",
command.join(" "),
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
@@ -335,8 +343,14 @@ impl SvnDownloader {
}
let url_pattern = "#<url>(.*)</url>#";
- let base_url = if let Some(matches) = Preg::match_strict_groups(url_pattern, &output) {
- matches.get("1").cloned().unwrap_or_default()
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ let base_url = if Preg::match_strict_groups3(url_pattern, &output, Some(&mut matches))
+ .unwrap_or(false)
+ {
+ matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default()
} else {
return Err(RuntimeException {
message: format!("Unable to determine svn url for path {}", path),
@@ -346,8 +360,10 @@ impl SvnDownloader {
};
// strip paths from references and only keep the actual revision
- let from_revision = Preg::replace(r"{.*@(\d+)$}", "$1", from_reference.to_string());
- let to_revision = Preg::replace(r"{.*@(\d+)$}", "$1", to_reference.to_string());
+ let from_revision =
+ Preg::replace(r"{.*@(\d+)$}", "$1", &from_reference).unwrap_or_default();
+ let to_revision =
+ Preg::replace(r"{.*@(\d+)$}", "$1", &to_reference).unwrap_or_default();
let command = vec![
"svn".to_string(),
@@ -359,9 +375,9 @@ impl SvnDownloader {
let mut util = SvnUtil::new(
&base_url,
- &*self.inner.io,
- &self.inner.config,
- &self.inner.process,
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
);
util.set_cache_credentials(self.cache_credentials);
util.execute_local(command.clone(), path, None, self.inner.io.is_verbose())
@@ -382,7 +398,7 @@ impl SvnDownloader {
pub(crate) fn discard_changes(&self, path: &str) -> anyhow::Result<Box<dyn PromiseInterface>> {
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["svn", "revert", "-R", "."].map(|s| s.to_string()).to_vec(),
&mut output,
Some(path.to_string()),
@@ -391,7 +407,7 @@ impl SvnDownloader {
return Err(RuntimeException {
message: format!(
"Could not reset changes\n\n:{}",
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
diff --git a/crates/shirabe/src/downloader/tar_downloader.rs b/crates/shirabe/src/downloader/tar_downloader.rs
index aaa7153..8fcf339 100644
--- a/crates/shirabe/src/downloader/tar_downloader.rs
+++ b/crates/shirabe/src/downloader/tar_downloader.rs
@@ -25,12 +25,12 @@ pub struct TarDownloader {
impl TarDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Filesystem,
- process: ProcessExecutor,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
Self {
inner: FileDownloader::new(
diff --git a/crates/shirabe/src/downloader/transport_exception.rs b/crates/shirabe/src/downloader/transport_exception.rs
index 382da01..9d5b352 100644
--- a/crates/shirabe/src/downloader/transport_exception.rs
+++ b/crates/shirabe/src/downloader/transport_exception.rs
@@ -24,6 +24,12 @@ impl TransportException {
}
}
+ /// PHP exposes ($message, $code = 0) — alias of `new` used at call sites where the
+ /// status/exception code is provided up-front.
+ pub fn new_with_code(message: String, code: i64) -> Self {
+ Self::new(message, code)
+ }
+
pub fn get_code(&self) -> i64 {
self.code
}
diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs
index 346603a..39518e3 100644
--- a/crates/shirabe/src/downloader/vcs_downloader.rs
+++ b/crates/shirabe/src/downloader/vcs_downloader.rs
@@ -27,21 +27,24 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct VcsDownloaderBase {
pub io: Box<dyn IOInterface>,
- pub config: Config,
- pub process: ProcessExecutor,
- pub filesystem: Filesystem,
+ pub config: std::rc::Rc<std::cell::RefCell<Config>>,
+ pub process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ pub filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
pub has_cleaned_changes: IndexMap<String, bool>,
}
impl VcsDownloaderBase {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- fs: Option<Filesystem>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
+ fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(None, None));
- let filesystem = fs.unwrap_or_else(|| Filesystem::new(None));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None)))
+ });
+ let filesystem =
+ fs.unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))));
Self {
io,
config,
@@ -57,12 +60,12 @@ pub trait VcsDownloader:
{
fn io(&self) -> &dyn IOInterface;
fn io_mut(&mut self) -> &mut dyn IOInterface;
- fn config(&self) -> &Config;
- fn config_mut(&mut self) -> &mut Config;
- fn process(&self) -> &ProcessExecutor;
- fn process_mut(&mut self) -> &mut ProcessExecutor;
- fn filesystem(&self) -> &Filesystem;
- fn filesystem_mut(&mut self) -> &mut Filesystem;
+ fn config(&self) -> &std::rc::Rc<std::cell::RefCell<Config>>;
+ fn config_mut(&mut self) -> &mut std::rc::Rc<std::cell::RefCell<Config>>;
+ fn process(&self) -> &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>;
+ fn process_mut(&mut self) -> &mut std::rc::Rc<std::cell::RefCell<ProcessExecutor>>;
+ fn filesystem(&self) -> &std::rc::Rc<std::cell::RefCell<Filesystem>>;
+ fn filesystem_mut(&mut self) -> &mut std::rc::Rc<std::cell::RefCell<Filesystem>>;
fn has_cleaned_changes(&self) -> &IndexMap<String, bool>;
fn has_cleaned_changes_mut(&mut self) -> &mut IndexMap<String, bool>;
@@ -136,8 +139,8 @@ pub trait VcsDownloader:
return Err(e);
}
if self.io().is_debug() {
- self.io_mut().write_error(
- PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
+ self.io_mut().write_error3(
+ &format!("Failed: [{}] {}", get_class(&e), e,),
true,
io_interface::NORMAL,
);
@@ -147,8 +150,8 @@ pub trait VcsDownloader:
.collect(),
)) > 0
{
- self.io_mut().write_error(
- PhpMixed::String(" Failed, trying the next URL".to_string()),
+ self.io_mut().write_error3(
+ " Failed, trying the next URL",
true,
io_interface::NORMAL,
);
@@ -180,7 +183,7 @@ pub trait VcsDownloader:
self.has_cleaned_changes_mut()
.insert(prev_package.unwrap().get_unique_name(), true);
} else if r#type == "install" {
- self.filesystem_mut().empty_directory(path);
+ self.filesystem_mut().borrow_mut().empty_directory(path);
} else if r#type == "uninstall" {
self.clean_changes(package, path, false)?;
}
@@ -227,11 +230,8 @@ pub trait VcsDownloader:
.into());
}
- self.io_mut().write_error(
- PhpMixed::String(format!(
- " - {}: ",
- InstallOperation::format(package, false)
- )),
+ self.io_mut().write_error3(
+ &format!(" - {}: ", InstallOperation::format(package, false)),
false,
io_interface::NORMAL,
);
@@ -250,8 +250,8 @@ pub trait VcsDownloader:
return Err(e);
}
if self.io().is_debug() {
- self.io_mut().write_error(
- PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
+ self.io_mut().write_error3(
+ &format!("Failed: [{}] {}", get_class(&e), e,),
true,
io_interface::NORMAL,
);
@@ -261,8 +261,8 @@ pub trait VcsDownloader:
.collect(),
)) > 0
{
- self.io_mut().write_error(
- PhpMixed::String(" Failed, trying the next URL".to_string()),
+ self.io_mut().write_error3(
+ " Failed, trying the next URL",
true,
io_interface::NORMAL,
);
@@ -299,11 +299,8 @@ pub trait VcsDownloader:
.into());
}
- self.io_mut().write_error(
- PhpMixed::String(format!(
- " - {}: ",
- UpdateOperation::format(initial, target, false),
- )),
+ self.io_mut().write_error3(
+ &format!(" - {}: ", UpdateOperation::format(initial, target, false),),
false,
io_interface::NORMAL,
);
@@ -328,8 +325,8 @@ pub trait VcsDownloader:
return Err(e);
}
if self.io().is_debug() {
- self.io_mut().write_error(
- PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
+ self.io_mut().write_error3(
+ &format!("Failed: [{}] {}", get_class(&e), e,),
true,
io_interface::NORMAL,
);
@@ -339,8 +336,8 @@ pub trait VcsDownloader:
.collect(),
)) > 0
{
- self.io_mut().write_error(
- PhpMixed::String(" Failed, trying the next URL".to_string()),
+ self.io_mut().write_error3(
+ " Failed, trying the next URL",
true,
io_interface::NORMAL,
);
@@ -379,11 +376,8 @@ pub trait VcsDownloader:
// escape angle brackets for proper output in the console
logs = str_replace("<", "\\<", &logs);
- self.io_mut().write_error(
- PhpMixed::String(format!(" {}", message)),
- true,
- io_interface::NORMAL,
- );
+ self.io_mut()
+ .write_error3(&format!(" {}", message), true, io_interface::NORMAL);
self.io_mut()
.write_error3(&logs, true, io_interface::NORMAL);
}
@@ -403,16 +397,16 @@ pub trait VcsDownloader:
package: &dyn PackageInterface,
path: &str,
) -> Result<Box<dyn PromiseInterface>> {
- self.io_mut().write_error(
- PhpMixed::String(format!(
- " - {}",
- UninstallOperation::format(package, false)
- )),
+ self.io_mut().write_error3(
+ &format!(" - {}", UninstallOperation::format(package, false)),
true,
io_interface::NORMAL,
);
- let promise = self.filesystem_mut().remove_directory_async(path);
+ let promise = self
+ .filesystem_mut()
+ .borrow_mut()
+ .remove_directory_async(path);
let path = path.to_string();
Ok(
@@ -432,7 +426,12 @@ pub trait VcsDownloader:
fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> {
let parser = VersionParser::new();
- let guesser = VersionGuesser::new(self.config(), self.process(), &parser, self.io());
+ let guesser = VersionGuesser::new(
+ std::rc::Rc::clone(self.config()),
+ std::rc::Rc::clone(self.process()),
+ parser.clone(),
+ Some(self.io().clone_box()),
+ );
let dumper = ArrayDumper::new();
let package_config = dumper.dump(package);
diff --git a/crates/shirabe/src/downloader/xz_downloader.rs b/crates/shirabe/src/downloader/xz_downloader.rs
index 61a7f14..99c29d3 100644
--- a/crates/shirabe/src/downloader/xz_downloader.rs
+++ b/crates/shirabe/src/downloader/xz_downloader.rs
@@ -24,12 +24,12 @@ pub struct XzDownloader {
impl XzDownloader {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
cache: Option<Cache>,
- filesystem: Filesystem,
- process: ProcessExecutor,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
Self {
inner: FileDownloader::new(
@@ -54,7 +54,7 @@ impl XzDownloader {
let command = vec!["tar", "-xJf", file, "-C", path];
let mut ignored_output = PhpMixed::Null;
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute(
PhpMixed::List(
command
.iter()
@@ -71,7 +71,7 @@ impl XzDownloader {
let process_error = format!(
"Failed to execute {}\n\n{}",
command.join(" "),
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
);
bail!(process_error);
diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs
index 7f779c8..bfaf180 100644
--- a/crates/shirabe/src/downloader/zip_downloader.rs
+++ b/crates/shirabe/src/downloader/zip_downloader.rs
@@ -7,7 +7,7 @@ use crate::util::ini_helper::IniHelper;
use crate::util::platform::Platform;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder;
use shirabe_external_packages::symfony::component::process::process::Process;
@@ -164,9 +164,7 @@ impl ZipDownloader {
}
}
- self.inner
- .inner
- .download(package, path, prev_package, output)
+ self.inner.download(package, path, prev_package, output)
}
fn extract_with_system_unzip(
@@ -217,18 +215,23 @@ impl ZipDownloader {
let mut output = String::new();
if self
.inner
- .inner
.process
.execute(&[command_spec[1].as_str()], &mut output)
== 0
{
- if let Some(m) =
- Preg::is_match_strict_groups(r"^\s*7-Zip(?:\s\[64\])?\s([0-9.]+)", &output)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"^\s*7-Zip(?:\s\[64\])?\s([0-9.]+)",
+ &output,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- if version_compare(&m[1], "21.01", "<") {
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ if version_compare(&m1, "21.01", "<") {
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,
+ executable, m1, executable,
));
}
}
@@ -281,7 +284,6 @@ impl ZipDownloader {
io.write_error(&format!(
"Origin URL: {}",
self.inner
- .inner
.process_url(package, &package.get_dist_url().unwrap_or_default())
));
let headers = FileDownloader::response_headers.lock().unwrap();
@@ -297,7 +299,7 @@ impl ZipDownloader {
self.extract_with_zip_archive(package, file, path)
};
- match self.inner.process.execute_async(&command) {
+ match self.inner.process.borrow_mut().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 a1bc0b4..a8d9a74 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::component::console::application::Application;
use shirabe_external_packages::symfony::component::console::input::string_input::StringInput;
use shirabe_external_packages::symfony::component::console::output::console_output::ConsoleOutput;
@@ -65,7 +65,7 @@ pub struct EventDispatcher {
pub(crate) composer: Box<PartialComposer>,
pub(crate) io: Box<dyn IOInterface>,
pub(crate) loader: Option<ClassLoader>,
- pub(crate) process: ProcessExecutor,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
pub(crate) listeners: IndexMap<String, IndexMap<i64, Vec<Callable>>>,
pub(crate) run_scripts: bool,
event_stack: Vec<String>,
@@ -78,9 +78,11 @@ impl EventDispatcher {
pub fn new(
composer: PartialComposer,
io: Box<dyn IOInterface>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io)))
+ });
let event_stack: Vec<String> = Vec::new();
let skip_scripts_env =
Platform::get_env("COMPOSER_SKIP_SCRIPTS").unwrap_or_else(|| "".to_string());
@@ -202,15 +204,15 @@ impl EventDispatcher {
// TODO(plugin): PackageEvent / CommandEvent / PreCommandRunEvent specialization
// requires polymorphic dispatch; the simple Event branch is sufficient for now.
let details: Option<String> = None;
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Dispatching <info>{}</info>{} event",
event.get_name(),
details
.as_ref()
.map(|d| format!(" ({})", d))
.unwrap_or_default()
- )),
+ ),
true,
crate::io::io_interface::NORMAL,
);
@@ -318,14 +320,14 @@ impl EventDispatcher {
} else {
"?".to_string()
};
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"> %s: %s",
&[
PhpMixed::String(formatted_event_name_with_args.clone()),
PhpMixed::String(format!("{}->{}", prefix, method_name)),
],
- )),
+ ),
true,
crate::io::io_interface::VERBOSE,
);
@@ -335,14 +337,14 @@ impl EventDispatcher {
} else {
match callable {
Callable::String(ref callable_str) if self.is_composer_script(callable_str) => {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"> %s: %s",
&[
PhpMixed::String(formatted_event_name_with_args.clone()),
PhpMixed::String(callable_str.clone()),
],
- )),
+ ),
true,
crate::io::io_interface::VERBOSE,
);
@@ -391,8 +393,7 @@ impl EventDispatcher {
);
let exit_code = self.execute_tty(&exec)?;
if exit_code != 0 {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(&sprintf(
&format!(
"<error>Script %s handling the %s event returned with error code {}</error>",
exit_code
@@ -401,16 +402,13 @@ impl EventDispatcher {
PhpMixed::String(callable_str.clone()),
PhpMixed::String(event.get_name().to_string()),
],
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
return Err(anyhow::anyhow!(ScriptExecutionException(
RuntimeException {
message: format!(
"Error Output: {}",
- self.process.get_error_output()
+ self.process.borrow().get_error_output()
),
code: exit_code,
}
@@ -425,14 +423,10 @@ impl EventDispatcher {
))
.is_empty()
{
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(&sprintf(
"<warning>You made a reference to a non-existent script %s</warning>",
&[PhpMixed::String(callable_str.clone())],
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
}
let composer_full = self.composer_as_full_or_panic();
@@ -458,14 +452,14 @@ impl EventDispatcher {
Ok(v) => r#return = v,
Err(e) => {
if e.downcast_ref::<ScriptExecutionException>().is_some() {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"<error>Script %s was called via %s</error>",
&[
PhpMixed::String(callable_str.clone()),
PhpMixed::String(event.get_name().to_string()),
],
- )),
+ ),
true,
crate::io::io_interface::QUIET,
);
@@ -482,27 +476,19 @@ impl EventDispatcher {
self.make_autoloader(event, &Callable::String(callable_str.clone()));
if !class_exists(&class_name) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(&format!(
"<warning>Class {} is not autoloadable, can not call {} script</warning>",
class_name,
event.get_name()
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
continue;
}
if !is_callable(&PhpMixed::String(callable_str.clone())) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(&format!(
"<warning>Method {} is not callable, can not call {} script</warning>",
callable_str,
event.get_name()
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
continue;
}
@@ -513,8 +499,8 @@ impl EventDispatcher {
Err(e) => {
let message =
"Script %s handling the %s event terminated with an exception";
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"<error>{}</error>",
sprintf(
message,
@@ -523,7 +509,7 @@ impl EventDispatcher {
PhpMixed::String(event.get_name().to_string()),
],
)
- )),
+ ),
true,
crate::io::io_interface::QUIET,
);
@@ -542,15 +528,11 @@ impl EventDispatcher {
),
);
if !class_exists(&class_name) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(&format!(
"<warning>Class {} is not autoloadable, can not call {} script</warning>",
class_name,
event.get_name()
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
continue;
}
if !is_a(
@@ -558,29 +540,21 @@ impl EventDispatcher {
"Symfony\\Component\\Console\\Command\\Command",
true,
) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(&format!(
"<warning>Class {} does not extend Symfony\\Component\\Console\\Command\\Command, can not call {} script</warning>",
class_name,
event.get_name()
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
continue;
}
if defined(&format!(
"Composer\\Script\\ScriptEvents::{}",
str_replace("-", "_", &strtoupper(event.get_name()))
)) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(&format!(
"<warning>You cannot bind {} to a Command class, use a non-reserved name</warning>",
event.get_name()
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
continue;
}
@@ -628,8 +602,8 @@ impl EventDispatcher {
Err(e) => {
let message =
"Script %s handling the %s event terminated with an exception";
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"<error>{}</error>",
sprintf(
message,
@@ -638,7 +612,7 @@ impl EventDispatcher {
PhpMixed::String(event.get_name().to_string()),
],
)
- )),
+ ),
true,
crate::io::io_interface::QUIET,
);
@@ -671,23 +645,20 @@ impl EventDispatcher {
};
if self.io.is_verbose() {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"> %s: %s",
&[
PhpMixed::String(event.get_name().to_string()),
PhpMixed::String(exec.clone()),
],
- )),
+ ),
true,
crate::io::io_interface::NORMAL,
);
} else if self.event_needs_to_output(event) {
- self.io.write_error(
- PhpMixed::String(sprintf(
- "> %s",
- &[PhpMixed::String(exec.clone())],
- )),
+ self.io.write_error3(
+ &sprintf("> %s", &[PhpMixed::String(exec.clone())]),
true,
crate::io::io_interface::NORMAL,
);
@@ -744,16 +715,19 @@ impl EventDispatcher {
}
// match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it
// in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path
- let mat = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^[^\\'\"\\s/\\\\]+}",
&path_and_args,
+ Some(&mut m),
)
- .ok()
- .flatten();
- if let Some(m) = mat {
- if !file_exists(&m[0]) {
+ .unwrap_or(false)
+ {
+ let m0 =
+ m.get(&CaptureKey::ByIndex(0)).cloned().unwrap_or_default();
+ if !file_exists(&m0) {
let finder = ExecutableFinder::new();
- if let Some(path_to_exec) = finder.find(&m[0]) {
+ if let Some(path_to_exec) = finder.find(&m0) {
let mut path_to_exec = path_to_exec;
if Platform::is_windows() {
let exec_without_ext = Preg::replace(
@@ -809,8 +783,7 @@ impl EventDispatcher {
let exit_code = self.execute_tty(&exec)?;
if exit_code != 0 {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(&sprintf(
&format!(
"<error>Script %s handling the %s event returned with error code {}</error>",
exit_code
@@ -819,16 +792,13 @@ impl EventDispatcher {
PhpMixed::String(callable_str.clone()),
PhpMixed::String(event.get_name().to_string()),
],
- )),
- true,
- crate::io::io_interface::QUIET,
- );
+ ), true, crate::io::io_interface::QUIET);
return Err(anyhow::anyhow!(ScriptExecutionException(
RuntimeException {
message: format!(
"Error Output: {}",
- self.process.get_error_output()
+ self.process.borrow().get_error_output()
),
code: exit_code,
}
@@ -878,10 +848,10 @@ impl EventDispatcher {
fn execute_tty(&self, exec: &str) -> anyhow::Result<i64> {
if self.io.is_interactive() {
- return self.process.execute_tty(exec);
+ return self.process.borrow_mut().execute_tty(exec);
}
- self.process.execute(exec)
+ self.process.borrow_mut().execute(exec)
}
fn get_php_exec_command(&self) -> anyhow::Result<String> {
@@ -932,27 +902,27 @@ impl EventDispatcher {
event: &Event,
) -> anyhow::Result<PhpMixed> {
if self.io.is_verbose() {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"> %s: %s::%s",
&[
PhpMixed::String(event.get_name().to_string()),
PhpMixed::String(class_name.to_string()),
PhpMixed::String(method_name.to_string()),
],
- )),
+ ),
true,
crate::io::io_interface::NORMAL,
);
} else if self.event_needs_to_output(event) {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"> %s::%s",
&[
PhpMixed::String(class_name.to_string()),
PhpMixed::String(method_name.to_string()),
],
- )),
+ ),
true,
crate::io::io_interface::NORMAL,
);
@@ -1081,11 +1051,11 @@ impl EventDispatcher {
};
if self.skip_scripts.iter().any(|s| s == event.get_name()) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Skipped script listeners for <info>{}</info> because of COMPOSER_SKIP_SCRIPTS",
event.get_name()
- )),
+ ),
true,
crate::io::io_interface::VERBOSE,
);
@@ -1168,11 +1138,10 @@ impl EventDispatcher {
let bin_dir = self
.composer
.get_config()
+ .borrow_mut()
.get("bin-dir")
- .and_then(|v| match v {
- PhpMixed::String(s) => Some(s),
- _ => None,
- })
+ .as_string()
+ .map(|s| s.to_string())
.unwrap_or_default();
if shirabe_php_shim::is_dir(&bin_dir) {
let bin_dir = realpath(&bin_dir).unwrap_or(bin_dir);
@@ -1288,11 +1257,10 @@ impl EventDispatcher {
let vendor_dir = composer
.get_config()
+ .borrow_mut()
.get("vendor-dir")
- .and_then(|v| match v {
- PhpMixed::String(s) => Some(s),
- _ => None,
- })
+ .as_string()
+ .map(|s| s.to_string())
.unwrap_or_default();
let mut loader = generator.create_loader(&map, &vendor_dir);
loader.register(false);
diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs
index 3d6ab6d..321d590 100644
--- a/crates/shirabe/src/factory.rs
+++ b/crates/shirabe/src/factory.rs
@@ -262,8 +262,8 @@ impl Factory {
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())),
+ io_ref.write_error3(
+ &format!("Loading config file {}", file.get_path()),
true,
crate::io::io_interface::DEBUG,
);
@@ -278,10 +278,7 @@ impl Factory {
}
config.set_config_source(JsonConfigSource::new(file.clone(), false));
- let htaccess_protect = config
- .get("htaccess-protect")
- .and_then(|v| v.as_bool())
- .unwrap_or(false);
+ let htaccess_protect = config.get("htaccess-protect").as_bool().unwrap_or(false);
if htaccess_protect {
// Protect directory against web access. Since HOME could be
// the www-data's user home and be web-accessible it is a
@@ -311,8 +308,8 @@ impl Factory {
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())),
+ io_ref.write_error3(
+ &format!("Loading config file {}", auth_file.get_path()),
true,
crate::io::io_interface::DEBUG,
);
@@ -460,7 +457,7 @@ impl Factory {
}
if !Platform::is_input_completion_process() {
- if let Err(e) = file.validate_schema(JsonFile::LAX_SCHEMA) {
+ if let Err(e) = file.validate_schema(JsonFile::LAX_SCHEMA, None) {
if let Some(jve) = e.downcast_ref::<JsonValidationException>() {
let errors = format!(
" - {}",
@@ -476,7 +473,7 @@ impl Factory {
}
}
- local_config_data = file.read()?;
+ local_config_data = file.read()?.as_array().cloned().unwrap_or_default();
local_config_source = file.get_path().to_string();
} else if let Some(LocalConfigInput::Data(data)) = local_config {
local_config_data = data;
@@ -486,26 +483,26 @@ impl Factory {
let mut config = Self::create_config(Some(io), Some(&cwd))?;
let is_global = local_config_source != Config::SOURCE_UNKNOWN
&& realpath(&config.get_str("home")?) == realpath(&dirname(&local_config_source));
- config.merge(local_config_data.clone(), local_config_source.clone());
+ config.merge(&local_config_data, &local_config_source);
if let Some(ref composer_file_path) = composer_file {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Loading config file {} ({})",
composer_file_path,
realpath(composer_file_path).unwrap_or_default()
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
- config.set_config_source(JsonConfigSource::new(
+ config.set_config_source(Box::new(JsonConfigSource::new(
JsonFile::new(
realpath(composer_file_path).unwrap_or_default(),
None,
- Some(io),
+ Some(io.clone_box()),
)?,
false,
- ));
+ )));
let local_auth_file = JsonFile::new(
format!(
@@ -513,36 +510,26 @@ impl Factory {
dirname(&realpath(composer_file_path).unwrap_or_default())
),
None,
- Some(io),
+ Some(io.clone_box()),
)?;
if local_auth_file.exists() {
- io.write_error(
- PhpMixed::String(format!(
- "Loading config file {}",
- local_auth_file.get_path()
- )),
+ io.write_error3(
+ &format!("Loading config file {}", local_auth_file.get_path()),
true,
crate::io::io_interface::DEBUG,
);
- Self::validate_json_schema(
- Some(io),
- ValidateJsonInput::File(local_auth_file.clone()),
- JsonFile::AUTH_SCHEMA,
- None,
- )?;
+ // TODO(phase-b): validate_json_schema/ValidateJsonInput::File expects an owned
+ // JsonFile (PHP class semantics share refs); needs Rc<RefCell<JsonFile>> refactor.
+ let _ = &local_auth_file;
+ let auth_read = local_auth_file.read()?;
let mut wrapped: IndexMap<String, PhpMixed> = IndexMap::new();
- wrapped.insert(
- "config".to_string(),
- PhpMixed::Array(
- local_auth_file
- .read()?
- .into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ),
- );
- config.merge(wrapped, local_auth_file.get_path().to_string());
- config.set_local_auth_config_source(JsonConfigSource::new(local_auth_file, true));
+ wrapped.insert("config".to_string(), auth_read);
+ let auth_path = local_auth_file.get_path().to_string();
+ config.merge(&wrapped, &auth_path);
+ config.set_local_auth_config_source(Box::new(JsonConfigSource::new(
+ local_auth_file,
+ true,
+ )));
}
}
@@ -551,25 +538,30 @@ impl Factory {
let vendor_dir = config.get_str("vendor-dir")?;
+ // wrap config into Rc<RefCell<...>> for shared ownership across composer + downloaders/utils
+ let config = std::rc::Rc::new(std::cell::RefCell::new(config));
+
// initialize composer
let mut composer: PartialComposerOrComposer = if full_load {
PartialComposerOrComposer::Full(Composer::new())
} else {
PartialComposerOrComposer::Partial(PartialComposer::default())
};
- composer.set_config(config.clone());
+ composer.set_config(std::rc::Rc::clone(&config));
if is_global {
composer.set_global();
}
if full_load {
// load auth configs into the IO instance
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
// load existing Composer\InstalledVersions instance if available and scripts/plugins are allowed, as they might need it
// we only load if the InstalledVersions class wasn't defined yet so that this is only loaded once
- let installed_versions_path =
- format!("{}/composer/installed.php", config.get_str("vendor-dir")?);
+ let installed_versions_path = format!(
+ "{}/composer/installed.php",
+ config.borrow_mut().get_str("vendor-dir")?
+ );
if !disable_plugins.is_disabled_at_all()
&& !disable_scripts
&& !class_exists("Composer\\InstalledVersions")
@@ -583,22 +575,33 @@ impl Factory {
}
}
- let http_downloader = Self::create_http_downloader(io, &config, IndexMap::new())?;
- let process = ProcessExecutor::new(io);
+ let http_downloader = std::rc::Rc::new(std::cell::RefCell::new(
+ Self::create_http_downloader(io, &config, IndexMap::new())?,
+ ));
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)));
let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
- http_downloader.clone(),
- Some(process.clone()),
+ std::rc::Rc::clone(&http_downloader),
+ Some(std::rc::Rc::clone(&process)),
)));
composer.set_loop(r#loop.clone());
// initialize event dispatcher
- let mut dispatcher =
- EventDispatcher::new(composer.as_partial(), io.clone_box(), Some(process.clone()));
+ let mut dispatcher = EventDispatcher::new(
+ composer.as_partial(),
+ io.clone_box(),
+ Some(std::rc::Rc::clone(&process)),
+ );
dispatcher.set_run_scripts(!disable_scripts);
composer.set_event_dispatcher(dispatcher.clone());
// initialize repository manager
- let rm = RepositoryFactory::manager(io, &config, &http_downloader, &dispatcher, &process)?;
+ let rm = RepositoryFactory::manager(
+ io,
+ &config,
+ Some(std::rc::Rc::clone(&http_downloader)),
+ Some(dispatcher.clone()),
+ Some(std::rc::Rc::clone(&process)),
+ )?;
composer.set_repository_manager(rm.clone());
// force-set the version of the global package if not defined as
@@ -609,9 +612,18 @@ impl Factory {
// load package
let parser = VersionParser::new();
- let guesser = VersionGuesser::new(&config, process.clone(), parser.clone());
- let mut loader =
- self.load_root_package(rm.clone(), config.clone(), parser, guesser, io.clone_box());
+ let guesser = VersionGuesser::new(
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&process),
+ parser.clone(),
+ );
+ let mut loader = self.load_root_package(
+ rm.clone(),
+ std::rc::Rc::clone(&config),
+ parser,
+ guesser,
+ io.clone_box(),
+ );
let package = loader.load(
local_config_data
.iter()
@@ -655,7 +667,7 @@ impl Factory {
composer_full.set_autoload_generator(generator);
// initialize archive manager
- let am = self.create_archive_manager(&config, &dm, &r#loop)?;
+ let am = self.create_archive_manager(&*config.borrow(), &dm, &r#loop)?;
composer_full.set_archive_manager(am);
}
@@ -666,13 +678,17 @@ impl Factory {
if let PartialComposerOrComposer::Full(ref mut composer_full) = composer {
if let Some(ref composer_file_path) = composer_file {
let lock_file = Self::get_lock_file(composer_file_path);
- let lock_enabled = config.get("lock").and_then(|v| v.as_bool()).unwrap_or(true);
+ let lock_enabled = config
+ .borrow_mut()
+ .get("lock")
+ .and_then(|v| v.as_bool())
+ .unwrap_or(true);
if !lock_enabled && file_exists(&lock_file) {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>{} is present but ignored as the \"lock\" config option is disabled.</warning>",
lock_file
- )),
+ ),
true,
crate::io::io_interface::NORMAL,
);
@@ -691,7 +707,7 @@ impl Factory {
)?,
im.clone(),
file_get_contents(composer_file_path).unwrap_or_default(),
- process.clone(),
+ std::rc::Rc::clone(&process),
);
composer_full.set_locker(locker);
} else {
@@ -708,7 +724,7 @@ impl Factory {
),
448,
),
- process.clone(),
+ std::rc::Rc::clone(&process),
);
composer_full.set_locker(locker);
}
@@ -719,7 +735,7 @@ impl Factory {
if !composer_full.is_global() {
global_composer = self.create_global_composer(
io,
- &config,
+ &*config.borrow(),
disable_plugins,
disable_scripts,
false,
@@ -776,9 +792,13 @@ impl Factory {
mut rm: RepositoryManager,
vendor_dir: &str,
root_package: &dyn RootPackageInterface,
- process: Option<&ProcessExecutor>,
+ process: Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) {
- let fs = process.map(|p| Filesystem::new(Some(p.clone())));
+ let fs = process.map(|p| {
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(Some(
+ std::rc::Rc::clone(p),
+ ))))
+ });
rm.set_local_repository(Box::new(
InstalledFilesystemRepository::new(
@@ -827,8 +847,8 @@ impl Factory {
) {
Ok(c) => Some(c.into_partial()),
Err(e) => {
- io.write_error(
- PhpMixed::String(format!("Failed to initialize global composer: {}", e)),
+ io.write_error3(
+ &format!("Failed to initialize global composer: {}", e),
true,
crate::io::io_interface::DEBUG,
);
@@ -842,21 +862,27 @@ impl Factory {
pub fn create_download_manager(
&self,
io: &dyn IOInterface,
- config: &Config,
- http_downloader: &HttpDownloader,
- process: &ProcessExecutor,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: &std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
event_dispatcher: Option<&EventDispatcher>,
) -> anyhow::Result<std::rc::Rc<std::cell::RefCell<DownloadManager>>> {
let mut cache: Option<Cache> = None;
if config
+ .borrow_mut()
.get("cache-files-ttl")
.and_then(|v| v.as_int())
.unwrap_or(0)
> 0
{
- let mut c = Cache::new(io, &config.get_str("cache-files-dir")?, "a-z0-9_./");
+ let mut c = Cache::new(
+ io,
+ &config.borrow_mut().get_str("cache-files-dir")?,
+ "a-z0-9_./",
+ );
c.set_read_only(
config
+ .borrow_mut()
.get("cache-read-only")
.and_then(|v| v.as_bool())
.unwrap_or(false),
@@ -864,10 +890,12 @@ impl Factory {
cache = Some(c);
}
- let fs = Filesystem::new(Some(process.clone()));
+ let fs = std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(Some(
+ std::rc::Rc::clone(process),
+ ))));
- let mut dm = DownloadManager::new(io.clone_box(), false, fs.clone());
- let preferred = config.get("preferred-install").cloned();
+ let mut dm = DownloadManager::new(io.clone_box(), false, Some(std::rc::Rc::clone(&fs)));
+ let preferred = config.borrow_mut().get("preferred-install").cloned();
match preferred.as_ref().and_then(|v| v.as_string()) {
Some("dist") => {
dm.set_prefer_dist(true);
@@ -901,141 +929,141 @@ impl Factory {
"git",
Box::new(GitDownloader::new(
io.clone_box(),
- config.clone(),
- process.clone(),
- fs.clone(),
+ std::rc::Rc::clone(&config),
+ Some(std::rc::Rc::clone(&process)),
+ Some(std::rc::Rc::clone(&fs)),
)),
);
dm.set_downloader(
"svn",
Box::new(SvnDownloader::new(
io.clone_box(),
- config.clone(),
- process.clone(),
- fs.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::clone(&fs),
)),
);
dm.set_downloader(
"fossil",
Box::new(FossilDownloader::new(
io.clone_box(),
- config.clone(),
- process.clone(),
- fs.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::clone(&fs),
)),
);
dm.set_downloader(
"hg",
Box::new(HgDownloader::new(
io.clone_box(),
- config.clone(),
- process.clone(),
- fs.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::clone(&fs),
)),
);
dm.set_downloader(
"perforce",
Box::new(PerforceDownloader::new(
io.clone_box(),
- config.clone(),
- process.clone(),
- fs.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::clone(&fs),
)),
);
dm.set_downloader(
"zip",
Box::new(ZipDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"rar",
Box::new(RarDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"tar",
Box::new(TarDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"gzip",
Box::new(GzipDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"xz",
Box::new(XzDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"phar",
Box::new(PharDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
dm.set_downloader(
"file",
Box::new(FileDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ Some(std::rc::Rc::clone(&fs)),
+ Some(std::rc::Rc::clone(&process)),
)),
);
dm.set_downloader(
"path",
Box::new(PathDownloader::new(
io.clone_box(),
- config.clone(),
- http_downloader.clone(),
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::clone(http_downloader),
event_dispatcher.cloned(),
cache.clone(),
- fs.clone(),
- process.clone(),
+ std::rc::Rc::clone(&fs),
+ std::rc::Rc::clone(&process),
)),
);
@@ -1088,57 +1116,75 @@ impl Factory {
im: &InstallationManager,
composer: &PartialComposerOrComposer,
io: &dyn IOInterface,
- process: Option<&ProcessExecutor>,
+ process: Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) {
- let fs = Filesystem::new(process.cloned());
+ let fs = std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(
+ process.map(std::rc::Rc::clone),
+ )));
let bin_dir = trim(
- &composer.get_config().get_str("bin-dir").unwrap_or_default(),
+ &composer
+ .get_config()
+ .borrow_mut()
+ .get_str("bin-dir")
+ .unwrap_or_default(),
"/",
);
let bin_compat = composer
.get_config()
+ .borrow_mut()
.get_str("bin-compat")
.unwrap_or_default();
let vendor_dir = trim(
&composer
.get_config()
+ .borrow_mut()
.get_str("vendor-dir")
.unwrap_or_default(),
"/",
);
- let binary_installer =
- BinaryInstaller::new(io.clone_box(), bin_dir, bin_compat, fs.clone(), vendor_dir);
+ let binary_installer = BinaryInstaller::new(
+ io.clone_box(),
+ bin_dir,
+ bin_compat,
+ Some(std::rc::Rc::clone(&fs)),
+ vendor_dir,
+ );
let mut im = im.clone();
im.add_installer(Box::new(LibraryInstaller::new(
io.clone_box(),
composer.as_partial(),
None,
- fs.clone(),
+ Some(std::rc::Rc::clone(&fs)),
binary_installer.clone(),
)));
im.add_installer(Box::new(PluginInstaller::new(
io.clone_box(),
composer.as_partial(),
- fs.clone(),
+ Some(std::rc::Rc::clone(&fs)),
binary_installer.clone(),
)));
im.add_installer(Box::new(MetapackageInstaller::new(io.clone_box())));
}
- fn purge_packages(&self, repo: &dyn InstalledRepositoryInterface, im: &InstallationManager) {
+ fn purge_packages(
+ &self,
+ repo: &dyn InstalledRepositoryInterface,
+ im: &mut InstallationManager,
+ ) -> anyhow::Result<()> {
for package in repo.get_packages() {
- if !im.is_package_installed(repo, package.as_ref()) {
+ if !im.is_package_installed(repo, package.as_ref())? {
// TODO(phase-b): mutable access on repo trait object
let _ = package;
}
}
+ Ok(())
}
fn load_root_package(
&self,
rm: RepositoryManager,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
parser: VersionParser,
guesser: VersionGuesser,
io: Box<dyn IOInterface>,
@@ -1187,7 +1233,7 @@ impl Factory {
/// If you are calling this in a plugin, you probably should instead use `$composer->getLoop()->getHttpDownloader()`
pub fn create_http_downloader(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
options: IndexMap<String, PhpMixed>,
) -> anyhow::Result<HttpDownloader> {
// TODO(plugin): static `$warned` flag — port as a OnceCell or atomic in Phase B.
@@ -1202,16 +1248,14 @@ impl Factory {
unsafe { WARNED = true };
disable_tls = !extension_loaded("openssl");
} else if config
+ .borrow_mut()
.get("disable-tls")
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
if !unsafe { WARNED } {
- io.write_error(
- PhpMixed::String(
- "<warning>You are running Composer with SSL/TLS protection disabled.</warning>"
- .to_string(),
- ),
+ io.write_error3(
+ "<warning>You are running Composer with SSL/TLS protection disabled.</warning>",
true,
crate::io::io_interface::NORMAL,
);
@@ -1228,18 +1272,18 @@ impl Factory {
}
let mut http_downloader_options: IndexMap<String, PhpMixed> = IndexMap::new();
if !disable_tls {
- if "" != config.get_str("cafile").unwrap_or_default() {
+ if "" != config.borrow_mut().get_str("cafile").unwrap_or_default() {
let mut ssl_map: IndexMap<String, PhpMixed> = IndexMap::new();
ssl_map.insert(
"cafile".to_string(),
- PhpMixed::String(config.get_str("cafile").unwrap_or_default()),
+ PhpMixed::String(config.borrow_mut().get_str("cafile").unwrap_or_default()),
);
http_downloader_options.insert(
"ssl".to_string(),
PhpMixed::Array(ssl_map.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
}
- if "" != config.get_str("capath").unwrap_or_default() {
+ if "" != config.borrow_mut().get_str("capath").unwrap_or_default() {
let existing_ssl = http_downloader_options
.get("ssl")
.and_then(|v| v.as_array())
@@ -1249,7 +1293,7 @@ impl Factory {
ssl_map.insert(
"capath".to_string(),
Box::new(PhpMixed::String(
- config.get_str("capath").unwrap_or_default(),
+ config.borrow_mut().get_str("capath").unwrap_or_default(),
)),
);
http_downloader_options.insert("ssl".to_string(), PhpMixed::Array(ssl_map));
@@ -1259,7 +1303,7 @@ impl Factory {
}
let http_downloader = match HttpDownloader::new_full(
io.clone_box(),
- config.clone(),
+ std::rc::Rc::clone(config),
http_downloader_options,
disable_tls,
) {
@@ -1267,27 +1311,18 @@ impl Factory {
Err(e) => {
if let Some(te) = e.downcast_ref::<TransportException>() {
if strpos(&te.get_message(), "cafile").is_some() {
- io.write(
- PhpMixed::String(
- "<error>Unable to locate a valid CA certificate file. You must set a valid 'cafile' option.</error>"
- .to_string(),
- ),
+ io.write3(
+ "<error>Unable to locate a valid CA certificate file. You must set a valid 'cafile' option.</error>",
true,
crate::io::io_interface::NORMAL,
);
- io.write(
- PhpMixed::String(
- "<error>A valid CA certificate file is required for SSL/TLS protection.</error>"
- .to_string(),
- ),
+ io.write3(
+ "<error>A valid CA certificate file is required for SSL/TLS protection.</error>",
true,
crate::io::io_interface::NORMAL,
);
- io.write(
- PhpMixed::String(
- "<error>You can disable this error, at your own risk, by setting the 'disable-tls' option to true.</error>"
- .to_string(),
- ),
+ io.write3(
+ "<error>You can disable this error, at your own risk, by setting the 'disable-tls' option to true.</error>",
true,
crate::io::io_interface::NORMAL,
);
@@ -1321,8 +1356,8 @@ impl Factory {
}
if let Some(io_ref) = io {
- io_ref.write_error(
- PhpMixed::String("Loading auth config from COMPOSER_AUTH".to_string()),
+ io_ref.write_error3(
+ "Loading auth config from COMPOSER_AUTH",
true,
crate::io::io_interface::DEBUG,
);
@@ -1400,8 +1435,8 @@ impl Factory {
implode(&format!("{} - ", PHP_EOL), jve.get_errors())
);
if let Some(io_ref) = io {
- io_ref.write_error(
- PhpMixed::String(format!("<warning>{}</>", msg)),
+ io_ref.write_error3(
+ &format!("<warning>{}</>", msg),
true,
crate::io::io_interface::NORMAL,
);
@@ -1431,7 +1466,7 @@ pub enum PartialComposerOrComposer {
}
impl PartialComposerOrComposer {
- fn set_config(&mut self, config: Config) {
+ fn set_config(&mut self, config: std::rc::Rc<std::cell::RefCell<Config>>) {
match self {
Self::Full(c) => c.set_config(config),
Self::Partial(p) => p.set_config(config),
@@ -1479,7 +1514,7 @@ impl PartialComposerOrComposer {
Self::Partial(p) => p.get_package(),
}
}
- fn get_config(&self) -> &Config {
+ fn get_config(&self) -> &std::rc::Rc<std::cell::RefCell<Config>> {
match self {
Self::Full(c) => c.get_config(),
Self::Partial(p) => p.get_config(),
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index cb3da38..40d08cf 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -64,6 +64,7 @@ use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::dumper::array_dumper::ArrayDumper;
use crate::package::link::Link;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::locker::Locker;
use crate::package::package::Package;
use crate::package::package_interface::PackageInterface;
@@ -90,7 +91,7 @@ use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
#[derive(Debug)]
pub struct Installer {
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
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>,
@@ -147,7 +148,7 @@ impl Installer {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
package: Box<dyn RootPackageInterface>,
download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
repository_manager: RepositoryManager,
@@ -158,7 +159,7 @@ impl Installer {
) -> Self {
let suggested_packages_reporter = SuggestedPackagesReporter::new(io.clone_box());
let platform_requirement_filter = PlatformRequirementFilterFactory::ignore_nothing();
- let write_lock = config.get("lock").as_bool().unwrap_or(false);
+ let write_lock = config.borrow_mut().get("lock").as_bool().unwrap_or(false);
Self {
io,
@@ -290,6 +291,7 @@ impl Installer {
&& self.install
&& self
.config
+ .borrow_mut()
.get("notify-on-install")
.as_bool()
.unwrap_or(false)
@@ -305,6 +307,7 @@ impl Installer {
&& self.install
&& self
.config
+ .borrow_mut()
.get("notify-on-install")
.as_bool()
.unwrap_or(false)
@@ -330,7 +333,7 @@ impl Installer {
// Find abandoned packages and warn user
let locked_repository = self.locker.get_locked_repository(true)?;
- for package in locked_repository.get_packages() {
+ for package in CanonicalPackagesTrait::get_packages(&locked_repository) {
let complete = match package.as_complete_package() {
Some(p) if p.is_abandoned() => p,
_ => continue,
@@ -369,7 +372,7 @@ impl Installer {
self.autoload_generator
.set_platform_requirement_filter(self.platform_requirement_filter.clone_box());
self.autoload_generator.dump(
- &self.config,
+ &*self.config.borrow(),
&local_repo,
&*self.package,
&self.installation_manager,
@@ -444,7 +447,7 @@ impl Installer {
(local_repo.get_canonical_packages(), "installed")
};
if count(&packages) > 0 {
- let auditor = Auditor::new();
+ let auditor = Auditor;
let mut repo_set = RepositorySet::new(
"stable".to_string(),
IndexMap::new(),
@@ -538,7 +541,7 @@ impl Installer {
if (self.update_allow_list.is_some() || self.update_mirrors) && locked_repository.is_none()
{
- self.io.write_error(
+ self.io.write_error3(
&format!(
"<error>Cannot update {} without a lock file present. Run `composer update` to generate a lock file.</error>",
if self.update_mirrors {
@@ -621,14 +624,14 @@ impl Installer {
false,
);
- self.io.write_error(
+ self.io.write_error3(
&format!("<error>{}</error>", err),
true,
io_interface::QUIET,
);
self.io.write_error(&pretty_problem);
if !self.dev_mode {
- self.io.write_error(
+ self.io.write_error3(
"<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>",
true,
io_interface::QUIET,
@@ -645,7 +648,7 @@ impl Installer {
}
let _ = solver;
- self.io.write_error(
+ self.io.write_error3(
&format!(
"Analyzed {} packages to resolve dependencies",
count(&pool.as_ref().unwrap())
@@ -653,7 +656,7 @@ impl Installer {
true,
io_interface::VERBOSE,
);
- self.io.write_error(
+ self.io.write_error3(
&format!("Analyzed {} rules to resolve dependencies", rule_set_size),
true,
io_interface::VERBOSE,
@@ -665,7 +668,10 @@ impl Installer {
if lock_transaction.get_operations().is_empty() {
self.io.write_error("Nothing to modify in lock file");
- if self.minimal_update && self.update_allow_list.is_none() && self.locker.is_fresh() {
+ if self.minimal_update
+ && self.update_allow_list.is_none()
+ && self.locker.is_fresh().unwrap_or(false)
+ {
self.io.write_error("<warning>The --minimal-changes option should be used with package arguments or after modifying composer.json requirements, otherwise it will likely not yield any dependency changes.</warning>");
}
}
@@ -725,7 +731,13 @@ impl Installer {
}
}
- if self.config.get("lock").as_bool().unwrap_or(false) {
+ if self
+ .config
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(false)
+ {
self.io.write_error(&sprintf(
"<info>Lock file operations: %d install%s, %d update%s, %d removal%s</info>",
&[
@@ -738,21 +750,21 @@ impl Installer {
],
));
if !install_names.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Installs: {}", implode(", ", &install_names)),
true,
io_interface::VERBOSE,
);
}
if !update_names.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Updates: {}", implode(", ", &update_names)),
true,
io_interface::VERBOSE,
);
}
if !uninstalls.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Removals: {}", implode(", ", &uninstall_names)),
true,
io_interface::VERBOSE,
@@ -788,7 +800,12 @@ impl Installer {
}
// output op if lock file is enabled, but alias op only in debug verbosity
- if self.config.get("lock").as_bool().unwrap_or(false)
+ if self
+ .config
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(false)
&& (strpos(operation.get_operation_type(), "Alias") == false || self.io.is_debug())
{
let mut source_repo = String::new();
@@ -821,6 +838,7 @@ impl Installer {
self.prefer_stable || self.package.get_prefer_stable(),
self.prefer_lowest,
self.config
+ .borrow_mut()
.get("platform")
.as_array()
.cloned()
@@ -893,7 +911,7 @@ impl Installer {
true,
);
- self.io.write_error(
+ self.io.write_error3(
&format!("<error>{}</error>", err),
true,
io_interface::QUIET,
@@ -923,7 +941,13 @@ impl Installer {
local_repo: Box<dyn InstalledRepositoryInterface>,
already_solved: bool,
) -> anyhow::Result<i64> {
- if self.config.get("lock").as_bool().unwrap_or(false) {
+ if self
+ .config
+ .borrow_mut()
+ .get("lock")
+ .as_bool()
+ .unwrap_or(false)
+ {
self.io.write_error(&format!(
"<info>Installing dependencies from lock file{}</info>",
if self.dev_mode {
@@ -962,8 +986,8 @@ impl Installer {
Some(&*locked_repository),
);
- if !self.locker.is_fresh() {
- self.io.write_error(
+ if !self.locker.is_fresh()? {
+ self.io.write_error3(
"<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`.</warning>",
true,
io_interface::QUIET,
@@ -978,6 +1002,7 @@ impl Installer {
if !self
.config
+ .borrow_mut()
.get("allow-missing-requirements")
.as_bool()
.unwrap_or(false)
@@ -986,7 +1011,7 @@ impl Installer {
}
}
- for package in locked_repository.get_packages() {
+ for package in RepositoryInterface::get_packages(&locked_repository) {
request.fix_locked_package(package);
}
@@ -1031,7 +1056,7 @@ impl Installer {
// installing the locked packages on this platform resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system
if 0 != count(&lock_transaction.get_operations()) {
- self.io.write_error(
+ self.io.write_error3(
"<error>Your lock file cannot be installed on this system without changes. Please run composer update.</error>",
true,
io_interface::QUIET,
@@ -1051,7 +1076,7 @@ impl Installer {
false,
);
- self.io.write_error(
+ self.io.write_error3(
&format!("<error>{}</error>", err),
true,
io_interface::QUIET,
@@ -1114,21 +1139,21 @@ impl Installer {
],
));
if !installs.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Installs: {}", implode(", ", &installs)),
true,
io_interface::VERBOSE,
);
}
if !updates.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Updates: {}", implode(", ", &updates)),
true,
io_interface::VERBOSE,
);
}
if !uninstalls.is_empty() {
- self.io.write_error(
+ self.io.write_error3(
&format!("Removals: {}", implode(", ", &uninstalls)),
true,
io_interface::VERBOSE,
@@ -1150,6 +1175,7 @@ impl Installer {
if count(&local_repo_transaction.get_operations()) > 0 {
let vendor_dir = self
.config
+ .borrow_mut()
.get("vendor-dir")
.as_string()
.unwrap_or("")
@@ -1176,6 +1202,7 @@ impl Installer {
pub(crate) fn create_platform_repo(&self, for_update: bool) -> PlatformRepository {
let platform_overrides = if for_update {
self.config
+ .borrow_mut()
.get("platform")
.as_array()
.cloned()
@@ -1321,7 +1348,7 @@ impl Installer {
let mut preferred_versions: Option<IndexMap<String, String>> = None;
if for_update && self.minimal_update && locked_repo.is_some() {
let mut versions: IndexMap<String, String> = IndexMap::new();
- for pkg in locked_repo.unwrap().get_packages() {
+ for pkg in CanonicalPackagesTrait::get_packages(locked_repo.unwrap()) {
if pkg.as_alias_package().is_some()
|| (self.update_allow_list.is_some()
&& in_array(
@@ -1405,7 +1432,7 @@ impl Installer {
IndexMap::new()
};
- for locked_package in locked_repository.unwrap().get_packages() {
+ for locked_package in CanonicalPackagesTrait::get_packages(locked_repository.unwrap()) {
// exclude alias packages here as for root aliases, both alias and aliased are
// present in the lock repo and we only want to require the aliased version
if locked_package.as_alias_package().is_none()
@@ -1482,9 +1509,10 @@ impl Installer {
packages.insert(key, new_alias_package);
}
}
- rm.set_local_repository(Box::new(InstalledArrayRepository::new_with_packages(
- packages.into_values().collect(),
- )));
+ rm.set_local_repository(Box::new(
+ InstalledArrayRepository::new_with_packages(packages.into_values().collect())
+ .expect("InstalledArrayRepository::new_with_packages should not fail"),
+ ));
}
fn create_pool_optimizer(&self, policy: &dyn PolicyInterface) -> Option<PoolOptimizer> {
@@ -1492,7 +1520,7 @@ impl Installer {
// to configure from the outside of Installer but this is only
// a debugging tool and should never be required in any other use case
if Platform::get_env("COMPOSER_POOL_OPTIMIZER").as_deref() == Some("0") {
- self.io.write(
+ self.io.write3(
"Pool Optimizer was disabled for debugging purposes.",
true,
io_interface::DEBUG,
@@ -1507,7 +1535,7 @@ impl Installer {
fn get_audit_config(&mut self) -> anyhow::Result<&AuditConfig> {
if self.audit_config.is_none() {
self.audit_config = Some(AuditConfig::from_config(
- &self.config,
+ &mut *self.config.borrow_mut(),
self.audit,
&self.audit_format,
)?);
@@ -1523,7 +1551,7 @@ impl Installer {
if audit_config.block_insecure && !self.update_mirrors {
return Ok(Some(SecurityAdvisoryPoolFilter::new(
- Auditor::new(),
+ Auditor,
audit_config.clone(),
)));
}
@@ -1689,7 +1717,7 @@ impl Installer {
}
/// set the config instance
- pub fn set_config(&mut self, config: Config) -> &mut Self {
+ pub fn set_config(&mut self, config: std::rc::Rc<std::cell::RefCell<Config>>) -> &mut Self {
self.config = config;
self
diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs
index e6677ed..c7b880b 100644
--- a/crates/shirabe/src/installer/binary_installer.rs
+++ b/crates/shirabe/src/installer/binary_installer.rs
@@ -1,7 +1,8 @@
//! ref: composer/src/Composer/Installer/BinaryInstaller.php
use crate::io::io_interface;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
PhpMixed, basename, basename_with_suffix, chmod, dirname, fclose, fgets, file_exists,
file_get_contents, file_put_contents, fopen, is_dir, is_file, is_link, realpath, rmdir, substr,
@@ -21,7 +22,7 @@ pub struct BinaryInstaller {
pub(crate) bin_dir: String,
pub(crate) bin_compat: String,
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) filesystem: Filesystem,
+ pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
vendor_dir: Option<String>,
}
@@ -30,10 +31,11 @@ impl BinaryInstaller {
io: Box<dyn IOInterface>,
bin_dir: String,
bin_compat: String,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
vendor_dir: Option<String>,
) -> Self {
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))));
Self {
bin_dir,
bin_compat,
@@ -59,30 +61,30 @@ impl BinaryInstaller {
for bin in &binaries {
let mut bin_path = format!("{}/{}", install_path, bin);
if !file_exists(&bin_path) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" <warning>Skipped installation of bin {} for package {}: file not found in package</warning>",
bin,
package.get_name(),
- )),
+ ),
true,
io_interface::NORMAL,
);
continue;
}
if is_dir(&bin_path) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" <warning>Skipped installation of bin {} for package {}: found a directory at that path</warning>",
bin,
package.get_name(),
- )),
+ ),
true,
io_interface::NORMAL,
);
continue;
}
- if !self.filesystem.is_absolute_path(&bin_path) {
+ if !self.filesystem.borrow_mut().is_absolute_path(&bin_path) {
// in case a custom installer returned a relative path for the
// $package, we can now safely turn it into a absolute path (as we
// already checked the binary's existence). The following helpers
@@ -94,12 +96,12 @@ impl BinaryInstaller {
if file_exists(&link) {
if !is_link(&link) {
if warn_on_overwrite {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" Skipped installation of bin {} for package {}: name conflicts with an existing file",
bin,
package.get_name(),
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -108,7 +110,7 @@ impl BinaryInstaller {
}
if realpath(&link) == realpath(&bin_path) {
// It is a linked binary from a previous installation, which can be replaced with a proxy file
- self.filesystem.unlink(&link);
+ self.filesystem.borrow_mut().unlink(&link);
}
}
@@ -142,15 +144,17 @@ impl BinaryInstaller {
let link = format!("{}/{}", self.bin_dir, basename(bin));
if is_link(&link) || file_exists(&link) {
// still checking for symlinks here for legacy support
- self.filesystem.unlink(&link);
+ self.filesystem.borrow_mut().unlink(&link);
}
if is_file(&format!("{}.bat", link)) {
- self.filesystem.unlink(&format!("{}.bat", link));
+ self.filesystem
+ .borrow_mut()
+ .unlink(&format!("{}.bat", link));
}
}
// attempt removing the bin dir in case it is left empty
- if is_dir(&self.bin_dir) && self.filesystem.is_dir_empty(&self.bin_dir) {
+ if is_dir(&self.bin_dir) && self.filesystem.borrow_mut().is_dir_empty(&self.bin_dir) {
let bin_dir = self.bin_dir.clone();
let _ = Silencer::call(|| {
rmdir(&bin_dir);
@@ -167,10 +171,20 @@ impl BinaryInstaller {
let handle = fopen(bin, "r");
let line = fgets(handle.clone()).unwrap_or_default();
fclose(handle);
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- return trim(m.get(1).map(|s| s.as_str()).unwrap_or(""), None);
+ return trim(
+ m.get(&CaptureKey::ByIndex(1))
+ .map(|s| s.as_str())
+ .unwrap_or(""),
+ None,
+ );
}
"php".to_string()
@@ -194,12 +208,12 @@ impl BinaryInstaller {
self.install_unixy_proxy_binaries(bin_path, &link);
link.push_str(".bat");
if file_exists(&link) {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
" Skipped installation of bin {}.bat proxy for package {}: a .bat proxy was already installed",
bin,
package.get_name(),
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -227,13 +241,18 @@ impl BinaryInstaller {
}
pub(crate) fn initialize_bin_dir(&mut self) {
- self.filesystem.ensure_directory_exists(&self.bin_dir);
+ self.filesystem
+ .borrow_mut()
+ .ensure_directory_exists(&self.bin_dir);
// TODO(phase-b): PHP assigns realpath(...) even when realpath returns false
self.bin_dir = realpath(&self.bin_dir).unwrap_or_default();
}
pub(crate) fn generate_windows_proxy_code(&self, bin: &str, link: &str) -> String {
- let bin_path = self.filesystem.find_shortest_path(link, bin, false);
+ let bin_path = self
+ .filesystem
+ .borrow_mut()
+ .find_shortest_path(link, bin, false);
let caller = Self::determine_binary_caller(bin);
// if the target is a php file, we run the unixy proxy file
@@ -266,7 +285,10 @@ impl BinaryInstaller {
}
pub(crate) fn generate_unixy_proxy_code(&self, bin: &str, link: &str) -> String {
- let bin_path = self.filesystem.find_shortest_path(link, bin, false);
+ let bin_path = self
+ .filesystem
+ .borrow_mut()
+ .find_shortest_path(link, bin, false);
let bin_dir = ProcessExecutor::escape(&dirname(&bin_path));
let bin_file = basename(&bin_path);
@@ -289,6 +311,7 @@ impl BinaryInstaller {
};
let bin_path_exported = self
.filesystem
+ .borrow()
.find_shortest_path_code(link, bin, false, true);
let mut stream_proxy_code = String::new();
let mut stream_hint = String::new();
@@ -301,7 +324,7 @@ impl BinaryInstaller {
let vendor_dir_real = realpath(vendor_dir).unwrap_or_else(|| vendor_dir.clone());
globals_code.push_str(&format!(
"$GLOBALS['_composer_autoload_path'] = {};\n",
- self.filesystem.find_shortest_path_code(
+ self.filesystem.borrow_mut().find_shortest_path_code(
link,
&format!("{}/autoload.php", vendor_dir_real),
false,
@@ -311,9 +334,10 @@ impl BinaryInstaller {
}
// Add workaround for PHPUnit process isolation
if let Some(vendor_dir) = &self.vendor_dir {
- if self.filesystem.normalize_path(bin)
+ if self.filesystem.borrow().normalize_path(bin)
== self
.filesystem
+ .borrow()
.normalize_path(&format!("{}/phpunit/phpunit/phpunit", vendor_dir))
{
// workaround issue on PHPUnit 6.5+ running on PHP 8+
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 59e029a..3789729 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -412,8 +412,8 @@ impl InstallationManager {
if !["update", "install", "uninstall"].contains(&op_type.as_str()) {
// output alias ops in debug verbosity as they have no output otherwise
if self.io.is_debug() {
- self.io.write_error(
- PhpMixed::String(format!(" - {}", operation.show(false))),
+ self.io.write_error3(
+ &format!(" - {}", operation.show(false)),
true,
io_interface::NORMAL,
);
@@ -696,12 +696,12 @@ impl InstallationManager {
),
);
- promises.push(self.loop_.borrow().get_http_downloader().add(
+ promises.push(self.loop_.borrow().get_http_downloader().borrow_mut().add(
&url,
&PhpMixed::Array(
opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
),
- ));
+ )?);
}
continue;
@@ -767,10 +767,10 @@ impl InstallationManager {
PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
- promises.push(self.loop_.borrow().get_http_downloader().add(
+ promises.push(self.loop_.borrow().get_http_downloader().borrow_mut().add(
repo_url,
&PhpMixed::Array(opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
- ));
+ )?);
}
let _ = self.loop_.borrow_mut().wait(promises, None);
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index cf700be..a9e8d9b 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -4,7 +4,7 @@ use crate::package::package_interface::PackageInterface;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
-pub trait InstallerInterface {
+pub trait InstallerInterface: std::fmt::Debug {
fn supports(&self, package_type: &str) -> bool;
fn is_installed(
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index 17404e2..0bc87f9 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -30,7 +30,7 @@ pub struct LibraryInstaller {
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,
+ pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
pub(crate) binary_installer: BinaryInstaller,
}
@@ -40,31 +40,34 @@ impl LibraryInstaller {
io: Box<dyn IOInterface>,
composer: PartialComposer,
r#type: Option<String>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
binary_installer: Option<BinaryInstaller>,
) -> Self {
// PHP: $this->downloadManager = $composer instanceof Composer ? $composer->getDownloadManager() : null;
- let download_manager = if let Some(full_composer) =
- (composer.as_any() as &dyn Any).downcast_ref::<Composer>()
- {
- // TODO(phase-b): clone or borrow the DownloadManager from the full Composer
- Some(todo!("composer.get_download_manager() as DownloadManager"))
- } else {
- None
- };
+ let download_manager =
+ if let Some(full_composer) = composer.as_any().downcast_ref::<Composer>() {
+ // TODO(phase-b): clone or borrow the DownloadManager from the full Composer
+ Some(todo!("composer.get_download_manager() as DownloadManager"))
+ } else {
+ None
+ };
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(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"),
+ // TODO(phase-b): composer.get_config().borrow_mut().get("vendor-dir") returns a PhpMixed/String
+ &composer.get_config().borrow_mut().get("vendor-dir"),
Some("/"),
);
let binary_installer = binary_installer.unwrap_or_else(|| {
BinaryInstaller::new(
// TODO(phase-b): pass io by reference/clone
todo!("io reference"),
- rtrim(&composer.get_config().get("bin-dir"), Some("/")),
- composer.get_config().get("bin-compat"),
+ rtrim(
+ &composer.get_config().borrow_mut().get("bin-dir"),
+ Some("/"),
+ ),
+ composer.get_config().borrow_mut().get("bin-compat"),
// TODO(phase-b): pass filesystem reference
todo!("filesystem reference"),
vendor_dir.clone(),
@@ -163,6 +166,7 @@ impl LibraryInstaller {
}
self.filesystem
+ .borrow_mut()
.rename(&initial_download_path, &target_download_path);
}
@@ -185,7 +189,9 @@ impl LibraryInstaller {
}
pub(crate) fn initialize_vendor_dir(&mut self) {
- self.filesystem.ensure_directory_exists(&self.vendor_dir);
+ self.filesystem
+ .borrow_mut()
+ .ensure_directory_exists(&self.vendor_dir);
// TODO(phase-b): realpath returns Option<String>; PHP assigns to vendorDir even when false
self.vendor_dir = realpath(&self.vendor_dir).unwrap();
}
@@ -232,7 +238,7 @@ impl InstallerInterface for LibraryInstaller {
return true;
}
- if Platform::is_windows() && self.filesystem.is_junction(&install_path) {
+ if Platform::is_windows() && self.filesystem.borrow_mut().is_junction(&install_path) {
return true;
}
@@ -393,7 +399,7 @@ impl InstallerInterface for LibraryInstaller {
if strpos(package.get_name(), "/").is_some() {
let package_vendor_dir = shirabe_php_shim::dirname(&download_path);
if shirabe_php_shim::is_dir(&package_vendor_dir)
- && filesystem.is_dir_empty(&package_vendor_dir)
+ && filesystem.borrow().is_dir_empty(&package_vendor_dir)
{
Silencer::call(|| {
rmdir(&package_vendor_dir);
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index 77bd669..bb04947 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -23,7 +23,7 @@ impl PluginInstaller {
pub fn new(
io: Box<dyn IOInterface>,
composer: PartialComposer,
- fs: Option<Filesystem>,
+ fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
binary_installer: Option<BinaryInstaller>,
) -> Self {
Self {
diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs
index f8b0bba..906e2c0 100644
--- a/crates/shirabe/src/installer/project_installer.rs
+++ b/crates/shirabe/src/installer/project_installer.rs
@@ -12,14 +12,14 @@ use shirabe_php_shim::InvalidArgumentException;
pub struct ProjectInstaller {
install_path: String,
download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
- filesystem: Filesystem,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
}
impl ProjectInstaller {
pub fn new(
install_path: &str,
dm: std::rc::Rc<std::cell::RefCell<DownloadManager>>,
- fs: Filesystem,
+ fs: std::rc::Rc<std::cell::RefCell<Filesystem>>,
) -> Self {
let install_path = format!("{}/", install_path.replace('\\', "/").trim_end_matches('/'));
Self {
@@ -50,7 +50,7 @@ impl InstallerInterface for ProjectInstaller {
) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> {
let install_path = &self.install_path;
if std::path::Path::new(install_path).exists()
- && !self.filesystem.is_dir_empty(install_path)
+ && !self.filesystem.borrow().is_dir_empty(install_path)
{
return Err(InvalidArgumentException {
message: format!("Project directory {} is not empty.", install_path),
diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs
index 31a34d8..c820009 100644
--- a/crates/shirabe/src/installer/suggested_packages_reporter.rs
+++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs
@@ -161,7 +161,7 @@ impl SuggestedPackagesReporter {
let mut installed_names: Vec<String> = Vec::new();
if installed_repo.is_some() && !suggested_packages.is_empty() {
for package in installed_repo.unwrap().get_packages() {
- installed_names.extend(package.get_names());
+ installed_names.extend(package.get_names(true));
}
}
diff --git a/crates/shirabe/src/io/base_io.rs b/crates/shirabe/src/io/base_io.rs
index d4e8f73..6e6e7d0 100644
--- a/crates/shirabe/src/io/base_io.rs
+++ b/crates/shirabe/src/io/base_io.rs
@@ -54,37 +54,29 @@ pub trait BaseIO: IOInterface {
self.authentications_mut().insert(repository_name, auth);
}
- fn write_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
- self.write(messages, newline, verbosity);
- }
-
- fn write_error_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
- self.write_error(messages, newline, verbosity);
- }
-
fn check_and_set_authentication(
&mut self,
repository_name: String,
username: String,
password: Option<String>,
) {
- if self.has_authentication(&repository_name) {
- let auth = self.get_authentication(&repository_name);
+ if BaseIO::has_authentication(self, &repository_name) {
+ let auth = BaseIO::get_authentication(self, &repository_name);
if auth.get("username").and_then(|v| v.as_deref()) == Some(username.as_str())
&& *auth.get("password").unwrap_or(&None) == password
{
return;
}
- self.write_error(
- PhpMixed::String(format!(
+ self.write_error3(
+ &format!(
"<warning>Warning: You should avoid overwriting already defined auth settings for {}.</warning>",
repository_name
- )),
+ ),
true,
io_interface::NORMAL,
);
}
- self.set_authentication(repository_name, username, password);
+ BaseIO::set_authentication(self, repository_name, username, password);
}
fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()> {
@@ -358,14 +350,10 @@ pub trait BaseIO: IOInterface {
}
if !ssl_options.contains_key("local_cert") {
- self.write_error(
- PhpMixed::String(format!(
- "<warning>Warning: Client certificate configuration is missing key `local_cert` for {}.</warning>",
- domain
- )),
- true,
- io_interface::NORMAL,
- );
+ self.write_error3(&format!(
+ "<warning>Warning: Client certificate configuration is missing key `local_cert` for {}.</warning>",
+ domain
+ ), true, io_interface::NORMAL);
continue;
}
@@ -481,31 +469,31 @@ pub trait BaseIO: IOInterface {
]),
false,
) {
- self.write_error(
- PhpMixed::String(format!("<error>{}</error>", message_str)),
+ self.write_error3(
+ &format!("<error>{}</error>", message_str),
true,
io_interface::NORMAL,
);
} else if level_str == LogLevel::WARNING {
- self.write_error(
- PhpMixed::String(format!("<warning>{}</warning>", message_str)),
+ self.write_error3(
+ &format!("<warning>{}</warning>", message_str),
true,
io_interface::NORMAL,
);
} else if level_str == LogLevel::NOTICE {
- self.write_error(
- PhpMixed::String(format!("<info>{}</info>", message_str)),
+ self.write_error3(
+ &format!("<info>{}</info>", message_str),
true,
io_interface::VERBOSE,
);
} else if level_str == LogLevel::INFO {
- self.write_error(
- PhpMixed::String(format!("<info>{}</info>", message_str)),
+ self.write_error3(
+ &format!("<info>{}</info>", message_str),
true,
io_interface::VERY_VERBOSE,
);
} else {
- self.write_error(PhpMixed::String(message_str), true, io_interface::DEBUG);
+ self.write_error3(&message_str, true, io_interface::DEBUG);
}
}
}
diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs
index aa1d970..79fa9c3 100644
--- a/crates/shirabe/src/io/buffer_io.rs
+++ b/crates/shirabe/src/io/buffer_io.rs
@@ -56,15 +56,26 @@ impl BufferIO {
let output = Preg::replace_callback(
r"{(?<=^|\n|\x08)(.+?)(\x08+)}",
- |matches: &[String]| -> String {
- let pre = strip_tags(&matches[1]);
+ |matches: &indexmap::IndexMap<
+ shirabe_external_packages::composer::pcre::preg::CaptureKey,
+ String,
+ >|
+ -> String {
+ let empty = String::new();
+ let g1 = matches
+ .get(&shirabe_external_packages::composer::pcre::preg::CaptureKey::ByIndex(1))
+ .unwrap_or(&empty);
+ let g2 = matches
+ .get(&shirabe_external_packages::composer::pcre::preg::CaptureKey::ByIndex(2))
+ .unwrap_or(&empty);
+ let pre = strip_tags(g1);
- if pre.len() == matches[2].len() {
+ if pre.len() == g2.len() {
return String::new();
}
// TODO reverse parse the string, skipping span tags and \033\[([0-9;]+)m(.*?)\033\[0m style blobs
- format!("{}\n", matches[1].trim_end())
+ format!("{}\n", g1.trim_end())
},
&output,
);
diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs
index 6f17a3e..39099c4 100644
--- a/crates/shirabe/src/io/console_io.rs
+++ b/crates/shirabe/src/io/console_io.rs
@@ -11,7 +11,9 @@ use shirabe_external_packages::symfony::component::console::helper::progress_bar
use shirabe_external_packages::symfony::component::console::helper::table::Table;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::console_output_interface::ConsoleOutputInterface;
-use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
+use shirabe_external_packages::symfony::component::console::output::output_interface::{
+ self as output_interface, OutputInterface,
+};
use shirabe_external_packages::symfony::component::console::question::choice_question::ChoiceQuestion;
use shirabe_external_packages::symfony::component::console::question::question::Question;
use shirabe_php_shim::{
@@ -19,6 +21,7 @@ use shirabe_php_shim::{
is_array, is_string, mb_check_encoding, mb_convert_encoding, microtime, sprintf, str_repeat,
strip_tags, strlen,
};
+use std::cell::RefCell;
use crate::io::base_io::BaseIO;
use crate::io::io_interface::IOInterface;
@@ -33,8 +36,8 @@ pub struct ConsoleIO {
pub(crate) input: Box<dyn InputInterface>,
pub(crate) output: Box<dyn OutputInterface>,
pub(crate) helper_set: HelperSet,
- pub(crate) last_message: String,
- pub(crate) last_message_err: String,
+ pub(crate) last_message: RefCell<String>,
+ pub(crate) last_message_err: RefCell<String>,
/// @var float
start_time: Option<f64>,
@@ -54,21 +57,21 @@ impl ConsoleIO {
helper_set: HelperSet,
) -> Self {
let mut verbosity_map = IndexMap::new();
- verbosity_map.insert(io_interface::QUIET, OutputInterface::VERBOSITY_QUIET);
- verbosity_map.insert(io_interface::NORMAL, OutputInterface::VERBOSITY_NORMAL);
- verbosity_map.insert(io_interface::VERBOSE, OutputInterface::VERBOSITY_VERBOSE);
+ verbosity_map.insert(io_interface::QUIET, output_interface::VERBOSITY_QUIET);
+ verbosity_map.insert(io_interface::NORMAL, output_interface::VERBOSITY_NORMAL);
+ verbosity_map.insert(io_interface::VERBOSE, output_interface::VERBOSITY_VERBOSE);
verbosity_map.insert(
io_interface::VERY_VERBOSE,
- OutputInterface::VERBOSITY_VERY_VERBOSE,
+ output_interface::VERBOSITY_VERY_VERBOSE,
);
- verbosity_map.insert(io_interface::DEBUG, OutputInterface::VERBOSITY_DEBUG);
+ verbosity_map.insert(io_interface::DEBUG, output_interface::VERBOSITY_DEBUG);
Self {
authentications: indexmap![],
input,
output,
helper_set,
- last_message: String::new(),
- last_message_err: String::new(),
+ last_message: RefCell::new(String::new()),
+ last_message_err: RefCell::new(String::new()),
start_time: None,
verbosity_map,
}
@@ -79,21 +82,14 @@ impl ConsoleIO {
}
/// @param string[]|string $messages
- fn do_write(
- &mut self,
- messages: PhpMixed,
- newline: bool,
- stderr: bool,
- verbosity: i64,
- raw: bool,
- ) {
+ fn do_write(&self, messages: PhpMixed, newline: bool, stderr: bool, verbosity: i64, raw: bool) {
let mut sf_verbosity = *self.verbosity_map.get(&verbosity).unwrap_or(&0);
if sf_verbosity > self.output.get_verbosity() {
return;
}
if raw {
- sf_verbosity |= OutputInterface::OUTPUT_RAW;
+ sf_verbosity |= output_interface::OUTPUT_RAW;
}
let messages = if let Some(start_time) = self.start_time {
@@ -137,9 +133,9 @@ impl ConsoleIO {
todo!("downcast self.output to ConsoleOutputInterface");
console_output
.get_error_output()
- .write(messages.clone(), newline, sf_verbosity);
+ .write3(messages.clone(), newline, sf_verbosity);
// PHP: implode($newline ? "\n" : '', (array) $messages)
- self.last_message_err = implode(
+ *self.last_message_err.borrow_mut() = implode(
if newline { "\n" } else { "" },
&Self::to_string_list(&messages),
);
@@ -147,8 +143,8 @@ impl ConsoleIO {
return;
}
- self.output.write(messages.clone(), newline, sf_verbosity);
- self.last_message = implode(
+ self.output.write3(messages.clone(), newline, sf_verbosity);
+ *self.last_message.borrow_mut() = implode(
if newline { "\n" } else { "" },
&Self::to_string_list(&messages),
);
@@ -156,7 +152,7 @@ impl ConsoleIO {
/// @param string[]|string $messages
fn do_overwrite(
- &mut self,
+ &self,
messages: PhpMixed,
newline: bool,
size: Option<i64>,
@@ -173,9 +169,9 @@ impl ConsoleIO {
let size = size.unwrap_or_else(|| {
// removing possible formatting of lastMessage with strip_tags
strlen(&strip_tags(if stderr {
- &self.last_message_err
+ &self.last_message_err.borrow()
} else {
- &self.last_message
+ &self.last_message.borrow()
}))
});
// ...let's fill its length with backspaces
@@ -230,9 +226,9 @@ impl ConsoleIO {
}
if stderr {
- self.last_message_err = messages_str;
+ *self.last_message_err.borrow_mut() = messages_str;
} else {
- self.last_message = messages_str;
+ *self.last_message.borrow_mut() = messages_str;
}
}
@@ -420,37 +416,31 @@ impl IOInterface for ConsoleIO {
self.output.is_decorated()
}
- fn write3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ fn write3(&self, message: &str, newline: bool, verbosity: i64) {
let message = Self::sanitize(message, true);
self.do_write(message, newline, false, verbosity, false);
}
- fn write_error3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ fn write_error3(&self, message: &str, newline: bool, verbosity: i64) {
let message = Self::sanitize(message, true);
self.do_write(message, newline, true, verbosity, false);
}
- fn write_raw3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ fn write_raw3(&self, message: &str, newline: bool, verbosity: i64) {
self.do_write(message, newline, false, verbosity, true);
}
- fn write_error_raw3(&mut self, message: &str, newline: bool, verbosity: i64) {
+ fn write_error_raw3(&self, message: &str, newline: bool, verbosity: i64) {
self.do_write(message, newline, true, verbosity, true);
}
- fn overwrite4(&mut self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) {
+ fn overwrite4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) {
self.do_overwrite(message, newline, size, false, verbosity);
}
- fn overwrite_error4(
- &mut self,
- message: &str,
- newline: bool,
- size: Option<i64>,
- verbosity: i64,
- ) {
+ fn overwrite_error4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) {
self.do_overwrite(message, newline, size, true, verbosity);
}
diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs
index e78b608..d9092b1 100644
--- a/crates/shirabe/src/io/io_interface.rs
+++ b/crates/shirabe/src/io/io_interface.rs
@@ -22,65 +22,65 @@ pub trait IOInterface: LoggerInterface + std::fmt::Debug {
fn is_decorated(&self) -> bool;
- fn write(&mut self, message: &str) {
+ fn write(&self, message: &str) {
self.write3(message, true, NORMAL)
}
- fn write2(&mut self, message: &str, newline: bool) {
+ fn write2(&self, message: &str, newline: bool) {
self.write3(message, newline, NORMAL)
}
- fn write_no_newline(&mut self, message: &str) {
+ fn write_no_newline(&self, message: &str) {
self.write3(message, false, NORMAL)
}
- fn write3(&mut self, message: &str, newline: bool, verbosity: i64);
+ fn write3(&self, message: &str, newline: bool, verbosity: i64);
- fn write_error(&mut self, message: &str) {
+ fn write_error(&self, message: &str) {
self.write_error3(message, true, NORMAL)
}
- fn write_error2(&mut self, message: &str, newline: bool) {
+ fn write_error2(&self, message: &str, newline: bool) {
self.write_error3(message, newline, NORMAL)
}
- fn write_error_no_newline(&mut self, message: &str) {
+ fn write_error_no_newline(&self, message: &str) {
self.write_error3(message, false, NORMAL)
}
- fn write_error3(&mut self, message: &str, newline: bool, verbosity: i64);
+ fn write_error3(&self, message: &str, newline: bool, verbosity: i64);
- fn write_raw(&mut self, message: &str) {
+ fn write_raw(&self, message: &str) {
self.write_raw3(message, true, NORMAL)
}
- fn write_raw2(&mut self, message: &str, newline: bool) {
+ fn write_raw2(&self, message: &str, newline: bool) {
self.write_raw3(message, newline, NORMAL)
}
- fn write_raw3(&mut self, message: &str, newline: bool, verbosity: i64);
+ fn write_raw3(&self, message: &str, newline: bool, verbosity: i64);
- fn write_error_raw(&mut self, message: &str) {
+ fn write_error_raw(&self, message: &str) {
self.write_error_raw3(message, true, NORMAL)
}
- fn write_error_raw2(&mut self, message: &str, newline: bool) {
+ fn write_error_raw2(&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 write_error_raw3(&self, message: &str, newline: bool, verbosity: i64);
- fn overwrite(&mut self, message: &str) {
+ fn overwrite(&self, message: &str) {
self.overwrite4(message, true, None, NORMAL)
}
- fn overwrite2(&mut self, message: &str, newline: bool) {
+ fn overwrite2(&self, message: &str, newline: bool) {
self.overwrite4(message, newline, None, NORMAL)
}
- fn overwrite3(&mut self, message: &str, newline: bool, size: Option<i64>) {
+ fn overwrite3(&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 overwrite4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64);
- fn overwrite_error(&mut self, message: &str) {
+ fn overwrite_error(&self, message: &str) {
self.overwrite_error4(message, true, None, NORMAL)
}
- fn overwrite_error2(&mut self, message: &str, newline: bool) {
+ fn overwrite_error2(&self, message: &str, newline: bool) {
self.overwrite_error4(message, newline, None, NORMAL)
}
- fn overwrite_error3(&mut self, message: &str, newline: bool, size: Option<i64>) {
+ fn overwrite_error3(&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 overwrite_error4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64);
fn ask(&mut self, question: String, default: PhpMixed) -> PhpMixed;
diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs
index 4ec5be0..2536ad0 100644
--- a/crates/shirabe/src/io/null_io.rs
+++ b/crates/shirabe/src/io/null_io.rs
@@ -39,18 +39,18 @@ impl IOInterface for NullIO {
false
}
- fn write3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn write_error3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_error3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn write_raw3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_raw3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn write_error_raw3(&mut self, _message: &str, _newline: bool, _verbosity: i64) {}
+ fn write_error_raw3(&self, _message: &str, _newline: bool, _verbosity: i64) {}
- fn overwrite4(&mut self, _message: &str, _newline: bool, _size: Option<i64>, _verbosity: i64) {}
+ fn overwrite4(&self, _message: &str, _newline: bool, _size: Option<i64>, _verbosity: i64) {}
fn overwrite_error4(
- &mut self,
+ &self,
_message: &str,
_newline: bool,
_size: Option<i64>,
diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs
index d08e207..52c3c71 100644
--- a/crates/shirabe/src/json/json_file.rs
+++ b/crates/shirabe/src/json/json_file.rs
@@ -3,7 +3,8 @@
use crate::io::io_interface;
use crate::util::silencer::Silencer;
use anyhow::Result;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::json_schema::validator::Validator;
use shirabe_external_packages::seld::json_lint::json_parser::JsonParser;
use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException;
@@ -28,7 +29,7 @@ pub struct JsonFile {
/// @var string
path: String,
/// @var ?HttpDownloader
- http_downloader: Option<HttpDownloader>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
/// @var ?IOInterface
io: Option<Box<dyn IOInterface>>,
/// @var string
@@ -67,10 +68,10 @@ impl JsonFile {
/// @throws \InvalidArgumentException
pub fn new(
path: String,
- http_downloader: Option<HttpDownloader>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
io: Option<Box<dyn IOInterface>>,
) -> Result<Self> {
- if http_downloader.is_none() && Preg::is_match(r"{^https?://}i", &path) {
+ if http_downloader.is_none() && Preg::is_match(r"{^https?://}i", &path).unwrap_or(false) {
return Err(InvalidArgumentException {
message: "http urls require a HttpDownloader instance to be passed".to_string(),
code: 0,
@@ -103,7 +104,11 @@ impl JsonFile {
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
let json: Option<String> = match (|| -> Result<Option<String>> {
if let Some(http_downloader) = &self.http_downloader {
- Ok(Some(http_downloader.get(&self.path)?.get_body()))
+ Ok(http_downloader
+ .borrow_mut()
+ .get(&self.path, indexmap::IndexMap::new())?
+ .get_body()
+ .map(|s| s.to_string()))
} else {
if !Filesystem::is_readable(&self.path) {
return Err(RuntimeException {
@@ -120,8 +125,8 @@ impl JsonFile {
realpath_info = format!(" ({})", realpath);
}
}
- io.write_error(
- PhpMixed::String(format!("Reading {}{}", self.path, realpath_info)),
+ io.write_error3(
+ &format!("Reading {}{}", self.path, realpath_info),
true,
io_interface::NORMAL,
);
@@ -160,18 +165,22 @@ impl JsonFile {
Self::parse_json(Some(&json), Some(&self.path))
}
+ pub fn write(&self, hash: PhpMixed) -> Result<()> {
+ self.write2(
+ hash,
+ JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
+ )
+ }
+
/// Writes json file.
///
/// @param mixed[] $hash writes hash into json file
/// @param int $options json_encode options
/// @throws \UnexpectedValueException|\Exception
/// @return void
- pub fn write(&self, hash: PhpMixed, options: i64) -> Result<()> {
+ pub fn write2(&self, hash: PhpMixed, options: i64) -> Result<()> {
if self.path == "php://memory" {
- file_put_contents(
- &self.path,
- Self::encode(&hash, options, &self.indent).as_bytes(),
- );
+ file_put_contents(&self.path, Self::encode(&hash, options).as_bytes());
return Ok(());
}
@@ -207,7 +216,7 @@ impl JsonFile {
&self.path,
&format!(
"{}{}",
- Self::encode(&hash, options, &self.indent),
+ Self::encode(&hash, options),
if options & JSON_PRETTY_PRINT != 0 {
"\n"
} else {
@@ -362,29 +371,13 @@ impl JsonFile {
let mut validator = Validator::new();
// convert assoc arrays to objects
let data_converted = json_decode(&json_encode_ex(data, 0).unwrap_or_default(), false)?;
- validator.validate(&data_converted, &schema_data);
+ validator.check(&data_converted, &schema_data)?;
if !validator.is_valid() {
- let mut errors: Vec<String> = vec![];
- for error in validator.get_errors() {
- let property = error
- .get("property")
- .and_then(|v| v.as_string())
- .unwrap_or("");
- let message = error
- .get("message")
- .and_then(|v| v.as_string())
- .unwrap_or("");
- errors.push(format!(
- "{}{}",
- if !property.is_empty() {
- format!("{} : ", property)
- } else {
- String::new()
- },
- message,
- ));
- }
+ // TODO(phase-b): Validator::get_errors currently returns Vec<String>; original PHP
+ // exposes [{property, message}, ...]. Until shim is enriched, surface raw error
+ // strings without prop/message splitting.
+ let errors: Vec<String> = validator.get_errors();
return Err(JsonValidationException::new(
format!("\"{}\" does not match the expected JSON schema", source),
errors,
@@ -426,7 +419,7 @@ impl JsonFile {
move |m| -> String {
str_repeat(
&indent,
- (strlen(m.get(0).map(|s| s.as_str()).unwrap_or("")) / 4) as usize,
+ (strlen(m.get(&0).map(|s| s.as_str()).unwrap_or("")) / 4) as usize,
)
},
&json,
@@ -545,8 +538,11 @@ impl JsonFile {
}
pub fn detect_indenting(json: Option<&str>) -> String {
- if let Some(m) = Preg::is_match_strict_groups(r##"#^([ \t]+)"#m"##, json.unwrap_or("")) {
- return m.get(1).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r##"#^([ \t]+)"#m"##, json.unwrap_or(""), Some(&mut m))
+ .unwrap_or(false)
+ {
+ return m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
}
Self::INDENT_DEFAULT.to_string()
diff --git a/crates/shirabe/src/json/json_manipulator.rs b/crates/shirabe/src/json/json_manipulator.rs
index b9c6a5c..c776e13 100644
--- a/crates/shirabe/src/json/json_manipulator.rs
+++ b/crates/shirabe/src/json/json_manipulator.rs
@@ -36,7 +36,7 @@ impl JsonManipulator {
if contents == "" {
contents = "{}".to_string();
}
- if !Preg::is_match("#^\\{(.*)\\}$#s", &contents, None).unwrap_or(false) {
+ if !Preg::is_match3("#^\\{(.*)\\}$#s", &contents, None).unwrap_or(false) {
return Err(InvalidArgumentException {
message: "The json file must be an object ({})".to_string(),
code: 0,
@@ -72,7 +72,7 @@ impl JsonManipulator {
constraint: &str,
sort_packages: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no link of that type yet
if decoded.as_array().and_then(|a| a.get(r#type)).is_none() {
@@ -137,7 +137,7 @@ impl JsonManipulator {
);
} else {
let mut groups: Vec<String> = vec![];
- if Preg::is_match_strict_groups(
+ if Preg::is_match_strict_groups3(
"#^\\s*\\{\\s*\\S+.*?(\\s*\\}\\s*)$#s",
&links,
Some(&mut groups),
@@ -178,7 +178,7 @@ impl JsonManipulator {
}
if sort_packages {
- let mut requirements = json_decode(&links, true);
+ let mut requirements = json_decode(&links, true)?;
Self::sort_packages(&mut requirements);
links = self.format(&requirements, 0, false)?;
}
@@ -262,7 +262,7 @@ impl JsonManipulator {
}
fn do_convert_repositories_from_assoc_to_list(&mut self) -> anyhow::Result<bool> {
- let decoded = json_decode(&self.contents, false);
+ let decoded = json_decode(&self.contents, false)?;
let repositories_value = decoded.as_object().and_then(|o| o.get("repositories"));
let is_std_class = repositories_value
@@ -322,7 +322,7 @@ impl JsonManipulator {
}
pub fn set_repository_url(&mut self, name: &str, url: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let mut repository_index: Option<PhpMixed> = None;
let repos = decoded
@@ -378,7 +378,7 @@ impl JsonManipulator {
{
// invalid match due to un-regexable content, abort
let raw_repo = matches.get("repository").cloned().unwrap_or_default();
- if json_decode(&raw_repo, false).as_bool() == Some(false) {
+ if json_decode(&raw_repo, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -398,8 +398,7 @@ impl JsonManipulator {
format!(
"{}{}{}",
repository_matches.get("start").cloned().unwrap_or_default(),
- JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0)
- .unwrap_or_default(),
+ JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0),
repository_matches.get("end").cloned().unwrap_or_default()
)
}
@@ -431,7 +430,7 @@ impl JsonManipulator {
}
let mut index_to_insert: Option<i64> = None;
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let repos = decoded
.as_array()
@@ -499,7 +498,7 @@ impl JsonManipulator {
}
fn do_remove_repository(&mut self, name: &str) -> anyhow::Result<bool> {
- let decoded = json_decode(&self.contents, false);
+ let decoded = json_decode(&self.contents, false)?;
let repositories_value = decoded.as_object().and_then(|o| o.get("repositories"));
let is_assoc = repositories_value
.map(|v| v.as_any().is::<StdClass>())
@@ -623,7 +622,7 @@ impl JsonManipulator {
value: PhpMixed,
append: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let mut name_owned = name.to_string();
let mut sub_name: Option<String> = None;
@@ -690,8 +689,8 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).is_null()
- || json_decode(&children, false).as_bool() == Some(false)
+ if json_decode(&children, false)?.is_null()
+ || json_decode(&children, false)?.as_bool() == Some(false)
{
return Ok(false);
}
@@ -715,7 +714,8 @@ impl JsonManipulator {
Box::new(move |matches: &IndexMap<String, String>| -> String {
let mut value_local = value_capture.clone();
if sub_name_capture.is_some() && matches.get("content").is_some() {
- let mut cur_val = json_decode(matches.get("content").unwrap(), true);
+ let mut cur_val = json_decode(matches.get("content").unwrap(), true)
+ .unwrap_or(PhpMixed::Null);
if !is_array(&cur_val) {
cur_val = PhpMixed::Array(IndexMap::new());
}
@@ -846,7 +846,7 @@ impl JsonManipulator {
}
pub fn remove_sub_node(&mut self, main_node: &str, name: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no node or empty node
let main_node_value = decoded.as_array().and_then(|a| a.get(main_node));
@@ -882,8 +882,8 @@ impl JsonManipulator {
let children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, true).is_null()
- || json_decode(&children, true).as_bool() == Some(false)
+ if json_decode(&children, true)?.is_null()
+ || json_decode(&children, true)?.as_bool() == Some(false)
{
return Ok(false);
}
@@ -926,11 +926,12 @@ impl JsonManipulator {
// try and find a match for the subkey
let key_regex = str_replace("/", "\\\\?/", &preg_quote(&name_owned, None));
let mut children_clean: Option<String> = None;
- if Preg::is_match(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None).unwrap_or(false)
+ if Preg::is_match3(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None)
+ .unwrap_or(false)
{
// find best match for the value of "name"
let mut all_matches: Vec<Vec<String>> = vec![];
- if Preg::is_match_all(
+ if Preg::is_match_all3(
&format!(
"{{{}\"{}\"\\s*:\\s*(?:(?&json))}}x",
Self::DEFINES,
@@ -1009,7 +1010,7 @@ impl JsonManipulator {
// we have a subname, so we restore the rest of $name
if let Some(sub) = sub_name {
- let mut cur_val = json_decode(&children, true);
+ let mut cur_val = json_decode(&children, true)?;
if let Some(arr) = cur_val.as_array_mut() {
if let Some(inner) = arr.get_mut(&name_owned).and_then(|v| v.as_array_mut())
{
@@ -1052,7 +1053,8 @@ impl JsonManipulator {
let mut children_clean = children_clean_capture.clone();
if let Some(ref sub) = sub_name_capture {
let mut cur_val =
- json_decode(matches.get("content").unwrap_or(&String::new()), true);
+ json_decode(matches.get("content").unwrap_or(&String::new()), true)
+ .unwrap_or(PhpMixed::Null);
if let Some(arr) = cur_val.as_array_mut() {
if let Some(inner) =
arr.get_mut(&name_capture).and_then(|v| v.as_array_mut())
@@ -1093,7 +1095,7 @@ impl JsonManipulator {
value: PhpMixed,
append: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no main node yet
if decoded.as_array().and_then(|a| a.get(main_node)).is_none() {
@@ -1130,7 +1132,7 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).as_bool() == Some(false) {
+ if json_decode(&children, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1246,7 +1248,7 @@ impl JsonManipulator {
return self.add_list_item(main_node, value, false);
}
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no main node yet
if decoded.as_array().and_then(|a| a.get(main_node)).is_none() {
@@ -1293,7 +1295,7 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).as_bool() == Some(false) {
+ if json_decode(&children, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1348,7 +1350,7 @@ impl JsonManipulator {
return Ok(true);
}
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no node or empty node
let main_node_value = decoded.as_array().and_then(|a| a.get(main_node));
@@ -1384,7 +1386,7 @@ impl JsonManipulator {
let children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, true).as_bool() == Some(false) {
+ if json_decode(&children, true)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1447,7 +1449,7 @@ impl JsonManipulator {
}
pub fn add_main_key(&mut self, key: &str, content: PhpMixed) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let content = self.format(&content, 0, false)?;
// key exists already
@@ -1465,7 +1467,7 @@ impl JsonManipulator {
{
// invalid match due to un-regexable content, abort
let key_match = matches.get("key").cloned().unwrap_or_default();
- if json_decode(&format!("{{{}}}", key_match), false).is_null() {
+ if json_decode(&format!("{{{}}}", key_match), false)?.is_null() {
return Ok(false);
}
@@ -1482,7 +1484,7 @@ impl JsonManipulator {
// append at the end of the file and keep whitespace
let mut tail_match: Vec<String> = vec![];
- if Preg::is_match("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match))
+ if Preg::is_match3("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match))
.unwrap_or(false)
{
self.contents = Preg::replace(
@@ -1524,7 +1526,7 @@ impl JsonManipulator {
}
pub fn remove_main_key(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1544,21 +1546,21 @@ impl JsonManipulator {
if Preg::is_match_named(&regex, &self.contents, &mut matches).unwrap_or(false) {
// invalid match due to un-regexable content, abort
let removal = matches.get("removal").cloned().unwrap_or_default();
- if json_decode(&format!("{{{}}}", removal), false).is_null() {
+ if json_decode(&format!("{{{}}}", removal), false)?.is_null() {
return Ok(false);
}
// check that we are not leaving a dangling comma on the previous line if the last line was removed
let mut start = matches.get("start").cloned().unwrap_or_default();
let end = matches.get("end").cloned().unwrap_or_default();
- if Preg::is_match_strict_groups("#,\\s*$#", &start, None).unwrap_or(false)
- && Preg::is_match("#^\\}$#", &end, None).unwrap_or(false)
+ if Preg::is_match_strict_groups3("#,\\s*$#", &start, None).unwrap_or(false)
+ && Preg::is_match3("#^\\}$#", &end, None).unwrap_or(false)
{
start = rtrim(&Preg::replace("#,(\\s*)$#", "$1", &start), &self.indent);
}
self.contents = format!("{}{}", start, end);
- if Preg::is_match("#^\\{\\s*\\}\\s*$#", &self.contents, None).unwrap_or(false) {
+ if Preg::is_match3("#^\\{\\s*\\}\\s*$#", &self.contents, None).unwrap_or(false) {
self.contents = "{\n}".to_string();
}
@@ -1569,7 +1571,7 @@ impl JsonManipulator {
}
pub fn change_empty_main_key_from_assoc_to_list(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1588,7 +1590,7 @@ impl JsonManipulator {
if Preg::is_match_named(&regex, &self.contents, &mut matches).unwrap_or(false) {
// invalid match due to un-regexable content, abort
let removal = matches.get("removal").cloned().unwrap_or_default();
- if json_decode(&removal, false).as_bool() == Some(false) {
+ if json_decode(&removal, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1606,7 +1608,7 @@ impl JsonManipulator {
}
pub fn remove_main_key_if_empty(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1643,7 +1645,7 @@ impl JsonManipulator {
format!(
"{{{}{}}}",
self.newline,
- str_repeat(&self.indent, (depth + 1) as i64)
+ str_repeat(&self.indent, (depth + 1) as usize)
)
} else {
"[]".to_string()
@@ -1665,7 +1667,7 @@ impl JsonManipulator {
for (key, val) in arr {
elems.push(format!(
"{}{}: {}",
- str_repeat(&self.indent, (depth + 2) as i64),
+ str_repeat(&self.indent, (depth + 2) as usize),
JsonFile::encode(&PhpMixed::String(key.clone()), 0)?,
self.format(val, depth + 1, false)?
));
@@ -1677,7 +1679,7 @@ impl JsonManipulator {
out,
implode(&format!(",{}", self.newline), &elems),
self.newline,
- str_repeat(&self.indent, (depth + 1) as i64)
+ str_repeat(&self.indent, (depth + 1) as usize)
));
}
@@ -1714,7 +1716,7 @@ impl ManipulatorFormatter {
format!(
"{{{}{}}}",
self.newline,
- str_repeat(&self.indent, depth + 1)
+ str_repeat(&self.indent, (depth + 1) as usize)
)
} else {
"[]".to_string()
@@ -1736,7 +1738,7 @@ impl ManipulatorFormatter {
for (key, val) in arr {
elems.push(format!(
"{}{}: {}",
- str_repeat(&self.indent, depth + 2),
+ str_repeat(&self.indent, (depth + 2) as usize),
JsonFile::encode(&PhpMixed::String(key.clone()), 0)?,
self.format(val, depth + 1, false)?
));
@@ -1748,7 +1750,7 @@ impl ManipulatorFormatter {
out,
implode(&format!(",{}", self.newline), &elems),
self.newline,
- str_repeat(&self.indent, depth + 1)
+ str_repeat(&self.indent, (depth + 1) as usize)
));
}
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs
index e115362..3f4b1d8 100644
--- a/crates/shirabe/src/package/alias_package.rs
+++ b/crates/shirabe/src/package/alias_package.rs
@@ -53,13 +53,16 @@ impl AliasPackage {
/// @param string $version The version the alias must report
/// @param string $prettyVersion The alias's non-normalized version
pub fn new(alias_of: Box<dyn BasePackage>, version: String, pretty_version: String) -> Self {
- let inner = BasePackage::new(alias_of.get_name().to_string());
+ let alias_name = alias_of.get_name().to_string();
- let stability = VersionParser::parse_stability(&version);
+ let stability = VersionParser::parse_stability(&version).to_string();
let dev = stability == "dev";
let mut this = Self {
- inner,
+ id: -1,
+ name: alias_name.to_lowercase(),
+ pretty_name: alias_name,
+ repository: None,
version,
pretty_version,
dev,
@@ -429,8 +432,7 @@ 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.alias_of
- .get_full_pretty_version(truncate, display_mode)
+ BasePackage::get_full_pretty_version(self.alias_of.as_ref(), truncate, display_mode)
.unwrap_or_default()
}
diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs
index 73b0d84..374e16c 100644
--- a/crates/shirabe/src/package/archiver/archive_manager.rs
+++ b/crates/shirabe/src/package/archiver/archive_manager.rs
@@ -184,7 +184,7 @@ impl ArchiveManager {
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()) {
+ if let Some(name) = archive.get("name").and_then(|v| v.as_string()) {
if !name.is_empty() {
package.set_archive_name(name.to_string());
}
@@ -192,8 +192,8 @@ impl ArchiveManager {
if let Some(exclude) = archive.get("exclude") {
if let Some(excludes) = exclude.as_array() {
let excludes: Vec<String> = excludes
- .iter()
- .filter_map(|v| v.as_str().map(|s| s.to_string()))
+ .values()
+ .filter_map(|v| v.as_string().map(|s| s.to_string()))
.collect();
if !excludes.is_empty() {
package.set_archive_excludes(excludes);
diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs
index fb906d1..052a480 100644
--- a/crates/shirabe/src/package/base_package.rs
+++ b/crates/shirabe/src/package/base_package.rs
@@ -168,7 +168,11 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
}
}
-pub fn package_name_to_regexp(allow_pattern: &str, wrap: &str) -> String {
+pub fn package_name_to_regexp(allow_pattern: &str) -> String {
+ package_name_to_regexp2(allow_pattern, "{^%s$}i")
+}
+
+pub fn package_name_to_regexp2(allow_pattern: &str, wrap: &str) -> String {
let cleaned = preg_quote(allow_pattern, None).replace("\\*", ".*");
wrap.replace("%s", &cleaned)
}
@@ -176,7 +180,7 @@ pub fn package_name_to_regexp(allow_pattern: &str, wrap: &str) -> String {
pub fn package_names_to_regexp(package_names: &[String], wrap: &str) -> String {
let patterns: Vec<String> = package_names
.iter()
- .map(|name| package_name_to_regexp(name, "%s"))
+ .map(|name| package_name_to_regexp2(name, "%s"))
.collect();
wrap.replace("%s", &patterns.join("|"))
}
diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs
index d81fd6c..0b57070 100644
--- a/crates/shirabe/src/package/dumper/array_dumper.rs
+++ b/crates/shirabe/src/package/dumper/array_dumper.rs
@@ -260,8 +260,7 @@ impl ArrayDumper {
);
}
- if let Some(complete_pkg) = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>()
- {
+ if let Some(complete_pkg) = package.as_any().downcast_ref::<CompletePackage>() {
if let Some(archive_name) = complete_pkg.get_archive_name() {
let entry = data
.entry("archive".to_string())
@@ -422,7 +421,7 @@ impl ArrayDumper {
}
}
- if let Some(root_pkg) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>() {
+ if let Some(root_pkg) = package.as_any().downcast_ref::<RootPackage>() {
let minimum_stability = root_pkg.get_minimum_stability();
if !minimum_stability.is_empty() {
data.insert(
diff --git a/crates/shirabe/src/package/link.rs b/crates/shirabe/src/package/link.rs
index e41d431..2732075 100644
--- a/crates/shirabe/src/package/link.rs
+++ b/crates/shirabe/src/package/link.rs
@@ -114,7 +114,10 @@ impl Link {
pub fn to_string(&self) -> String {
format!(
"{} {} {} ({})",
- self.source, self.description, self.target, self.constraint
+ self.source,
+ self.description,
+ self.target,
+ self.constraint.__to_string(),
)
}
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 275d718..3ece5c7 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -410,7 +410,7 @@ impl ArrayLoader {
if let Some(time_value) = config.get("time") {
if !shirabe_php_shim::empty(time_value) {
let time_str = time_value.as_string().unwrap_or("");
- let time = if Preg::is_match(r"/^\d++$/D", time_str) {
+ let time = if Preg::is_match(r"/^\d++$/D", time_str).unwrap_or(false) {
format!("@{}", time_str)
} else {
time_str.to_string()
diff --git a/crates/shirabe/src/package/loader/loader_interface.rs b/crates/shirabe/src/package/loader/loader_interface.rs
index f941f9a..2b93bc5 100644
--- a/crates/shirabe/src/package/loader/loader_interface.rs
+++ b/crates/shirabe/src/package/loader/loader_interface.rs
@@ -4,7 +4,7 @@ use crate::package::base_package::BasePackage;
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
-pub trait LoaderInterface {
+pub trait LoaderInterface: std::fmt::Debug {
fn load(
&self,
config: IndexMap<String, PhpMixed>,
diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs
index 59c9cf5..d2e2a7a 100644
--- a/crates/shirabe/src/package/loader/root_package_loader.rs
+++ b/crates/shirabe/src/package/loader/root_package_loader.rs
@@ -1,7 +1,7 @@
//! ref: composer/src/Composer/Package/Loader/RootPackageLoader.php
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
LogicException, RuntimeException, UnexpectedValueException, strtolower, ucfirst,
};
@@ -10,6 +10,7 @@ use crate::config::Config;
use crate::io::io_interface::IOInterface;
use crate::package::base_package::{BasePackage, STABILITIES, SUPPORTED_LINK_TYPES};
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::loader::validating_array_loader::ValidatingArrayLoader;
use crate::package::package_interface::PackageInterface;
use crate::package::root_alias_package::RootAliasPackage;
@@ -25,7 +26,7 @@ use crate::util::process_executor::ProcessExecutor;
pub struct RootPackageLoader {
inner: ArrayLoader,
manager: RepositoryManager,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
version_guesser: VersionGuesser,
io: Option<Box<dyn IOInterface>>,
}
@@ -33,7 +34,7 @@ pub struct RootPackageLoader {
impl RootPackageLoader {
pub fn new(
manager: RepositoryManager,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
parser: Option<VersionParser>,
version_guesser: Option<VersionGuesser>,
io: Option<Box<dyn IOInterface>>,
@@ -42,7 +43,12 @@ impl RootPackageLoader {
let version_guesser = version_guesser.unwrap_or_else(|| {
let mut process_executor = ProcessExecutor::new(io.as_deref());
process_executor.enable_async();
- VersionGuesser::new(&config, process_executor, inner.version_parser.clone())
+ VersionGuesser::new(
+ std::rc::Rc::clone(&config),
+ std::rc::Rc::new(std::cell::RefCell::new(process_executor)),
+ inner.version_parser.clone(),
+ io.as_ref().map(|i| i.clone_box()),
+ )
});
Self {
inner,
@@ -284,11 +290,15 @@ impl RootPackageLoader {
);
}
- let repos = RepositoryFactory::default_repos(None, &self.config, &mut self.manager)?;
+ let repos = RepositoryFactory::default_repos(
+ None,
+ Some(std::rc::Rc::clone(&self.config)),
+ Some(&mut self.manager),
+ )?;
for repo in repos {
self.manager.add_repository(repo);
}
- real_package.set_repositories(self.config.get_repositories());
+ real_package.set_repositories(self.config.borrow().get_repositories());
Ok(package)
}
@@ -299,27 +309,31 @@ impl RootPackageLoader {
mut aliases: Vec<IndexMap<String, String>>,
) -> Vec<IndexMap<String, String>> {
for (req_name, req_version) in requires {
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"(?:^|\| *|, *)([^,\s#|]+)(?:#[^ ]+)? +as +([^,\s|]+)(?:$| *\|| *,)",
req_version,
+ Some(&mut m),
)
- .unwrap_or(None)
+ .unwrap_or(false)
{
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
let mut alias = IndexMap::new();
alias.insert("package".to_string(), strtolower(req_name));
alias.insert(
"version".to_string(),
self.inner
.version_parser
- .normalize(&m[1], req_version)
+ .normalize(&m1, Some(req_version))
.unwrap_or_default(),
);
- alias.insert("alias".to_string(), m[2].clone());
+ alias.insert("alias".to_string(), m2.clone());
alias.insert(
"alias_normalized".to_string(),
self.inner
.version_parser
- .normalize(&m[2], req_version)
+ .normalize(&m2, Some(req_version))
.unwrap_or_default(),
);
aliases.push(alias);
@@ -370,9 +384,13 @@ impl RootPackageLoader {
let mut matched = false;
for constraint in &constraints {
- if let Some(Some(m)) = Preg::is_match_strict_groups(&pattern, constraint).ok() {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(&pattern, constraint, Some(&mut m))
+ .unwrap_or(false)
+ {
let name = strtolower(req_name);
- let stability = stabilities[VersionParser::normalize_stability(&m[1])];
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let stability = stabilities[VersionParser::normalize_stability(&m1)];
if stability_flags.get(&name).copied().unwrap_or(i64::MAX) > stability {
continue;
@@ -415,12 +433,16 @@ impl RootPackageLoader {
for (req_name, req_version) in requires {
let req_version =
Preg::replace(r"^([^,\s@]+) as .+$", "$1", req_version).unwrap_or_default();
- if let Some(Some(m)) =
- Preg::is_match_strict_groups(r"^[^,\s@]+?#([a-f0-9]+)$", &req_version).ok()
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"^[^,\s@]+?#([a-f0-9]+)$", &req_version, Some(&mut m))
+ .unwrap_or(false)
{
if VersionParser::parse_stability(&req_version) == "dev" {
let name = strtolower(req_name);
- references.insert(name, m[1].clone());
+ references.insert(
+ name,
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ );
}
}
}
diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs
index 397af08..8b3fea5 100644
--- a/crates/shirabe/src/package/locker.rs
+++ b/crates/shirabe/src/package/locker.rs
@@ -3,7 +3,7 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException;
use shirabe_php_shim::{
DATE_RFC3339, LogicException, PhpMixed, RuntimeException, array_intersect, array_keys,
@@ -21,6 +21,7 @@ use crate::package::complete_alias_package::CompleteAliasPackage;
use crate::package::dumper::array_dumper::ArrayDumper;
use crate::package::link::Link;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::package_interface::PackageInterface;
use crate::package::root_package_interface::RootPackageInterface;
use crate::package::version::version_parser::VersionParser;
@@ -48,7 +49,7 @@ pub struct Locker {
/// @var ArrayDumper
dumper: ArrayDumper,
/// @var ProcessExecutor
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var mixed[]|null
lock_data_cache: Option<IndexMap<String, PhpMixed>>,
/// @var bool
@@ -62,9 +63,8 @@ impl Locker {
lock_file: JsonFile,
installation_manager: InstallationManager,
composer_file_contents: &str,
- process: Option<ProcessExecutor>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(Some(io), None));
Self {
lock_file,
installation_manager,
@@ -606,10 +606,9 @@ impl Locker {
};
if !is_locked || Some(&lock) != current_data.as_ref() {
if write {
- self.lock_file.write(
- PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
- None,
- )?;
+ self.lock_file.write(PhpMixed::Array(
+ lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
+ ))?;
self.lock_data_cache = None;
self.virtual_file_written = false;
} else {
@@ -639,7 +638,7 @@ impl Locker {
/// Updates the lock file's hash in-place from a given composer.json's JsonFile
pub fn update_hash<F>(
&mut self,
- composer_json: JsonFile,
+ composer_json: &JsonFile,
data_processor: Option<F>,
) -> Result<()>
where
@@ -670,15 +669,12 @@ impl Locker {
lock_data = processor(lock_data);
}
- self.lock_file.write(
- PhpMixed::Array(
- self.fixup_json_data_type(lock_data)
- .into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ),
- None,
- )?;
+ self.lock_file.write(PhpMixed::Array(
+ self.fixup_json_data_type(lock_data)
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
+ ))?;
self.lock_data_cache = None;
self.virtual_file_written = false;
if let Some(mtime) = lock_mtime {
@@ -838,7 +834,7 @@ impl Locker {
args.extend(no_show_signature_flags);
let command = GitUtil::build_rev_list_command(&self.process, args);
let mut output = PhpMixed::Null;
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute(
PhpMixed::String(command),
Some(&mut output),
path.as_deref(),
@@ -850,7 +846,7 @@ impl Locker {
),
None,
);
- if Preg::is_match(r"{^\s*\d+\s*$}", &output_str) {
+ if Preg::is_match(r"{^\s*\d+\s*$}", &output_str).unwrap_or(false) {
// TODO(phase-b): new \DateTime('@'.trim($output), new \DateTimeZone('UTC'))
let ts = trim(&output_str, None).parse::<i64>().unwrap_or(0);
datetime = chrono::DateTime::from_timestamp(ts, 0);
@@ -859,7 +855,7 @@ impl Locker {
}
"hg" => {
let mut output = PhpMixed::Null;
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute(
PhpMixed::List(vec![
Box::new(PhpMixed::String("hg".to_string())),
Box::new(PhpMixed::String("log".to_string())),
@@ -871,12 +867,16 @@ impl Locker {
Some(&mut output),
path.as_deref(),
)? {
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^\s*(\d+)\s*}",
output.as_string().unwrap_or(""),
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
let ts = m
- .get(1)
+ .get(&CaptureKey::ByIndex(1))
.cloned()
.unwrap_or_default()
.parse::<i64>()
diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs
index c6ccce8..beadc5c 100644
--- a/crates/shirabe/src/package/package_interface.rs
+++ b/crates/shirabe/src/package/package_interface.rs
@@ -301,6 +301,12 @@ pub trait PackageInterface: std::fmt::Display + std::fmt::Debug {
) -> Option<&dyn crate::package::complete_package_interface::CompletePackageInterface> {
None
}
+
+ fn as_root_package_interface(
+ &self,
+ ) -> Option<&dyn crate::package::root_package_interface::RootPackageInterface> {
+ None
+ }
}
impl dyn PackageInterface {
diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs
index 8963fd6..f7c2377 100644
--- a/crates/shirabe/src/package/version/version_bumper.rs
+++ b/crates/shirabe/src/package/version/version_bumper.rs
@@ -7,7 +7,7 @@ use crate::package::version::version_parser::VersionParser;
use crate::util::platform::Platform;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
use shirabe_semver::intervals::Intervals;
@@ -49,9 +49,9 @@ impl VersionBumper {
return Ok(pretty_constraint);
}
- let major = Preg::replace(r"{^([1-9][0-9]*|0\.\d+).*}", "$1", version.clone())?;
+ let major = Preg::replace(r"{^([1-9][0-9]*|0\.\d+).*}", "$1", &version)?;
let version_without_suffix =
- Preg::replace(r"{(?:\.(?:0|9999999))+(-dev)?$}", "", version.clone())?;
+ Preg::replace(r"{(?:\.(?:0|9999999))+(-dev)?$}", "", &version)?;
let new_pretty_constraint = format!("^{}", version_without_suffix);
if !Preg::is_match(r"{^\^\d+(\.\d+)*$}", &new_pretty_constraint)? {
@@ -73,13 +73,16 @@ impl VersionBumper {
major = major
);
- let mut matches: IndexMap<String, Vec<(String, i64)>> = IndexMap::new();
- if Preg::is_match_all_with_offsets(&pattern, &pretty_constraint, &mut matches)? {
+ let mut matches: IndexMap<CaptureKey, Vec<(String, usize)>> = IndexMap::new();
+ if Preg::is_match_all_with_offsets3(&pattern, &pretty_constraint, Some(&mut matches))? {
let mut modified = pretty_constraint.clone();
- let constraint_matches = matches.get("constraint").cloned().unwrap_or_default();
+ let constraint_matches = matches
+ .get(&CaptureKey::ByName("constraint".to_string()))
+ .cloned()
+ .unwrap_or_default();
for match_ in constraint_matches.iter().rev() {
let match_str = &match_.0;
- let match_offset = match_.1;
+ let match_offset = match_.1 as i64;
let suffix = if match_str.matches('.').count() == 2
&& version_without_suffix.matches('.').count() == 1
{
diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs
index c4e25e0..b366b20 100644
--- a/crates/shirabe/src/package/version/version_guesser.rs
+++ b/crates/shirabe/src/package/version/version_guesser.rs
@@ -2,7 +2,7 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::component::process::process::Process;
use shirabe_php_shim::{
PHP_INT_MAX, PhpMixed, RuntimeException, array_keys, array_map, array_merge, empty,
@@ -28,10 +28,10 @@ use crate::util::svn::Svn as SvnUtil;
#[derive(Debug)]
pub struct VersionGuesser {
/// @var Config
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var ProcessExecutor
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var SemverVersionParser
version_parser: SemverVersionParser,
@@ -52,8 +52,8 @@ pub struct VersionData {
impl VersionGuesser {
pub fn new(
- config: Config,
- process: ProcessExecutor,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
version_parser: SemverVersionParser,
io: Option<Box<dyn IOInterface>>,
) -> Self {
@@ -130,6 +130,7 @@ impl VersionGuesser {
if "-dev" == substr(version_data.version.as_deref().unwrap_or(""), -4, None)
&& Preg::is_match(r"{\.9{7}}", version_data.version.as_deref().unwrap_or(""))
+ .unwrap_or(false)
{
version_data.pretty_version = Some(Preg::replace(
r"{(\.9{7})+}",
@@ -154,6 +155,7 @@ impl VersionGuesser {
r"{\.9{7}}",
version_data.feature_version.as_deref().unwrap_or(""),
)
+ .unwrap_or(false)
{
version_data.feature_pretty_version = Some(Preg::replace(
r"{(\.9{7})+}",
@@ -183,7 +185,7 @@ impl VersionGuesser {
// try to fetch current version from git branch
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"branch".to_string(),
@@ -199,14 +201,24 @@ impl VersionGuesser {
let mut is_feature_branch = false;
// find current branch and collect all branch names
- for branch in self.process.split_lines(&output) {
+ for branch in self.process.borrow().split_lines(&output) {
if !branch.is_empty() {
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}",
&branch,
- ) {
- let g1 = m.get(1).cloned().unwrap_or_default();
- let g2 = m.get(2).cloned().unwrap_or_default();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let g1 = m
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ let g2 = m
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default();
if g1 == "(no branch)"
|| strpos(&g1, "(detached ") == Some(0)
|| strpos(&g1, "(HEAD detached at") == Some(0)
@@ -225,14 +237,20 @@ impl VersionGuesser {
}
}
- if !branch.is_empty()
- && Preg::is_match_strict_groups(r"{^ *.+/HEAD }", &branch).is_none()
- {
- if let Some(m) = Preg::is_match_strict_groups(
+ if !branch.is_empty() && {
+ let mut tmp: IndexMap<CaptureKey, String> = IndexMap::new();
+ !Preg::is_match_strict_groups3(r"{^ *.+/HEAD }", &branch, Some(&mut tmp))
+ .unwrap_or(false)
+ } {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^(?:\* )? *((?:remotes/(?:origin|upstream)/)?[^\s/]+) *([a-f0-9]+) .*$}",
&branch,
- ) {
- branches.push(m.get(1).cloned().unwrap_or_default());
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ branches.push(m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default());
}
}
}
@@ -258,7 +276,7 @@ impl VersionGuesser {
}
}
GitUtil::check_for_repo_ownership_error(
- &self.process.get_error_output(),
+ &self.process.borrow().get_error_output(),
path,
self.io.as_deref(),
);
@@ -293,10 +311,11 @@ impl VersionGuesser {
.unwrap_or_default(),
);
let mut command_output = String::new();
- if 0 == self
- .process
- .execute(&command, &mut command_output, Some(path.to_string()))
- {
+ if 0 == self.process.borrow_mut().execute_args(
+ &command,
+ &mut command_output,
+ Some(path.to_string()),
+ ) {
let parsed = trim(
&GitUtil::parse_rev_list_output(&command_output, &self.process),
None,
@@ -322,7 +341,7 @@ impl VersionGuesser {
fn version_from_git_tags(&mut self, path: &str) -> Result<Option<(String, String)>> {
// try to fetch current version from git tags
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"describe".to_string(),
@@ -352,7 +371,7 @@ impl VersionGuesser {
) -> Result<Option<VersionData>> {
// try to fetch current version from hg branch
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&["hg".to_string(), "branch".to_string()],
&mut output,
Some(path.to_string()),
@@ -392,8 +411,7 @@ impl VersionGuesser {
self.config.clone(),
// TODO(phase-b): HttpDownloader::new signature
todo!("HttpDownloader::new(io, config)"),
- // TODO(phase-b): clone ProcessExecutor
- todo!("self.process.clone()"),
+ std::rc::Rc::clone(&self.process),
);
let branches: Vec<String> =
array_map(|k: &String| k.clone(), &array_keys(driver.get_branches()));
@@ -495,7 +513,7 @@ impl VersionGuesser {
let mut promises: Vec<Box<dyn shirabe_external_packages::react::promise::promise_interface::PromiseInterface>> =
vec![];
- self.process.set_max_jobs(30);
+ self.process.borrow_mut().set_max_jobs(30);
// TODO(phase-b): try/finally with resetMaxJobs
let result: Result<()> = (|| -> Result<()> {
let mut last_index: i64 = -1;
@@ -519,7 +537,7 @@ impl VersionGuesser {
},
&scm_cmdline,
);
- let async_promise = self.process.execute_async(&cmd_line, path);
+ let async_promise = self.process.borrow_mut().execute_async(&cmd_line, path);
promises.push(async_promise.then(Box::new(
move |process: Process| -> Result<()> {
if !process.is_successful() {
@@ -538,10 +556,10 @@ impl VersionGuesser {
)));
}
- self.process.wait();
+ self.process.borrow_mut().wait();
Ok(())
})();
- self.process.reset_max_jobs();
+ self.process.borrow_mut().reset_max_jobs();
result?;
}
@@ -571,13 +589,11 @@ impl VersionGuesser {
non_feature_branches = implode("|", &names);
}
- !Preg::is_match(
- &format!(
- r"{{^({}|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}}",
- non_feature_branches,
- ),
- branch_name.unwrap_or(""),
- )
+ !Preg::is_match(&format!(
+ r"{{^({}|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}}",
+ non_feature_branches,
+ ), branch_name.unwrap_or("")).unwrap_or(false)
+ .unwrap_or(false)
}
/// @return array{version: string|null, commit: '', pretty_version: string|null}
@@ -587,7 +603,7 @@ impl VersionGuesser {
// try to fetch current version from fossil
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&[
"fossil".to_string(),
"branch".to_string(),
@@ -603,7 +619,7 @@ impl VersionGuesser {
// try to fetch current version from fossil tags
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&["fossil".to_string(), "tag".to_string(), "list".to_string()],
&mut output,
Some(path.to_string()),
@@ -639,7 +655,7 @@ impl VersionGuesser {
// try to fetch current version from svn
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&["svn".to_string(), "info".to_string(), "--xml".to_string()],
&mut output,
Some(path.to_string()),
@@ -720,8 +736,14 @@ impl VersionGuesser {
.into());
}
};
- if let Some(m) = Preg::is_match_strict_groups(r"{^(\d+(?:\.\d+)*)-dev$}i", &version) {
- return Ok(format!("{}.x-dev", m.get(1).cloned().unwrap_or_default()));
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^(\d+(?:\.\d+)*)-dev$}i", &version, Some(&mut m))
+ .unwrap_or(false)
+ {
+ return Ok(format!(
+ "{}.x-dev",
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default()
+ ));
}
Ok(version)
diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs
index 6dbf425..84749a3 100644
--- a/crates/shirabe/src/package/version/version_parser.rs
+++ b/crates/shirabe/src/package/version/version_parser.rs
@@ -42,11 +42,7 @@ impl VersionParser {
let count = pairs.len();
let mut i = 0_usize;
while i < count {
- let mut pair = Preg::replace(
- r"{^([^=: ]+)[=: ](.*)$}",
- "$1 $2",
- pairs[i].trim().to_string(),
- )?;
+ let mut pair = Preg::replace(r"{^([^=: ]+)[=: ](.*)$}", "$1 $2", &pairs[i].trim())?;
if !pair.contains(' ')
&& i + 1 < count
&& !pairs[i + 1].contains('/')
diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs
index 5cbc052..1df58ab 100644
--- a/crates/shirabe/src/package/version/version_selector.rs
+++ b/crates/shirabe/src/package/version/version_selector.rs
@@ -17,7 +17,7 @@ use crate::filter::platform_requirement_filter::platform_requirement_filter_fact
use crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface;
use crate::io::io_interface::IOInterface;
use crate::package::alias_package::AliasPackage;
-use crate::package::base_package::BasePackage;
+use crate::package::base_package::{self, BasePackage};
use crate::package::dumper::array_dumper::ArrayDumper;
use crate::package::loader::array_loader::ArrayLoader;
use crate::package::package_interface::PackageInterface;
@@ -41,7 +41,7 @@ impl VersionSelector {
IndexMap::new();
if let Some(platform_repo) = platform_repo {
for package in platform_repo.get_packages() {
- let constraint = Constraint::new("==", package.get_version())?;
+ let constraint = Constraint::new("==", package.get_version());
platform_constraints
.entry(package.get_name().to_string())
.or_default()
@@ -65,7 +65,7 @@ impl VersionSelector {
io: Option<&dyn IOInterface>,
show_warnings: shirabe_php_shim::PhpMixed,
) -> anyhow::Result<Option<Box<dyn PackageInterface>>> {
- if !BasePackage::STABILITIES.contains_key(preferred_stability) {
+ if !base_package::STABILITIES.contains_key(preferred_stability) {
return Err(shirabe_php_shim::UnexpectedValueException {
message: format!(
"Expected a valid stability name as 3rd argument, got {}",
@@ -92,7 +92,7 @@ impl VersionSelector {
repo_set_flags,
)?;
- let min_priority = *BasePackage::STABILITIES.get(preferred_stability).unwrap();
+ let min_priority = *base_package::STABILITIES.get(preferred_stability).unwrap();
candidates.sort_by(|a, b| {
let a_priority = a.get_stability_priority();
let b_priority = b.get_stability_priority();
@@ -116,7 +116,9 @@ impl VersionSelector {
}
});
- let is_ignore_all = (platform_requirement_filter.as_ref() as &dyn Any)
+ let is_ignore_all = platform_requirement_filter
+ .as_ref()
+ .as_any()
.downcast_ref::<IgnoreAllPlatformRequirementFilter>()
.is_some();
@@ -141,9 +143,11 @@ impl VersionSelector {
if link.get_constraint().matches(provided_constraint.as_ref()) {
continue 'reqs;
}
- let list_filter_opt = (platform_requirement_filter.as_ref()
- as &dyn Any)
- .downcast_ref::<IgnoreListPlatformRequirementFilter>();
+ let list_filter_opt = platform_requirement_filter
+ .as_ref()
+ .as_any()
+ .downcast_ref::<IgnoreListPlatformRequirementFilter>(
+ );
if let Some(list_filter) = list_filter_opt {
if list_filter.is_upper_bound_ignored(name) {
let filtered_constraint = list_filter.filter_constraint(
@@ -178,8 +182,8 @@ impl VersionSelector {
} else {
""
};
- io.write_error(
- shirabe_php_shim::PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Cannot use {}{} {} as it {} {} {} which {}.</>",
pkg.get_pretty_name(),
latest,
@@ -188,7 +192,7 @@ impl VersionSelector {
link.get_target(),
link.get_pretty_constraint(),
reason
- )),
+ ),
true,
if is_first_warning {
io_interface::NORMAL
@@ -223,16 +227,16 @@ impl VersionSelector {
Some(p) => p,
};
- let package =
- if let Some(alias) = (package.as_ref() as &dyn Any).downcast_ref::<AliasPackage>() {
- if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
- alias.get_alias_of()
- } else {
- package
- }
+ let package = if let Some(alias) = package.as_ref().as_any().downcast_ref::<AliasPackage>()
+ {
+ if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
+ alias.get_alias_of()
} else {
package
- };
+ }
+ } else {
+ package
+ };
Ok(Some(package))
}
@@ -268,7 +272,7 @@ impl VersionSelector {
if let Some(extra) = extra {
if extra != VersionParser::DEFAULT_BRANCH_ALIAS {
let new_extra =
- Preg::replace(r"{^(\d+\.\d+\.\d+)(\.9999999)-dev$}", "$1.0", extra.clone())?;
+ Preg::replace(r"{^(\d+\.\d+\.\d+)(\.9999999)-dev$}", "$1.0", &extra)?;
if new_extra != extra {
let new_extra = new_extra.replace(".9999999", ".0");
return self.transform_version(&new_extra, &new_extra, "dev");
diff --git a/crates/shirabe/src/partial_composer.rs b/crates/shirabe/src/partial_composer.rs
index 5547b5a..a310ae8 100644
--- a/crates/shirabe/src/partial_composer.rs
+++ b/crates/shirabe/src/partial_composer.rs
@@ -7,14 +7,14 @@ use crate::package::root_package_interface::RootPackageInterface;
use crate::repository::repository_manager::RepositoryManager;
use crate::util::r#loop::Loop;
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub struct PartialComposer {
global: bool,
package: Option<Box<dyn RootPackageInterface>>,
r#loop: Option<std::rc::Rc<std::cell::RefCell<Loop>>>,
repository_manager: Option<RepositoryManager>,
installation_manager: Option<InstallationManager>,
- config: Option<Config>,
+ config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
event_dispatcher: Option<EventDispatcher>,
}
@@ -27,14 +27,18 @@ impl PartialComposer {
self.package.as_deref().unwrap()
}
- pub fn set_config(&mut self, config: Config) {
+ pub fn set_config(&mut self, config: std::rc::Rc<std::cell::RefCell<Config>>) {
self.config = Some(config);
}
- pub fn get_config(&self) -> &Config {
+ pub fn get_config(&self) -> &std::rc::Rc<std::cell::RefCell<Config>> {
self.config.as_ref().unwrap()
}
+ pub fn get_config_mut(&mut self) -> &mut std::rc::Rc<std::cell::RefCell<Config>> {
+ self.config.as_mut().unwrap()
+ }
+
pub fn set_loop(&mut self, r#loop: std::rc::Rc<std::cell::RefCell<Loop>>) {
self.r#loop = Some(r#loop);
}
diff --git a/crates/shirabe/src/platform/hhvm_detector.rs b/crates/shirabe/src/platform/hhvm_detector.rs
index b425bfe..4e40a00 100644
--- a/crates/shirabe/src/platform/hhvm_detector.rs
+++ b/crates/shirabe/src/platform/hhvm_detector.rs
@@ -12,13 +12,13 @@ static HHVM_VERSION_CACHE: Mutex<Option<Option<String>>> = Mutex::new(None);
#[derive(Debug)]
pub struct HhvmDetector {
executable_finder: Option<ExecutableFinder>,
- process_executor: Option<ProcessExecutor>,
+ process_executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
}
impl HhvmDetector {
pub fn new(
executable_finder: Option<ExecutableFinder>,
- process_executor: Option<ProcessExecutor>,
+ process_executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
Self {
executable_finder,
@@ -50,9 +50,9 @@ impl HhvmDetector {
.get_or_insert_with(ExecutableFinder::new);
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(None, None));
+ let executor = self.process_executor.get_or_insert_with(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None)))
+ });
let mut version_output = shirabe_php_shim::PhpMixed::Null;
let cmd = shirabe_php_shim::PhpMixed::List(
[
@@ -68,6 +68,7 @@ impl HhvmDetector {
.collect(),
);
let exit_code = executor
+ .borrow_mut()
.execute(cmd, Some(&mut version_output), None)
.unwrap_or(1);
if exit_code == 0 {
diff --git a/crates/shirabe/src/platform/version.rs b/crates/shirabe/src/platform/version.rs
index a9566ee..2071d2a 100644
--- a/crates/shirabe/src/platform/version.rs
+++ b/crates/shirabe/src/platform/version.rs
@@ -1,6 +1,7 @@
//! ref: composer/src/Composer/Platform/Version.php
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::version_compare;
pub struct Version;
@@ -9,51 +10,102 @@ impl Version {
pub fn parse_openssl(openssl_version: &str, is_fips: &mut bool) -> Option<String> {
*is_fips = false;
- let matches = Preg::match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::match_strict_groups3(
r"^(?P<version>[0-9.]+)(?P<patch>[a-z]{0,2})(?P<suffix>(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)(?:-\w+)?(?: \(.+?\))?$",
openssl_version,
- )?;
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return None;
+ }
+
+ let version = matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let patch_str = matches
+ .get(&CaptureKey::ByName("patch".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let suffix_str = matches
+ .get(&CaptureKey::ByName("suffix".to_string()))
+ .cloned()
+ .unwrap_or_default();
- let patch = if version_compare(&matches["version"], "3.0.0", "<") {
+ let patch = if version_compare(&version, "3.0.0", "<") {
format!(
".{}",
- Self::convert_alpha_version_to_int_version(&matches["patch"])
+ Self::convert_alpha_version_to_int_version(&patch_str)
)
} else {
String::new()
};
- *is_fips = matches["suffix"].contains("fips");
- let suffix = format!("-{}", matches["suffix"].trim_start_matches('-'))
+ *is_fips = suffix_str.contains("fips");
+ let suffix = format!("-{}", suffix_str.trim_start_matches('-'))
.replace("-fips", "")
.replace("-pre", "-alpha");
Some(
- format!("{}{}{}", matches["version"], patch, suffix)
+ format!("{}{}{}", version, patch, suffix)
.trim_end_matches('-')
.to_string(),
)
}
pub fn parse_libjpeg(libjpeg_version: &str) -> Option<String> {
- let matches =
- Preg::match_strict_groups(r"^(?P<major>\d+)(?P<minor>[a-z]*)$", libjpeg_version)?;
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::match_strict_groups3(
+ r"^(?P<major>\d+)(?P<minor>[a-z]*)$",
+ libjpeg_version,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return None;
+ }
+ let major = matches
+ .get(&CaptureKey::ByName("major".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let minor = matches
+ .get(&CaptureKey::ByName("minor".to_string()))
+ .cloned()
+ .unwrap_or_default();
Some(format!(
"{}.{}",
- matches["major"],
- Self::convert_alpha_version_to_int_version(&matches["minor"])
+ major,
+ Self::convert_alpha_version_to_int_version(&minor)
))
}
pub fn parse_zoneinfo_version(zoneinfo_version: &str) -> Option<String> {
- let matches =
- Preg::match_strict_groups(r"^(?P<year>\d{4})(?P<revision>[a-z]*)$", zoneinfo_version)?;
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::match_strict_groups3(
+ r"^(?P<year>\d{4})(?P<revision>[a-z]*)$",
+ zoneinfo_version,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return None;
+ }
+ let year = matches
+ .get(&CaptureKey::ByName("year".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let revision = matches
+ .get(&CaptureKey::ByName("revision".to_string()))
+ .cloned()
+ .unwrap_or_default();
Some(format!(
"{}.{}",
- matches["year"],
- Self::convert_alpha_version_to_int_version(&matches["revision"])
+ year,
+ Self::convert_alpha_version_to_int_version(&revision)
))
}
diff --git a/crates/shirabe/src/plugin/command_event.rs b/crates/shirabe/src/plugin/command_event.rs
index 715f29a..f2a8417 100644
--- a/crates/shirabe/src/plugin/command_event.rs
+++ b/crates/shirabe/src/plugin/command_event.rs
@@ -2,8 +2,8 @@
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_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;
#[derive(Debug)]
@@ -16,17 +16,26 @@ 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,
+ name: &str,
+ command_name: &str,
+ _input: &dyn InputInterface,
+ _output: &dyn OutputInterface,
+ ) -> Self {
+ Self::new6(name, command_name, _input, _output, vec![], IndexMap::new())
+ }
+
+ pub fn new6(
+ name: &str,
+ command_name: &str,
_input: &dyn InputInterface,
_output: &dyn OutputInterface,
args: Vec<String>,
flags: IndexMap<String, PhpMixed>,
) -> Self {
- let inner = Event::new(name, args, flags);
+ let inner = Event::new(name.to_string(), args, flags);
Self {
inner,
- command_name,
+ command_name: command_name.to_string(),
}
}
diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs
index 24b8e0b..03c4841 100644
--- a/crates/shirabe/src/plugin/plugin_manager.rs
+++ b/crates/shirabe/src/plugin/plugin_manager.rs
@@ -76,13 +76,13 @@ impl PluginManager {
disable_plugins: DisablePlugins,
) -> Self {
let allow_plugin_rules = Self::parse_allowed_plugins(
- composer.get_config().get("allow-plugins").clone(),
+ composer.get_config().borrow().get("allow-plugins").clone(),
Some(composer.get_locker()),
);
let allow_global_plugin_rules = Self::parse_allowed_plugins(
global_composer
.as_ref()
- .map(|gc| gc.get_config().get("allow-plugins").clone())
+ .map(|gc| gc.get_config().borrow_mut().get("allow-plugins").clone())
.unwrap_or(PhpMixed::Bool(false)),
None,
);
@@ -224,7 +224,7 @@ impl PluginManager {
}
if package.get_name() == "symfony/flex"
- && Preg::is_match("{^[0-9.]+$}", package.get_version(), None).unwrap_or(false)
+ && Preg::is_match3("{^[0-9.]+$}", package.get_version(), None).unwrap_or(false)
&& version_compare(package.get_version(), "1.9.8", "<")
{
self.io.write_error(&format!("<warning>The \"{}\" plugin {}was skipped because it is not compatible with Composer 2+. Make sure to update it to version 1.9.8 or greater.</warning>",
@@ -762,7 +762,7 @@ impl PluginManager {
.map(|(k, v)| (k.clone(), *v))
.collect();
for (pattern, allow) in &rules_snapshot {
- if Preg::is_match(pattern, package, None).unwrap_or(false) {
+ if Preg::is_match3(pattern, package, None).unwrap_or(false) {
return Ok(*allow);
}
}
@@ -811,14 +811,18 @@ impl PluginManager {
// persist answer in composer.json if it wasn't simply discarded
if answer_str == "y" || answer_str == "n" {
- let allow_plugins_value =
- composer_ref.get_config().get("allow-plugins").clone();
+ let allow_plugins_value = composer_ref
+ .get_config()
+ .borrow_mut()
+ .get("allow-plugins")
+ .clone();
if let Some(arr) = allow_plugins_value.as_array() {
let mut allow_plugins = arr.clone();
allow_plugins
.insert(package.to_string(), Box::new(PhpMixed::Bool(allow)));
if composer_ref
.get_config()
+ .borrow_mut()
.get("sort-packages")
.as_bool()
.unwrap_or(false)
@@ -827,6 +831,7 @@ impl PluginManager {
}
composer_ref
.get_config()
+ .borrow()
.get_config_source()
.add_config_setting(
"allow-plugins",
diff --git a/crates/shirabe/src/plugin/pre_command_run_event.rs b/crates/shirabe/src/plugin/pre_command_run_event.rs
index 7a8b2f1..a3da4f7 100644
--- a/crates/shirabe/src/plugin/pre_command_run_event.rs
+++ b/crates/shirabe/src/plugin/pre_command_run_event.rs
@@ -2,7 +2,7 @@
// TODO(plugin): this event is part of the plugin API and is dispatched before a command runs
use crate::event_dispatcher::event::Event;
-use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
#[derive(Debug)]
pub struct PreCommandRunEvent {
diff --git a/crates/shirabe/src/plugin/pre_file_download_event.rs b/crates/shirabe/src/plugin/pre_file_download_event.rs
index 0e2be7a..37e1e1e 100644
--- a/crates/shirabe/src/plugin/pre_file_download_event.rs
+++ b/crates/shirabe/src/plugin/pre_file_download_event.rs
@@ -9,7 +9,7 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct PreFileDownloadEvent {
inner: Event,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
processed_url: String,
custom_cache_key: Option<String>,
r#type: String,
@@ -20,7 +20,7 @@ pub struct PreFileDownloadEvent {
impl PreFileDownloadEvent {
pub fn new(
name: String,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
processed_url: String,
r#type: String,
context: PhpMixed,
@@ -36,7 +36,7 @@ impl PreFileDownloadEvent {
}
}
- pub fn get_http_downloader(&self) -> &HttpDownloader {
+ pub fn get_http_downloader(&self) -> &std::rc::Rc<std::cell::RefCell<HttpDownloader>> {
&self.http_downloader
}
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index 5652dad..ef15b39 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -53,10 +53,7 @@ impl ArrayRepository {
/// Adds a new package to the repository
pub fn add_package(&self, package: Box<dyn PackageInterface>) -> Result<()> {
// PHP: if (!$package instanceof BasePackage) throw new \InvalidArgumentException(...)
- if (package.as_any() as &dyn Any)
- .downcast_ref::<BasePackage>()
- .is_none()
- {
+ if package.as_any().downcast_ref::<dyn BasePackage>().is_none() {
return Err(InvalidArgumentException {
message: "Only subclasses of BasePackage are supported".to_string(),
code: 0,
@@ -74,7 +71,7 @@ impl ArrayRepository {
package.set_repository(todo!("self as Box<dyn RepositoryInterface>"))?;
let aliased_package: Option<Box<dyn BasePackage>> =
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
Some(alias.get_alias_of().clone_box())
} else {
None
@@ -101,14 +98,11 @@ impl ArrayRepository {
alias: String,
pretty_alias: String,
) -> Box<dyn BasePackage> {
- while let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
package = alias_pkg.get_alias_of().clone_box();
}
- if (package.as_any() as &dyn Any)
- .downcast_ref::<CompletePackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<CompletePackage>().is_some() {
// TODO(phase-b): construct CompleteAliasPackage/AliasPackage and return as Box<BasePackage>
return todo!("new CompleteAliasPackage(package, alias, pretty_alias)");
}
@@ -199,9 +193,7 @@ impl RepositoryInterface for ArrayRepository {
// add selected packages which match stability requirements
result.insert(spl_object_hash(package.as_ref()), package.clone_box());
// add the aliased package for packages where the alias matches
- if let Some(alias) =
- (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
- {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
let aliased = alias.get_alias_of();
if !result.contains_key(&spl_object_hash(aliased.as_ref())) {
result.insert(spl_object_hash(aliased.as_ref()), aliased.clone_box());
@@ -218,7 +210,7 @@ impl RepositoryInterface for ArrayRepository {
// add aliases of packages that were selected, even if the aliases did not match
for package in &packages {
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
let aliased = alias.get_alias_of();
if result.contains_key(&spl_object_hash(aliased.as_ref())) {
result.insert(spl_object_hash(package.as_ref()), package.clone_box());
@@ -234,17 +226,16 @@ impl RepositoryInterface for ArrayRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
- let name = strtolower(&name);
+ let name = strtolower(name);
let constraint: Box<dyn ConstraintInterface> = match constraint {
FindPackageConstraint::Constraint(c) => c,
FindPackageConstraint::String(s) => {
let version_parser = VersionParser::new();
- // TODO(phase-b): Arc<dyn ConstraintInterface + Send + Sync> -> Box<dyn ConstraintInterface>
- Box::new(version_parser.parse_constraints(&s).unwrap())
+ version_parser.parse_constraints(&s).unwrap().clone_box()
}
};
@@ -262,11 +253,11 @@ impl RepositoryInterface for ArrayRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
// normalize name
- let name = strtolower(&name);
+ let name = strtolower(name);
let mut packages = vec![];
let constraint: Option<Box<dyn ConstraintInterface>> = match constraint {
@@ -274,8 +265,7 @@ impl RepositoryInterface for ArrayRepository {
Some(FindPackageConstraint::Constraint(c)) => Some(c),
Some(FindPackageConstraint::String(s)) => {
let version_parser = VersionParser::new();
- // TODO(phase-b): Arc<dyn ConstraintInterface + Send + Sync> -> Box<dyn ConstraintInterface>
- Some(Box::new(version_parser.parse_constraints(&s).unwrap()))
+ Some(version_parser.parse_constraints(&s).unwrap().clone_box())
}
};
@@ -296,7 +286,7 @@ impl RepositoryInterface for ArrayRepository {
}
fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
- let regex = if mode == Self::SEARCH_FULLTEXT {
+ let regex = if mode == crate::repository::repository_interface::SEARCH_FULLTEXT {
format!(
"{{(?:{})}}i",
implode("|", &Preg::split("{\\s+}", &preg_quote(&query, None)))
@@ -309,7 +299,7 @@ impl RepositoryInterface for ArrayRepository {
let mut matches: IndexMap<String, SearchResult> = IndexMap::new();
for package in self.get_packages() {
let mut name = PackageInterface::get_name(package.as_ref()).to_string();
- if mode == Self::SEARCH_VENDOR {
+ if mode == crate::repository::repository_interface::SEARCH_VENDOR {
// PHP: [$name] = explode('/', $name);
let parts: Vec<&str> = name.splitn(2, '/').collect();
name = parts[0].to_string();
@@ -323,9 +313,9 @@ impl RepositoryInterface for ArrayRepository {
}
}
- let complete = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>();
+ let complete = package.as_any().downcast_ref::<CompletePackage>();
- let fulltext_match = mode == Self::SEARCH_FULLTEXT
+ let fulltext_match = mode == crate::repository::repository_interface::SEARCH_FULLTEXT
&& complete.is_some()
&& Preg::is_match(
&regex,
@@ -334,10 +324,11 @@ impl RepositoryInterface for ArrayRepository {
implode(" ", &complete.unwrap().get_keywords()),
complete.unwrap().get_description().unwrap_or("")
),
- );
+ )
+ .unwrap_or(false);
- if Preg::is_match(&regex, &name) || fulltext_match {
- if mode == Self::SEARCH_VENDOR {
+ if Preg::is_match(&regex, &name).unwrap_or(false) || fulltext_match {
+ if mode == crate::repository::repository_interface::SEARCH_VENDOR {
matches.insert(
name.clone(),
SearchResult {
@@ -405,8 +396,7 @@ impl RepositoryInterface for ArrayRepository {
}
for link in candidate.get_provides().values() {
if package_name == link.get_target() {
- let complete =
- (candidate.as_any() as &dyn Any).downcast_ref::<CompletePackage>();
+ let complete = candidate.as_any().downcast_ref::<CompletePackage>();
let description = complete.and_then(|c| c.get_description().map(String::from));
result.insert(
PackageInterface::get_name(candidate.as_ref()).to_string(),
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index faef831..afaf9cb 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -106,7 +106,7 @@ impl ArtifactRepository {
let basename = file_path.file_name().and_then(|n| n.to_str()).unwrap_or("");
match package {
None => {
- self.io.write_error(
+ self.io.write_error3(
&format!(
"File <comment>{}</comment> doesn't seem to hold a package",
basename
@@ -116,16 +116,12 @@ impl ArtifactRepository {
);
}
Some(package) => {
- self.io.write_error(
- &format!(
- "Found package <info>{}</info> (<comment>{}</comment>) in file <info>{}</info>",
- package.get_name(),
- package.get_pretty_version(),
- basename,
- ),
- true,
- io_interface::VERBOSE,
- );
+ self.io.write_error3(&format!(
+ "Found package <info>{}</info> (<comment>{}</comment>) in file <info>{}</info>",
+ package.get_name(),
+ package.get_pretty_version(),
+ basename,
+ ), true, io_interface::VERBOSE);
self.inner.add_package(package);
}
}
@@ -169,7 +165,7 @@ impl ArtifactRepository {
match get_result {
Ok(j) => json = j,
Err(exception) => {
- self.io.write(
+ self.io.write3(
&format!("Failed loading package {}: {}", pathname, exception),
false,
io_interface::VERBOSE,
diff --git a/crates/shirabe/src/repository/canonical_packages_trait.rs b/crates/shirabe/src/repository/canonical_packages_trait.rs
index 8fae936..3c9c75c 100644
--- a/crates/shirabe/src/repository/canonical_packages_trait.rs
+++ b/crates/shirabe/src/repository/canonical_packages_trait.rs
@@ -14,8 +14,12 @@ pub trait CanonicalPackagesTrait {
// get at most one package of each name, preferring non-aliased ones
let mut packages_by_name: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
for package in packages {
- let name = package.get_name();
- if !packages_by_name.contains_key(&name) || packages_by_name[&name].is_alias_package() {
+ let name = package.get_name().to_string();
+ let prefer_replace = packages_by_name
+ .get(&name)
+ .map(|existing| existing.as_alias_package().is_some())
+ .unwrap_or(true);
+ if prefer_replace {
packages_by_name.insert(name, package);
}
}
@@ -23,11 +27,10 @@ pub trait CanonicalPackagesTrait {
let mut canonical_packages = Vec::new();
// unfold aliased packages
- for mut package in packages_by_name.into_values() {
- while package.is_alias_package() {
- package = package.get_alias_of();
- }
-
+ for package in packages_by_name.into_values() {
+ // TODO(phase-b): unfolding requires `Box<dyn PackageInterface>` traversal of
+ // `AliasPackage::get_alias_of()` (currently returns `&BasePackage`, not an
+ // ownable trait object). Push the alias as-is for now.
canonical_packages.push(package);
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index dfbb0b5..1e6443f 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::metadata_minifier::metadata_minifier::MetadataMinifier;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, LogicException,
@@ -84,7 +84,7 @@ pub struct ComposerRepository {
/// non-empty-string
base_url: String,
io: Box<dyn IOInterface>,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
pub(crate) cache: Cache,
pub(crate) notify_url: Option<String>,
@@ -148,7 +148,7 @@ impl ComposerRepository {
mut repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
config: &Config,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
) -> anyhow::Result<Self> {
// parent::__construct();
@@ -246,13 +246,16 @@ impl ComposerRepository {
.to_string();
// force url for packagist.org to repo.packagist.org
- let mut match_packagist: Vec<String> = Vec::new();
- if Preg::is_match_with_matches(
+ let mut match_packagist: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
r"{^(?P<proto>https?)://packagist\.org/?$}i",
&url,
- &mut match_packagist,
+ Some(&mut match_packagist),
)? {
- let proto = match_packagist.get(1).cloned().unwrap_or_default();
+ let proto = match_packagist
+ .get(&CaptureKey::ByName("proto".to_string()))
+ .cloned()
+ .unwrap_or_default();
url = format!("{}://repo.packagist.org", proto);
}
@@ -272,7 +275,7 @@ impl ComposerRepository {
let loader = ArrayLoader::new_with_parser(version_parser.clone());
let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
- http_downloader.clone(),
+ std::rc::Rc::clone(&http_downloader),
None,
)));
@@ -336,10 +339,10 @@ impl ComposerRepository {
let name = strtolower(&name);
let constraint: Box<dyn ConstraintInterface> = match constraint {
- PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?,
+ PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?.clone_box(),
_ => {
// already a ConstraintInterface object passed as opaque PhpMixed
- self.version_parser.parse_constraints("")?
+ self.version_parser.parse_constraints("")?.clone_box()
}
};
@@ -393,7 +396,10 @@ impl ComposerRepository {
return Ok(None);
}
- Ok(self.inner.find_package(name, Some(constraint)))
+ Ok(self.inner.find_package(
+ &name,
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(constraint),
+ ))
}
/// @inheritDoc
@@ -408,7 +414,9 @@ impl ComposerRepository {
let name = strtolower(&name);
let constraint: Option<Box<dyn ConstraintInterface>> = match constraint {
None => None,
- Some(PhpMixed::String(s)) => Some(self.version_parser.parse_constraints(&s)?),
+ Some(PhpMixed::String(s)) => {
+ Some(self.version_parser.parse_constraints(&s)?.clone_box())
+ }
Some(_) => None,
};
@@ -458,7 +466,11 @@ impl ComposerRepository {
return Ok(vec![]);
}
- Ok(self.inner.find_packages(name, constraint))
+ Ok(self.inner.find_packages(
+ &name,
+ constraint
+ .map(crate::repository::repository_interface::FindPackageConstraint::Constraint),
+ ))
}
fn filter_packages(
@@ -571,7 +583,10 @@ impl ComposerRepository {
};
let filter_results = |results: Vec<String>| -> anyhow::Result<Vec<String>> {
match &package_filter_regex {
- Some(regex) => Ok(Preg::grep(regex, &results)?),
+ Some(regex) => {
+ let results_refs: Vec<&str> = results.iter().map(|s| s.as_str()).collect();
+ Ok(Preg::grep(regex, &results_refs)?)
+ }
None => Ok(results),
}
};
@@ -658,6 +673,7 @@ impl ComposerRepository {
url.push_str(&format!("?filter={}", urlencode(filter)));
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
let package_names: Vec<String> = result
@@ -689,6 +705,7 @@ impl ComposerRepository {
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
let package_names: Vec<String> = result
@@ -914,15 +931,21 @@ impl ComposerRepository {
if self.has_providers()? || self.lazy_providers_url.is_some() {
// optimize search for "^foo/bar" where at least "^foo/" is present by loading this directly from the listUrl if present
- let mut match_groups: Vec<String> = Vec::new();
- if Preg::is_match_strict_groups(
+ let mut match_groups: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^\^(?P<query>(?P<vendor>[a-z0-9_.-]+)/[a-z0-9_.-]*)\*?$}i",
&query,
- &mut match_groups,
+ Some(&mut match_groups),
)? && self.list_url.is_some()
{
- let q = match_groups.get(1).cloned().unwrap_or_default();
- let vendor = match_groups.get(2).cloned().unwrap_or_default();
+ let q = match_groups
+ .get(&CaptureKey::ByName("query".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let vendor = match_groups
+ .get(&CaptureKey::ByName("vendor".to_string()))
+ .cloned()
+ .unwrap_or_default();
let url = format!(
"{}?vendor={}&filter={}",
self.list_url.as_ref().unwrap(),
@@ -931,6 +954,7 @@ impl ComposerRepository {
);
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
@@ -1179,7 +1203,7 @@ impl ComposerRepository {
http_map.insert("content".to_string(), Box::new(PhpMixed::String(body)));
}
- let response = self.http_downloader.get(&api_url, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&api_url, &options)?;
let mut warned = false;
let decoded = response.decode_json()?;
let advisories_response = decoded
@@ -1245,7 +1269,7 @@ impl ComposerRepository {
let mut result: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new();
if let Some(providers_api_url) = self.providers_api_url.clone() {
- let api_result = match self.http_downloader.get(
+ let api_result = match self.http_downloader.borrow_mut().get(
&providers_api_url.replace("%package%", package_name),
&self.options,
) {
@@ -2514,11 +2538,14 @@ impl ComposerRepository {
}
if url.starts_with('/') {
- let mut matches: Vec<String> = Vec::new();
- if Preg::is_match_with_matches(r"{^[^:]++://[^/]*+}", &self.url, &mut matches)? {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(r"{^[^:]++://[^/]*+}", &self.url, Some(&mut matches))? {
return Ok(format!(
"{}{}",
- matches.get(0).cloned().unwrap_or_default(),
+ matches
+ .get(&CaptureKey::ByIndex(0))
+ .cloned()
+ .unwrap_or_default(),
url
));
}
@@ -2823,7 +2850,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -2842,7 +2869,7 @@ impl ComposerRepository {
options = pre_file_download_event.get_transport_options();
}
- let response = self.http_downloader.get(&filename, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
let mut json = response.get_body().to_string();
if let Some(sha256_val) = sha256 {
if sha256_val != hash("sha256", &json) {
@@ -3004,7 +3031,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -3048,7 +3075,7 @@ impl ComposerRepository {
http_map.insert("header".to_string(), Box::new(PhpMixed::List(headers)));
}
- let response = self.http_downloader.get(&filename, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
let mut json = response.get_body().to_string();
if json.is_empty() && response.get_status_code() == 304 {
return Ok(FetchFileIfLastModifiedResult::NotModified);
@@ -3159,7 +3186,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -3332,7 +3359,7 @@ impl ComposerRepository {
}
};
- let initial = self.http_downloader.add(&filename, &options)?;
+ let initial = self.http_downloader.borrow_mut().add(&filename, &options)?;
Ok(initial.then_with_reject_boxed(Box::new(accept), Box::new(reject)))
}
diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs
index 4914538..d956cdd 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -31,17 +31,16 @@ impl CompositeRepository {
&self.repositories
}
- pub fn remove_package(&mut self, package: &dyn PackageInterface) {
- for repository in &mut self.repositories {
- // TODO(phase-b): only call remove_package on WritableRepositoryInterface implementors
- let _ = repository.remove_package(package);
+ pub fn remove_package(&mut self, _package: &dyn PackageInterface) {
+ // TODO(phase-b): only call remove_package on WritableRepositoryInterface implementors;
+ // requires a downcast helper such as `as_writable() -> Option<&mut dyn WritableRepositoryInterface>` on RepositoryInterface.
+ for _repository in &mut self.repositories {
+ todo!()
}
}
pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
- if let Some(composite) =
- (repository.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
+ if let Some(composite) = repository.as_any().downcast_ref::<CompositeRepository>() {
for repo in composite.get_repositories() {
self.repositories.push(repo.clone_box());
}
@@ -78,11 +77,11 @@ impl RepositoryInterface for CompositeRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
for repository in &self.repositories {
- let package = repository.find_package(name.clone(), constraint.clone());
+ let package = repository.find_package(name, constraint.clone());
if package.is_some() {
return package;
}
@@ -92,12 +91,12 @@ impl RepositoryInterface for CompositeRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
let mut packages = vec![];
for repository in &self.repositories {
- packages.extend(repository.find_packages(name.clone(), constraint.clone()));
+ packages.extend(repository.find_packages(name, constraint.clone()));
}
packages
}
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 3d785e7..4ce8585 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -19,6 +19,7 @@ use crate::json::json_file::JsonFile;
use crate::package::alias_package::AliasPackage;
use crate::package::dumper::array_dumper::ArrayDumper;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::package_interface::PackageInterface;
use crate::package::root_alias_package::RootAliasPackage;
use crate::package::root_package_interface::RootPackageInterface;
@@ -39,7 +40,7 @@ pub struct FilesystemRepository {
/// @var ?RootPackageInterface
root_package: Option<Box<dyn RootPackageInterface>>,
/// @var Filesystem
- filesystem: Filesystem,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
/// @var bool|null
dev_mode: Option<bool>,
}
@@ -53,9 +54,10 @@ impl FilesystemRepository {
repository_file: JsonFile,
dump_versions: bool,
root_package: Option<Box<dyn RootPackageInterface>>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(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(),
@@ -191,10 +193,13 @@ impl FilesystemRepository {
// as realpath() does some additional normalizations with network paths that normalizePath does not
// and we need to find shortest path correctly
let repo_dir = dirname(self.file.get_path());
- self.filesystem.ensure_directory_exists(&repo_dir);
+ self.filesystem
+ .borrow_mut()
+ .ensure_directory_exists(&repo_dir);
let repo_dir = self
.filesystem
+ .borrow()
.normalize_path(&realpath(&repo_dir).unwrap_or_default());
let mut install_paths: IndexMap<String, Option<String>> = IndexMap::new();
@@ -204,8 +209,9 @@ impl FilesystemRepository {
let mut install_path: Option<String> = None;
if let Some(path_str) = &path {
if !path_str.is_empty() {
- let normalized_path = self.filesystem.normalize_path(&if self
+ let normalized_path = self.filesystem.borrow_mut().normalize_path(&if self
.filesystem
+ .borrow()
.is_absolute_path(path_str)
{
path_str.clone()
@@ -216,7 +222,7 @@ impl FilesystemRepository {
path_str
)
});
- install_path = Some(self.filesystem.find_shortest_path(
+ install_path = Some(self.filesystem.borrow_mut().find_shortest_path(
&repo_dir,
&normalized_path,
true,
@@ -282,17 +288,12 @@ impl FilesystemRepository {
});
}
- self.file.write(
- PhpMixed::Array(
- data.clone()
- .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,
- )?;
+ self.file.write(PhpMixed::Array(
+ data.clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
+ ))?;
if self.dump_versions {
let versions = self.generate_installed_versions(
@@ -302,7 +303,7 @@ impl FilesystemRepository {
&repo_dir,
)?;
- self.filesystem.file_put_contents_if_modified(
+ self.filesystem.borrow_mut().file_put_contents_if_modified(
&format!("{}/installed.php", repo_dir),
&format!("<?php return {};\n", self.dump_to_php_code(&versions, 0),),
);
@@ -311,7 +312,7 @@ impl FilesystemRepository {
// this normally should not happen but during upgrades of Composer when it is installed in the project it is a possibility
if let Some(class_content) = installed_versions_class {
- self.filesystem.file_put_contents_if_modified(
+ self.filesystem.borrow_mut().file_put_contents_if_modified(
&format!("{}/InstalledVersions.php", repo_dir),
&class_content,
);
@@ -347,7 +348,7 @@ impl FilesystemRepository {
let pattern = "{(?(DEFINE)\n (?<number> -? \\s*+ \\d++ (?:\\.\\d++)? )\n (?<boolean> true | false | null )\n (?<strings> (?&string) (?: \\s*+ \\. \\s*+ (?&string))*+ )\n (?<string> (?: \" (?:[^\"\\\\$]*+ | \\\\ [\"\\\\0] )* \" | ' (?:[^'\\\\]*+ | \\\\ ['\\\\] )* ' ) )\n (?<array> array\\( \\s*+ (?: (?:(?&number)|(?&strings)) \\s*+ => \\s*+ (?: (?:__DIR__ \\s*+ \\. \\s*+)? (?&strings) | (?&value) ) \\s*+, \\s*+ )*+ \\s*+ \\) )\n (?<value> (?: (?&number) | (?&boolean) | (?&strings) | (?&array) ) )\n)\n^<\\?php\\s++return\\s++(?&array)\\s*+;$}ix";
if let Some(data) = installed_versions_data {
let mixed = PhpMixed::String(data.clone());
- if is_string(&mixed) && Preg::is_match(pattern, &trim(&data, None)) {
+ if is_string(&mixed) && Preg::is_match(pattern, &trim(&data, None)).unwrap_or(false) {
let replaced = Preg::replace(
r#"{=>\s*+__DIR__\s*+\.\s*+(['\"])}"#,
&format!(
@@ -356,6 +357,10 @@ impl FilesystemRepository {
),
&data,
);
+ let replaced = match replaced {
+ Ok(s) => s,
+ Err(_) => return false,
+ };
let evaluated = r#eval(&format!("?>{}", replaced));
InstalledVersions::reload(
evaluated
@@ -411,7 +416,7 @@ impl FilesystemRepository {
}
} else if key == "install_path" && is_string(value) {
let s = value.as_string().unwrap_or("").to_string();
- if self.filesystem.is_absolute_path(&s) {
+ if self.filesystem.borrow_mut().is_absolute_path(&s) {
lines.push_str(&format!("{},\n", var_export(&PhpMixed::String(s), true),));
} else {
lines.push_str(&format!(
@@ -480,9 +485,7 @@ impl FilesystemRepository {
let mut current_root: Box<dyn RootPackageInterface> = root_package;
// packages.push(current_root.clone_box());
- while let Some(_alias) =
- (current_root.as_any() as &dyn Any).downcast_ref::<RootAliasPackage>()
- {
+ while let Some(_alias) = current_root.as_any().downcast_ref::<RootAliasPackage>() {
current_root =
todo!("RootAliasPackage::get_alias_of() returning Box<dyn RootPackageInterface>");
// packages.push(current_root.clone_box());
@@ -507,10 +510,7 @@ impl FilesystemRepository {
// add real installed packages
for package in &packages {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
continue;
}
@@ -532,7 +532,7 @@ impl FilesystemRepository {
.as_array()
.map(|m| m.contains_key(package.get_name()))
.unwrap_or(false);
- for replace in package.get_replaces() {
+ for (_, replace) in package.get_replaces() {
// exclude platform replaces as when they are really there we can not check for their presence
if PlatformRepository::is_platform_package(replace.get_target()) {
continue;
@@ -550,7 +550,7 @@ impl FilesystemRepository {
todo!("append replaced to versions['versions'][target]['replaced']");
}
}
- for provide in package.get_provides() {
+ for (_, provide) in package.get_provides() {
// exclude platform provides as when they are really there we can not check for their presence
if PlatformRepository::is_platform_package(provide.get_target()) {
continue;
@@ -571,12 +571,13 @@ impl FilesystemRepository {
// add aliases
for package in &packages {
- let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() else {
+ let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() else {
continue;
};
// TODO(phase-b): mutate nested versions['versions'][name]['aliases']
todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']");
- if (package.as_any() as &dyn Any)
+ if package
+ .as_any()
.downcast_ref::<dyn RootPackageInterface>()
.is_some()
{
@@ -641,14 +642,19 @@ impl FilesystemRepository {
};
}
- let install_path = if (package.as_any() as &dyn Any)
+ let install_path = if package
+ .as_any()
.downcast_ref::<dyn RootPackageInterface>()
.is_some()
{
- let to = self.filesystem.normalize_path(
+ let to = self.filesystem.borrow_mut().normalize_path(
&realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default(),
);
- Some(self.filesystem.find_shortest_path(repo_dir, &to, true))
+ Some(
+ self.filesystem
+ .borrow_mut()
+ .find_shortest_path(repo_dir, &to, true),
+ )
} else {
install_paths.get(package.get_name()).cloned().flatten()
};
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index ffdb3b9..482ffbe 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -165,10 +165,10 @@ impl RepositoryInterface for FilterRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
- if !self.is_allowed(&name) {
+ if !self.is_allowed(name) {
return None;
}
@@ -177,10 +177,10 @@ impl RepositoryInterface for FilterRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
- if !self.is_allowed(&name) {
+ if !self.is_allowed(name) {
return Vec::new();
}
diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs
index c30d682..ddcda90 100644
--- a/crates/shirabe/src/repository/installed_array_repository.rs
+++ b/crates/shirabe/src/repository/installed_array_repository.rs
@@ -20,6 +20,16 @@ pub struct InstalledArrayRepository {
}
impl InstalledArrayRepository {
+ pub fn new() -> anyhow::Result<Self> {
+ Self::new_with_packages(Vec::new())
+ }
+
+ pub fn new_with_packages(packages: Vec<Box<dyn PackageInterface>>) -> anyhow::Result<Self> {
+ Ok(Self {
+ inner: WritableArrayRepository::new(packages)?,
+ })
+ }
+
pub fn get_repo_name(&self) -> String {
format!("installed {}", self.inner.get_repo_name())
}
@@ -89,14 +99,14 @@ impl RepositoryInterface for InstalledArrayRepository {
}
fn find_package(
&self,
- _name: String,
+ _name: &str,
_constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
todo!()
}
fn find_packages(
&self,
- _name: String,
+ _name: &str,
_constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
todo!()
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index 1d1caf6..c7f2a4d 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -28,7 +28,7 @@ impl InstalledFilesystemRepository {
repository_file: JsonFile,
dump_versions: bool,
root_package: Option<Box<dyn RootPackageInterface>>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
Ok(Self {
inner: FilesystemRepository::new(
@@ -109,14 +109,14 @@ impl RepositoryInterface for InstalledFilesystemRepository {
}
fn find_package(
&self,
- _name: String,
+ _name: &str,
_constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
todo!()
}
fn find_packages(
&self,
- _name: String,
+ _name: &str,
_constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
todo!()
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index d0130f4..12abf1e 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -51,7 +51,7 @@ impl InstalledRepository {
pub fn find_packages_with_replacers_and_providers(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
let name = name.to_lowercase();
@@ -121,7 +121,7 @@ impl InstalledRepository {
let mut root_package: Option<Box<dyn BasePackage>> = None;
for package in self.inner.get_packages() {
- if package.as_any().is::<dyn RootPackageInterface>() {
+ if package.as_root_package_interface().is_some() {
root_package = Some(package);
break;
}
@@ -177,7 +177,7 @@ impl InstalledRepository {
}
}
- if package.as_any().is::<dyn RootPackageInterface>() {
+ if package.as_root_package_interface().is_some() {
for (k, v) in package.get_dev_requires() {
links.entry(k).or_insert(v);
}
@@ -222,7 +222,7 @@ impl InstalledRepository {
if invert && needles.contains(&package.get_name().to_string()) {
for link in package.get_conflicts().values() {
- for pkg in self.find_packages(link.get_target().to_string(), None) {
+ for pkg in self.find_packages(link.get_target(), None) {
let version = Constraint::new("=", pkg.get_version());
if link.get_constraint().matches(&version) == invert {
results.push(DependentsEntry(package.clone_box(), link.clone(), None));
@@ -233,7 +233,7 @@ impl InstalledRepository {
for link in package.get_conflicts().values() {
if needles.contains(&link.get_target().to_string()) {
- for pkg in self.find_packages(link.get_target().to_string(), None) {
+ for pkg in self.find_packages(link.get_target(), None) {
let version = Constraint::new("=", pkg.get_version());
if link.get_constraint().matches(&version) == invert {
results.push(DependentsEntry(package.clone_box(), link.clone(), None));
@@ -254,7 +254,7 @@ impl InstalledRepository {
if PlatformRepository::is_platform_package(link.get_target()) {
if self
.find_package(
- link.get_target().to_string(),
+ link.get_target(),
FindPackageConstraint::Constraint(
link.get_constraint().clone_box(),
),
@@ -265,7 +265,7 @@ impl InstalledRepository {
}
let platform_pkg = self.find_package(
- link.get_target().to_string(),
+ link.get_target(),
FindPackageConstraint::String("*".to_string()),
);
let description = platform_pkg
@@ -292,7 +292,7 @@ impl InstalledRepository {
}
for pkg in self.get_packages() {
- if !pkg.get_names().contains(&link.get_target().to_string()) {
+ if !pkg.get_names(true).contains(&link.get_target().to_string()) {
continue;
}
@@ -320,7 +320,9 @@ impl InstalledRepository {
root_reqs.entry(k).or_insert(v);
}
for root_req in root_reqs.values() {
- if pkg.get_names().contains(&root_req.get_target().to_string())
+ if pkg
+ .get_names(true)
+ .contains(&root_req.get_target().to_string())
&& !root_req.get_constraint().matches(link.get_constraint())
{
results.push(DependentsEntry(
@@ -422,7 +424,7 @@ impl RepositoryInterface for InstalledRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -430,7 +432,7 @@ impl RepositoryInterface for InstalledRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index c0a5034..87acf6e 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -41,7 +41,7 @@ impl RepositoryInterface for LockArrayRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -49,7 +49,7 @@ impl RepositoryInterface for LockArrayRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index 64b578e..dc8a8f3 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -40,18 +40,24 @@ impl PackageRepository {
};
Self {
- inner: ArrayRepository::new(),
+ inner: ArrayRepository::new(vec![])
+ .expect("ArrayRepository::new with empty vec cannot fail"),
config: config_list,
security_advisories,
}
}
pub fn initialize(&mut self) -> anyhow::Result<Result<(), InvalidRepositoryException>> {
- self.inner.initialize()?;
+ self.inner.initialize();
- let loader = ValidatingArrayLoader::new(ArrayLoader::new(None, true), true);
+ let mut loader =
+ ValidatingArrayLoader::new(Box::new(ArrayLoader::new(None, true)), true, None, 0);
for package in &self.config {
- let package = match loader.load(package) {
+ let config_map: IndexMap<String, Box<PhpMixed>> = match package {
+ PhpMixed::Array(m) => m.clone(),
+ _ => IndexMap::new(),
+ };
+ let package_loaded = match loader.load(config_map, "") {
Ok(p) => p,
Err(e) => {
let msg = format!(
@@ -65,13 +71,16 @@ impl PackageRepository {
})));
}
};
- self.inner.add_package(package)?;
+ // TODO(phase-b): add_package expects Box<dyn PackageInterface>; loader returns Box<dyn BasePackage>
+ let _ = package_loaded;
}
Ok(Ok(()))
}
pub fn get_repo_name(&self) -> String {
+ use crate::repository::repository_interface::RepositoryInterface;
Preg::replace(r"^array ", "package ", &self.inner.get_repo_name())
+ .unwrap_or_else(|_| self.inner.get_repo_name())
}
}
@@ -91,42 +100,45 @@ impl AdvisoryProviderInterface for PackageRepository {
let mut advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new();
for (package_name, package_advisories) in &self.security_advisories {
- if package_constraint_map.contains_key(package_name.as_str()) {
- let items: anyhow::Result<Vec<PartialOrSecurityAdvisory>> = match package_advisories {
- PhpMixed::List(list) => list
- .iter()
- .filter_map(|data| {
- let data_map = match data.as_ref() {
- PhpMixed::Array(m) => m
- .iter()
- .map(|(k, v)| (k.clone(), *v.clone()))
- .collect::<IndexMap<String, PhpMixed>>(),
- _ => return Ok(None),
- };
- let advisory =
- PartialSecurityAdvisory::create(package_name, &data_map, &semver_parser)
- .ok()?;
- if !allow_partial_advisories
- && matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
- {
- return Err(anyhow::anyhow!(RuntimeException { message: format!("Advisory for {} could not be loaded as a full advisory from {}\n{}", package_name, self.get_repo_name(), var_export(data, true)), code: 0 }));
- }
- let affected_versions = match &advisory {
- PartialOrSecurityAdvisory::Full(a) => &a.affected_versions,
- PartialOrSecurityAdvisory::Partial(a) => &a.affected_versions,
- };
- if !affected_versions
- .matches(package_constraint_map[package_name.as_str()].as_ref())
- {
- return Ok(None);
- }
- Ok(Some(advisory))
- })
- .collect(),
- _ => vec![],
+ if !package_constraint_map.contains_key(package_name.as_str()) {
+ continue;
+ }
+ let list = match package_advisories {
+ PhpMixed::List(list) => list,
+ _ => continue,
+ };
+ let mut items: Vec<PartialOrSecurityAdvisory> = Vec::new();
+ for data in list {
+ let data_map: IndexMap<String, PhpMixed> = match data.as_ref() {
+ PhpMixed::Array(m) => m.iter().map(|(k, v)| (k.clone(), *v.clone())).collect(),
+ _ => continue,
};
- advisories.insert(package_name.clone(), items?);
+ let advisory = match PartialSecurityAdvisory::create(
+ package_name,
+ &data_map,
+ &semver_parser,
+ ) {
+ Ok(a) => a,
+ Err(_) => continue,
+ };
+ if !allow_partial_advisories
+ && matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
+ {
+ return Err(anyhow::anyhow!(RuntimeException {
+ message: format!(
+ "Advisory for {} could not be loaded as a full advisory from {}\n{}",
+ package_name,
+ self.get_repo_name(),
+ var_export(data, true)
+ ),
+ code: 0,
+ }));
+ }
+ // TODO(phase-b): affected_versions is a method, not a field, and matches() return type may differ
+ let _ = (&advisory, &package_constraint_map);
+ items.push(advisory);
}
+ advisories.insert(package_name.clone(), items);
}
let names_found: Vec<String> = advisories.keys().cloned().collect();
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index 3b6ed8d..c29e6c0 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -12,6 +12,7 @@ use crate::event_dispatcher::event_dispatcher::EventDispatcher;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::version::version_guesser::VersionGuesser;
use crate::package::version::version_parser::VersionParser;
use crate::repository::array_repository::ArrayRepository;
@@ -30,7 +31,7 @@ pub struct PathRepository {
version_guesser: VersionGuesser,
url: String,
repo_config: IndexMap<String, PhpMixed>,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
options: IndexMap<String, PhpMixed>,
}
@@ -44,10 +45,10 @@ impl PathRepository {
pub fn new(
repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: Option<HttpDownloader>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<Self> {
if !repo_config.contains_key("url") {
return Err(RuntimeException {
@@ -64,8 +65,17 @@ impl PathRepository {
.unwrap_or("")
.to_string();
let url = Platform::expand_path(&url_str);
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
- let version_guesser = VersionGuesser::new(&config, &process, VersionParser::new(), &*io);
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ io.clone_box(),
+ ))))
+ });
+ let version_guesser = VersionGuesser::new(
+ config,
+ std::rc::Rc::clone(&process),
+ shirabe_semver::version_parser::VersionParser,
+ Some(io.clone_box()),
+ );
let mut options = repo_config
.get("options")
.and_then(|v| v.as_array())
@@ -81,7 +91,7 @@ impl PathRepository {
}
Ok(Self {
- inner: ArrayRepository::new(),
+ inner: ArrayRepository::new(vec![])?,
loader: ArrayLoader::new(None, true),
version_guesser,
url,
@@ -105,7 +115,7 @@ impl PathRepository {
}
pub(crate) fn initialize(&mut self) -> anyhow::Result<()> {
- self.inner.initialize()?;
+ self.inner.initialize();
let url_matches = self.get_url_matches()?;
@@ -140,8 +150,11 @@ impl PathRepository {
}
let json = file_get_contents(&composer_file_path).unwrap_or_default();
- let mut package =
- JsonFile::parse_json(&json, Some(&composer_file_path))?.unwrap_or_default();
+ let parsed = JsonFile::parse_json(Some(&json), Some(&composer_file_path))?;
+ let mut package: IndexMap<String, PhpMixed> = match parsed {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
let dist = {
let mut dist = IndexMap::new();
dist.insert(
@@ -213,20 +226,20 @@ impl PathRepository {
if !package.contains_key("version") {
if let Some(root_version) = Platform::get_env("COMPOSER_ROOT_VERSION") {
if !root_version.is_empty() {
- let mut ref1 = String::new();
- let mut ref2 = String::new();
- if self.process.execute(
- &["git", "rev-parse", "HEAD"].map(|s| s.to_string()).to_vec(),
- &mut ref1,
- Some(path.clone()),
- ) == 0
- && self.process.execute(
- &["git", "rev-parse", "HEAD"].map(|s| s.to_string()).to_vec(),
- &mut ref2,
- None,
- ) == 0
- && ref1 == ref2
- {
+ let mut ref1 = PhpMixed::Null;
+ let mut ref2 = PhpMixed::Null;
+ let cmd = PhpMixed::from(vec!["git", "rev-parse", "HEAD"]);
+ let code1 = self
+ .process
+ .borrow_mut()
+ .execute(cmd.clone(), Some(&mut ref1), Some(path.as_str()))
+ .unwrap_or(1);
+ let code2 = self
+ .process
+ .borrow_mut()
+ .execute(cmd, Some(&mut ref2), None)
+ .unwrap_or(1);
+ if code1 == 0 && code2 == 0 && ref1.as_string() == ref2.as_string() {
package.insert(
"version".to_string(),
PhpMixed::String(self.version_guesser.get_root_version_from_env()),
@@ -236,7 +249,7 @@ impl PathRepository {
}
}
- let mut output = String::new();
+ let mut output = PhpMixed::Null;
let command = GitUtil::build_rev_list_command(&self.process, {
let mut args = vec![
"-n1".to_string(),
@@ -250,10 +263,17 @@ impl PathRepository {
&& shirabe_php_shim::is_dir(&format!("{}/.git", path.trim_end_matches('/')))
&& self
.process
- .execute(&command, &mut output, Some(path.clone()))
+ .borrow_mut()
+ .execute(
+ PhpMixed::from(command),
+ Some(&mut output),
+ Some(path.as_str()),
+ )
+ .unwrap_or(1)
== 0
{
- let ref_val = GitUtil::parse_rev_list_output(&output, &self.process)
+ let output_str = output.as_string().unwrap_or("").to_string();
+ let ref_val = GitUtil::parse_rev_list_output(&output_str, &self.process)
.trim()
.to_string();
if let Some(PhpMixed::Array(ref mut dist)) = package.get_mut("dist") {
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 8fb9d0a..64bc861 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -4,7 +4,7 @@ use std::sync::{LazyLock, Mutex};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_map_str_fn, array_slice,
@@ -337,14 +337,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// librabbitmq version => 0.9.0
- if let Ok(Some(librabbitmq_matches)) = Preg::is_match_strict_groups(
+ let mut librabbitmq_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^librabbitmq version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut librabbitmq_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-librabbitmq", name),
- Some(&librabbitmq_matches["version"]),
+ librabbitmq_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("AMQP librabbitmq version"),
&[],
&[],
@@ -352,14 +358,22 @@ impl PlatformRepository {
}
// AMQP protocol version => 0-9-1
- if let Ok(Some(protocol_matches)) = Preg::is_match_strict_groups(
+ let mut protocol_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^AMQP protocol version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut protocol_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_str = protocol_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-protocol", name),
- Some(&str_replace("-", ".", &protocol_matches["version"])),
+ Some(&str_replace("-", ".", &version_str)),
Some("AMQP protocol version"),
&[],
&[],
@@ -371,13 +385,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// BZip2 Version => 1.0.6, 6-Sept-2010
- if let Ok(Some(matches)) =
- Preg::is_match_strict_groups("/^BZip2 Version => (?<version>.*),/im", &info)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^BZip2 Version => (?<version>.*),/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
name,
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -402,16 +423,27 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// SSL Version => OpenSSL/1.0.1t
- if let Ok(Some(ssl_matches)) = Preg::is_match_strict_groups(
+ let mut ssl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im",
&info,
- ) {
- let library = strtolower(&ssl_matches["library"]);
+ Some(&mut ssl_matches),
+ )
+ .unwrap_or(false)
+ {
+ let ssl_library_raw = ssl_matches
+ .get(&CaptureKey::ByName("library".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let ssl_version = ssl_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let library = strtolower(&ssl_library_raw);
if library == "openssl" {
let mut is_fips = false;
- let parsed_version =
- Version::parse_openssl(&ssl_matches["version"], &mut is_fips)
- .unwrap_or_default();
+ let parsed_version = Version::parse_openssl(&ssl_version, &mut is_fips)
+ .unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-openssl{}", name, if is_fips { "-fips" } else { "" }),
@@ -427,14 +459,21 @@ impl PlatformRepository {
} else {
let (shortlib, ssl_lib);
if str_starts_with(&library, "(securetransport)") {
- if let Ok(Some(securetransport_matches)) =
- Preg::is_match_strict_groups(
- "{^\\(securetransport\\) ([a-z0-9]+)}",
- &library,
- )
+ let mut securetransport_matches: IndexMap<CaptureKey, String> =
+ IndexMap::new();
+ if Preg::is_match3(
+ "{^\\(securetransport\\) ([a-z0-9]+)}",
+ &library,
+ Some(&mut securetransport_matches),
+ )
+ .unwrap_or(false)
{
shortlib = "securetransport".to_string();
- ssl_lib = format!("curl-{}", securetransport_matches["1"]);
+ let m1 = securetransport_matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ ssl_lib = format!("curl-{}", m1);
} else {
shortlib = library.clone();
ssl_lib = "curl-openssl".to_string();
@@ -446,11 +485,8 @@ impl PlatformRepository {
self.add_library(
&mut libraries,
&format!("{}-{}", name, shortlib),
- Some(&ssl_matches["version"]),
- Some(&format!(
- "curl {} version ({})",
- library, &ssl_matches["version"]
- )),
+ Some(&ssl_version),
+ Some(&format!("curl {} version ({})", library, ssl_version)),
&[ssl_lib],
&[],
)?;
@@ -458,28 +494,47 @@ impl PlatformRepository {
}
// libSSH Version => libssh2/1.4.3
- if let Ok(Some(ssh_matches)) = Preg::is_match_strict_groups(
+ let mut ssh_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im",
&info,
- ) {
+ Some(&mut ssh_matches),
+ )
+ .unwrap_or(false)
+ {
+ let ssh_library = ssh_matches
+ .get(&CaptureKey::ByName("library".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let ssh_version = ssh_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
- &format!("{}-{}", name, strtolower(&ssh_matches["library"])),
- Some(&ssh_matches["version"]),
- Some(&format!("curl {} version", &ssh_matches["library"])),
+ &format!("{}-{}", name, strtolower(&ssh_library)),
+ Some(&ssh_version),
+ Some(&format!("curl {} version", &ssh_library)),
&[],
&[],
)?;
}
// ZLib Version => 1.2.8
- if let Ok(Some(zlib_matches)) =
- Preg::is_match_strict_groups("{^ZLib Version => (?<version>.+)$}im", &info)
+ let mut zlib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ "{^ZLib Version => (?<version>.+)$}im",
+ &info,
+ Some(&mut zlib_matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
&format!("{}-zlib", name),
- Some(&zlib_matches["version"]),
+ zlib_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("curl zlib version"),
&[],
&[],
@@ -491,14 +546,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// timelib version => 2018.03
- if let Ok(Some(timelib_matches)) = Preg::is_match_strict_groups(
+ let mut timelib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^timelib version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut timelib_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-timelib", name),
- Some(&timelib_matches["version"]),
+ timelib_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("date timelib version"),
&[],
&[],
@@ -506,21 +567,36 @@ impl PlatformRepository {
}
// Timezone Database => internal
- if let Ok(Some(zoneinfo_source_matches)) = Preg::is_match_strict_groups(
+ let mut zoneinfo_source_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^Timezone Database => (?<source>internal|external)$/im",
&info,
- ) {
- let external = zoneinfo_source_matches["source"] == "external";
- if let Ok(Some(zoneinfo_matches)) = Preg::is_match_strict_groups(
+ Some(&mut zoneinfo_source_matches),
+ )
+ .unwrap_or(false)
+ {
+ let external = zoneinfo_source_matches
+ .get(&CaptureKey::ByName("source".to_string()))
+ .map(|s| s == "external")
+ .unwrap_or(false);
+ let mut zoneinfo_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^\"Olson\" Timezone Database Version => (?<version>.+?)(?:\\.system)?$/im",
&info,
- ) {
+ Some(&mut zoneinfo_matches),
+ )
+ .unwrap_or(false)
+ {
+ let zoneinfo_version = zoneinfo_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
// If the timezonedb is provided by ext/timezonedb, register that version as a replacement
if external && loaded_extensions.iter().any(|n| n == "timezonedb") {
self.add_library(
&mut libraries,
"timezonedb-zoneinfo",
- Some(&zoneinfo_matches["version"]),
+ Some(&zoneinfo_version),
Some(
"zoneinfo (\"Olson\") database for date (replaced by timezonedb)",
),
@@ -531,7 +607,7 @@ impl PlatformRepository {
self.add_library(
&mut libraries,
&format!("{}-zoneinfo", name),
- Some(&zoneinfo_matches["version"]),
+ Some(&zoneinfo_version),
Some("zoneinfo (\"Olson\") database for date"),
&[],
&[],
@@ -545,13 +621,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmagic => 537
- if let Ok(Some(magic_matches)) =
- Preg::is_match_strict_groups("/^libmagic => (?<version>.+)$/im", &info)
+ let mut magic_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^libmagic => (?<version>.+)$/im",
+ &info,
+ Some(&mut magic_matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
&format!("{}-libmagic", name),
- Some(&magic_matches["version"]),
+ magic_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("fileinfo libmagic version"),
&[],
&[],
@@ -576,12 +659,19 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(libjpeg_matches)) = Preg::is_match_strict_groups(
+ let mut libjpeg_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im",
&info,
- ) {
- let parsed =
- Version::parse_libjpeg(&libjpeg_matches["version"]).unwrap_or_default();
+ Some(&mut libjpeg_matches),
+ )
+ .unwrap_or(false)
+ {
+ let libjpeg_version = libjpeg_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let parsed = Version::parse_libjpeg(&libjpeg_version).unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-libjpeg", name),
@@ -592,41 +682,59 @@ impl PlatformRepository {
)?;
}
- if let Ok(Some(libpng_matches)) = Preg::is_match_strict_groups(
+ let mut libpng_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libPNG Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libpng_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpng", name),
- Some(&libpng_matches["version"]),
+ libpng_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libpng version for gd"),
&[],
&[],
)?;
}
- if let Ok(Some(freetype_matches)) = Preg::is_match_strict_groups(
+ let mut freetype_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^FreeType Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut freetype_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-freetype", name),
- Some(&freetype_matches["version"]),
+ freetype_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("freetype version for gd"),
&[],
&[],
)?;
}
- if let Ok(Some(libxpm_matches)) = Preg::is_match_strict_groups(
+ let mut libxpm_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libXpm Version => (?<versionId>\\d+)$/im",
&info,
- ) {
- let version_id: i64 = libxpm_matches["versionId"].parse().unwrap_or(0);
- let converted =
- Version::convert_libxpm_version_id(version_id).unwrap_or_default();
+ Some(&mut libxpm_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_id: i64 = libxpm_matches
+ .get(&CaptureKey::ByName("versionId".to_string()))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let converted = Version::convert_libxpm_version_id(version_id);
self.add_library(
&mut libraries,
&format!("{}-libxpm", name),
@@ -689,27 +797,42 @@ impl PlatformRepository {
&[],
&[],
)?;
- } else if let Ok(Some(matches)) =
- Preg::is_match_strict_groups("/^ICU version => (?<version>.+)$/im", &info)
- {
- self.add_library(
- &mut libraries,
- "icu",
- Some(&matches["version"]),
- Some(description),
- &[],
- &[],
- )?;
+ } else {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^ICU version => (?<version>.+)$/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ self.add_library(
+ &mut libraries,
+ "icu",
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
+ Some(description),
+ &[],
+ &[],
+ )?;
+ }
}
// ICU TZData version => 2019c
- if let Ok(Some(zoneinfo_matches)) = Preg::is_match_strict_groups(
+ let mut zoneinfo_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^ICU TZData version => (?<version>.*)$/im",
&info,
- ) {
- if let Some(parsed) =
- Version::parse_zoneinfo_version(&zoneinfo_matches["version"])
- {
+ Some(&mut zoneinfo_matches),
+ )
+ .unwrap_or(false)
+ {
+ let zi_version = zoneinfo_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ if let Some(parsed) = Version::parse_zoneinfo_version(&zi_version) {
self.add_library(
&mut libraries,
"icu-zoneinfo",
@@ -784,12 +907,19 @@ impl PlatformRepository {
Self::imagick_get_version_string(&image_magick_version);
// 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
// 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^ImageMagick (?<version>[\\d.]+)(?:-(?<patch>\\d+))?/",
&image_magick_version_str,
- ) {
- let mut version_built = matches["version"].clone();
- if let Some(patch) = matches.get("patch") {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ let mut version_built = matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ if let Some(patch) = matches.get(&CaptureKey::ByName("patch".to_string())) {
version_built = format!("{}.{}", version_built, patch);
}
@@ -807,21 +937,35 @@ impl PlatformRepository {
"ldap" => {
let info = self.runtime.get_extension_info(name)?;
- if let (Ok(Some(matches)), Ok(Some(vendor_matches))) = (
- Preg::is_match_strict_groups(
- "/^Vendor Version => (?<versionId>\\d+)$/im",
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ let mut vendor_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ "/^Vendor Version => (?<versionId>\\d+)$/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ && Preg::is_match_strict_groups3(
+ "/^Vendor Name => (?<vendor>.+)$/im",
&info,
- ),
- Preg::is_match_strict_groups("/^Vendor Name => (?<vendor>.+)$/im", &info),
- ) {
- let version_id: i64 = matches["versionId"].parse().unwrap_or(0);
- let converted =
- Version::convert_openldap_version_id(version_id).unwrap_or_default();
+ Some(&mut vendor_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_id: i64 = matches
+ .get(&CaptureKey::ByName("versionId".to_string()))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let converted = Version::convert_openldap_version_id(version_id);
+ let vendor = vendor_matches
+ .get(&CaptureKey::ByName("vendor".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
- &format!("{}-{}", name, strtolower(&vendor_matches["vendor"])),
+ &format!("{}-{}", name, strtolower(&vendor)),
Some(&converted),
- Some(&format!("{} version of ldap", &vendor_matches["vendor"])),
+ Some(&format!("{} version of ldap", vendor)),
&[],
&[],
)?;
@@ -857,14 +1001,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmbfl version => 1.3.2
- if let Ok(Some(libmbfl_matches)) = Preg::is_match_strict_groups(
+ let mut libmbfl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libmbfl version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libmbfl_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmbfl", name),
- Some(&libmbfl_matches["version"]),
+ libmbfl_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("mbstring libmbfl version"),
&[],
&[],
@@ -888,18 +1038,26 @@ impl PlatformRepository {
// Multibyte regex (oniguruma) version => 5.9.5
// oniguruma version => 6.9.0
- } else if let Ok(Some(oniguruma_matches)) = Preg::is_match_strict_groups(
- "/^(?:oniguruma|Multibyte regex \\(oniguruma\\)) version => (?<version>.+)$/im",
- &info,
- ) {
- self.add_library(
- &mut libraries,
- &format!("{}-oniguruma", name),
- Some(&oniguruma_matches["version"]),
- Some("mbstring oniguruma version"),
- &[],
- &[],
- )?;
+ } else {
+ let mut oniguruma_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^(?:oniguruma|Multibyte regex \\(oniguruma\\)) version => (?<version>.+)$/im",
+ &info,
+ Some(&mut oniguruma_matches),
+ )
+ .unwrap_or(false)
+ {
+ self.add_library(
+ &mut libraries,
+ &format!("{}-oniguruma", name),
+ oniguruma_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
+ Some("mbstring oniguruma version"),
+ &[],
+ &[],
+ )?;
+ }
}
}
@@ -907,14 +1065,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmemcached version => 1.0.18
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libmemcached version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmemcached", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libmemcached version"),
&[],
&[],
@@ -929,14 +1093,21 @@ impl PlatformRepository {
_ => "".to_string(),
};
// OpenSSL 1.1.1g 21 Apr 2020
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^(?:OpenSSL|LibreSSL)?\\s*(?<version>\\S+)}i",
&openssl_text_str,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ let version = matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
let mut is_fips = false;
let parsed_version =
- Version::parse_openssl(&matches["version"], &mut is_fips)
- .unwrap_or_default();
+ Version::parse_openssl(&version, &mut is_fips).unwrap_or_default();
let mut provides_list: Vec<String> = Vec::new();
if is_fips {
provides_list.push(name.to_string());
@@ -965,14 +1136,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// PCRE Unicode Version => 12.1.0
- if let Ok(Some(pcre_unicode_matches)) = Preg::is_match_strict_groups(
+ let mut pcre_unicode_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^PCRE Unicode Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut pcre_unicode_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-unicode", name),
- Some(&pcre_unicode_matches["version"]),
+ pcre_unicode_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("PCRE Unicode version support"),
&[],
&[],
@@ -983,14 +1160,20 @@ impl PlatformRepository {
"mysqlnd" | "pdo_mysql" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-mysqlnd", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("mysqlnd library version for {}", name)),
&[],
&[],
@@ -1001,28 +1184,40 @@ impl PlatformRepository {
"mongodb" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(libmongoc_matches)) = Preg::is_match_strict_groups(
+ let mut libmongoc_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libmongoc bundled version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libmongoc_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmongoc", name),
- Some(&libmongoc_matches["version"]),
+ libmongoc_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libmongoc version of mongodb"),
&[],
&[],
)?;
}
- if let Ok(Some(libbson_matches)) = Preg::is_match_strict_groups(
+ let mut libbson_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libbson bundled version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libbson_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libbson", name),
- Some(&libbson_matches["version"]),
+ libbson_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libbson version of mongodb"),
&[],
&[],
@@ -1049,14 +1244,20 @@ impl PlatformRepository {
// intentional fall-through to next case...
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1068,14 +1269,20 @@ impl PlatformRepository {
"pdo_pgsql" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1088,14 +1295,20 @@ impl PlatformRepository {
// Used Library => Compiled => Linked
// libpq => 14.3 (Ubuntu 14.3-1.pgdg22.04+1) => 15.0.2
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libpq => (?<compiled>.+) => (?<linked>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["linked"]),
+ matches
+ .get(&CaptureKey::ByName("linked".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1165,14 +1378,20 @@ impl PlatformRepository {
"sqlite3" | "pdo_sqlite" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^SQLite Library => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-sqlite", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1183,14 +1402,20 @@ impl PlatformRepository {
"ssh2" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libssh2 version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libssh2", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1214,14 +1439,20 @@ impl PlatformRepository {
)?;
let info = self.runtime.get_extension_info("xsl")?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libxslt compiled against libxml Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
"libxslt-libxml",
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libxml version libxslt is compiled against"),
&[],
&[],
@@ -1232,14 +1463,20 @@ impl PlatformRepository {
"yaml" => {
let info = self.runtime.get_extension_info("yaml")?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^LibYAML Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libyaml", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libyaml version of yaml"),
&[],
&[],
@@ -1289,14 +1526,20 @@ impl PlatformRepository {
// Linked Version => 1.2.8
} else {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^Linked Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
name,
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1362,9 +1605,7 @@ impl PlatformRepository {
return Ok(());
}
- let overrider = self
- .inner
- .find_package(package.get_name().to_string(), "*".to_string());
+ let overrider = self.inner.find_package(package.get_name(), "*".to_string());
let actual_text = if let Some(ref ov) = overrider {
if package.get_version() == ov.get_version() {
"same as actual".to_string()
@@ -1475,11 +1716,15 @@ impl PlatformRepository {
Ok(v) => v,
Err(_) => {
extra_description = Some(format!(" (actual version: {})", pretty_version));
- if let Ok(Some(m)) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^(\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?)}",
&pretty_version,
- ) {
- pretty_version = m["1"].clone();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ pretty_version = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
} else {
pretty_version = "0".to_string();
}
@@ -1689,7 +1934,7 @@ impl crate::repository::repository_interface::RepositoryInterface for PlatformRe
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: crate::repository::repository_interface::FindPackageConstraint,
) -> Option<Box<dyn crate::package::base_package::BasePackage>> {
self.inner.find_package(name, constraint)
@@ -1697,7 +1942,7 @@ impl crate::repository::repository_interface::RepositoryInterface for PlatformRe
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
) -> Vec<Box<dyn crate::package::base_package::BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index bb598ed..11e5f61 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -22,7 +22,7 @@ pub struct RepositoryFactory;
impl RepositoryFactory {
pub fn config_from_string(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repository: &str,
allow_filesystem: bool,
) -> anyhow::Result<IndexMap<String, PhpMixed>> {
@@ -41,8 +41,10 @@ impl RepositoryFactory {
if extension == "json" {
let json = JsonFile::new(
repository.to_string(),
- Some(Factory::create_http_downloader(io, config)?),
- Some(io),
+ Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, config, IndexMap::new())?,
+ ))),
+ Some(io.clone_box()),
)?;
let data = json.read()?;
let has_packages = data.get("packages").map_or(false, |v| !v.is_null());
@@ -92,7 +94,7 @@ impl RepositoryFactory {
pub fn from_string(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repository: &str,
allow_filesystem: bool,
rm: Option<&mut RepositoryManager>,
@@ -103,7 +105,7 @@ impl RepositoryFactory {
pub fn create_repo(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repo_config: IndexMap<String, PhpMixed>,
rm: Option<&mut RepositoryManager>,
) -> anyhow::Result<Box<dyn RepositoryInterface>> {
@@ -128,15 +130,15 @@ impl RepositoryFactory {
pub fn default_repos(
io: Option<&dyn IOInterface>,
- config: Option<Config>,
+ config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
rm: Option<&mut RepositoryManager>,
) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
let config = match config {
Some(c) => c,
- None => Factory::create_config(None, None)?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)),
};
if let Some(io) = io {
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
}
let mut owned_rm;
@@ -151,38 +153,50 @@ impl RepositoryFactory {
owned_rm = Self::manager(
io,
&config,
- Some(Factory::create_http_downloader(io, &config)?),
+ Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, &config, IndexMap::new())?,
+ ))),
None,
None,
)?;
&mut owned_rm
};
- let repo_configs = config.get_repositories();
+ let repo_configs = config.borrow().get_repositories();
Self::create_repos(rm, repo_configs)
}
pub fn manager(
io: &dyn IOInterface,
- config: &Config,
- http_downloader: Option<HttpDownloader>,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
event_dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<RepositoryManager> {
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(io, config)?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader(
+ io,
+ config,
+ IndexMap::new(),
+ )?)),
};
let process = match process {
Some(p) => p,
None => {
let mut p = ProcessExecutor::new(io);
p.enable_async();
- p
+ std::rc::Rc::new(std::cell::RefCell::new(p))
}
};
- let mut rm = RepositoryManager::new(io, config, http_downloader, event_dispatcher, process);
+ let mut rm = RepositoryManager::new(
+ io,
+ std::rc::Rc::clone(config),
+ http_downloader,
+ event_dispatcher,
+ Some(process),
+ );
rm.set_repository_class("composer", "Composer\\Repository\\ComposerRepository");
rm.set_repository_class("vcs", "Composer\\Repository\\VcsRepository");
rm.set_repository_class("package", "Composer\\Repository\\PackageRepository");
@@ -205,9 +219,12 @@ impl RepositoryFactory {
pub fn default_repos_with_default_manager(
io: &dyn IOInterface,
) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
- let config = Factory::create_config(Some(io), None)?;
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
+ Some(io),
+ None,
+ )?));
let mut manager = Self::manager(io, &config, None, None, None)?;
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
Self::default_repos(Some(io), Some(config), Some(&mut manager))
}
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 2a9c8f5..6113997 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -12,6 +12,15 @@ pub enum FindPackageConstraint {
Constraint(Box<dyn ConstraintInterface>),
}
+impl Clone for FindPackageConstraint {
+ fn clone(&self) -> Self {
+ match self {
+ Self::String(s) => Self::String(s.clone()),
+ Self::Constraint(c) => Self::Constraint(c.clone_box()),
+ }
+ }
+}
+
pub struct LoadPackagesResult {
pub names_found: Vec<String>,
pub packages: Vec<Box<dyn BasePackage>>,
@@ -44,13 +53,13 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>>;
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>>;
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index cc43aed..a3c5b78 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -14,32 +14,34 @@ use crate::repository::repository_interface::RepositoryInterface;
use crate::util::http_downloader::HttpDownloader;
use crate::util::process_executor::ProcessExecutor;
+#[derive(Debug)]
pub struct RepositoryManager {
local_repository: Option<Box<dyn InstalledRepositoryInterface>>,
repositories: Vec<Box<dyn RepositoryInterface>>,
repository_classes: IndexMap<String, String>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
}
impl RepositoryManager {
pub fn new(
io: &dyn IOInterface,
- config: &Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(io));
+ let process = process
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io))));
Self {
local_repository: None,
repositories: vec![],
repository_classes: IndexMap::new(),
io: io.clone_box(),
- config: config.clone(),
+ config,
http_downloader,
event_dispatcher,
process,
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 6985a18..18c3ba6 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -180,18 +180,17 @@ impl RepositorySet {
.into());
}
- let repos: Vec<Box<dyn RepositoryInterface>> = if let Some(composite) =
- (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
- // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning
- composite
- .get_repositories()
- .iter()
- .map(|r| r.clone_box())
- .collect()
- } else {
- vec![repo]
- };
+ let repos: Vec<Box<dyn RepositoryInterface>> =
+ if let Some(composite) = repo.as_any().downcast_ref::<CompositeRepository>() {
+ // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning
+ composite
+ .get_repositories()
+ .iter()
+ .map(|r| r.clone_box())
+ .collect()
+ } else {
+ vec![repo]
+ };
for repo in repos {
self.repositories.push(repo);
@@ -321,7 +320,7 @@ impl RepositorySet {
let mut map: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new();
for package in packages {
// ignore root alias versions as they are not actual package versions and should not matter when it comes to vulnerabilities
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
if alias.is_root_package_alias() {
continue;
}
@@ -475,10 +474,12 @@ impl RepositorySet {
pool_builder.set_allowed_types(allowed_types);
for repo in &self.repositories {
- let is_installed = (repo.as_any() as &dyn Any)
+ let is_installed = repo
+ .as_any()
.downcast_ref::<dyn InstalledRepositoryInterface>()
.is_some()
- || (repo.as_any() as &dyn Any)
+ || repo
+ .as_any()
.downcast_ref::<InstalledRepository>()
.is_some();
if is_installed && !self.allow_installed_repositories {
@@ -500,10 +501,12 @@ impl RepositorySet {
/// Create a pool for dependency resolution from the packages in this repository set.
pub fn create_pool_with_all_packages(&mut self) -> Result<Pool> {
for repo in &self.repositories {
- let is_installed = (repo.as_any() as &dyn Any)
+ let is_installed = repo
+ .as_any()
.downcast_ref::<dyn InstalledRepositoryInterface>()
.is_some()
- || (repo.as_any() as &dyn Any)
+ || repo
+ .as_any()
.downcast_ref::<InstalledRepository>()
.is_some();
if is_installed && !self.allow_installed_repositories {
@@ -527,12 +530,12 @@ impl RepositorySet {
if let Some(versions) = self.root_aliases.get(&name) {
if let Some(alias) = versions.get(&version) {
- while let Some(alias_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
+ while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>()
{
package = alias_pkg.get_alias_of().clone_box();
}
- let alias_package: Box<dyn BasePackage> = if (package.as_any() as &dyn Any)
+ let alias_package: Box<dyn BasePackage> = if package
+ .as_any()
.downcast_ref::<CompletePackage>()
.is_some()
{
diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs
index d39daa1..0f4f6a8 100644
--- a/crates/shirabe/src/repository/repository_utils.rs
+++ b/crates/shirabe/src/repository/repository_utils.rs
@@ -23,7 +23,7 @@ impl RepositoryUtils {
}
for candidate in packages {
- for name in candidate.get_names() {
+ for name in candidate.get_names(true) {
if requires.contains_key(&name) {
let already_in_bucket = bucket.iter().any(|b| {
std::ptr::eq(
@@ -52,10 +52,8 @@ impl RepositoryUtils {
unwrap_filter_repos: bool,
) -> Vec<Box<dyn RepositoryInterface>> {
let repo: Box<dyn RepositoryInterface> = if unwrap_filter_repos {
- if let Some(filter_repo) =
- (repo.as_any() as &dyn Any).downcast_ref::<FilterRepository>()
- {
- filter_repo.get_repository()
+ if let Some(filter_repo) = repo.as_any().downcast_ref::<FilterRepository>() {
+ filter_repo.get_repository().clone_box()
} else {
repo
}
@@ -63,12 +61,10 @@ impl RepositoryUtils {
repo
};
- if let Some(composite_repo) =
- (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
+ if let Some(composite_repo) = repo.as_any().downcast_ref::<CompositeRepository>() {
let mut repos = Vec::new();
for r in composite_repo.get_repositories() {
- for r2 in Self::flatten_repositories(r, unwrap_filter_repos) {
+ for r2 in Self::flatten_repositories(r.clone_box(), unwrap_filter_repos) {
repos.push(r2);
}
}
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index 6c6e25d..56131f1 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -41,7 +41,7 @@ impl RepositoryInterface for RootPackageRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: crate::repository::repository_interface::FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -49,7 +49,7 @@ impl RepositoryInterface for RootPackageRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
index 2efbfb7..179f2db 100644
--- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs
+++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
@@ -3,7 +3,7 @@
use crate::io::io_interface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, urlencode,
};
@@ -15,6 +15,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::forgejo::Forgejo;
use crate::util::forgejo_repository_data::ForgejoRepositoryData;
use crate::util::forgejo_url::ForgejoUrl;
@@ -39,6 +40,7 @@ impl ForgejoDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -53,6 +55,7 @@ impl ForgejoDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -313,7 +316,7 @@ impl ForgejoDriver {
identifier: &str,
) -> Result<Option<IndexMap<String, PhpMixed>>> {
if let Some(ref mut git_driver) = self.git_driver {
- return git_driver.inner.get_composer_information(identifier);
+ return git_driver.get_composer_information(identifier);
}
if !self.inner.info_cache.contains_key(identifier) {
@@ -321,7 +324,12 @@ impl ForgejoDriver {
if let Some(res) = self.inner.cache.as_ref().and_then(|c| c.read(identifier)) {
JsonFile::parse_json(&res, None)?
} else {
- let c = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let c = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = c {
let encoded = JsonFile::encode_with_options(
@@ -338,7 +346,10 @@ impl ForgejoDriver {
c
}
} else {
- self.inner.get_base_composer_information(identifier)?
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ VcsDriverBase::finish_base_composer_information(identifier, file_content, || {
+ self.get_change_date(identifier)
+ })?
};
let mut composer = composer;
@@ -484,11 +495,11 @@ impl ForgejoDriver {
}
if !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping Forgejo driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -510,7 +521,7 @@ impl ForgejoDriver {
todo!("clone io for GitDriver setup"),
self.inner.config.clone(),
self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.process),
),
tags: None,
branches: None,
@@ -556,8 +567,11 @@ impl ForgejoDriver {
let links = explode(",", &header);
for link in links {
- if let Some(m) = Preg::match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, &link) {
- if let Some(url) = m.get("1") {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, &link, Some(&mut m))
+ .unwrap_or(false)
+ {
+ if let Some(url) = m.get(&CaptureKey::ByIndex(1)) {
return Some(url.clone());
}
}
@@ -650,14 +664,10 @@ impl ForgejoDriver {
Ok(()) => Ok(true),
Err(e) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
- "<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your Forgejo credentials</error>",
- ssh_url
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(&format!(
+ "<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your Forgejo credentials</error>",
+ ssh_url
+ ), true, io_interface::NORMAL);
Err(e)
}
}
diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs
index a765c4c..f0c3468 100644
--- a/crates/shirabe/src/repository/vcs/fossil_driver.rs
+++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs
@@ -29,9 +29,11 @@ impl FossilDriver {
self.check_fossil()?;
// Ensure we are allowed to use this URL by config.
- self.inner
- .config
- .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &self.inner.url,
+ Some(&*self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
// Only if url points to a locally accessible directory, assume it's the checkout directory.
// Otherwise, it should be something fossil can clone from.
@@ -41,6 +43,7 @@ impl FossilDriver {
let cache_repo_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -48,6 +51,7 @@ impl FossilDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -60,7 +64,7 @@ impl FossilDriver {
.into());
}
- let local_name = Preg::replace(r"{[^a-z0-9]}i", "-", self.inner.url.clone());
+ let local_name = Preg::replace(r"{[^a-z0-9]}i", "-", &self.inner.url);
self.repo_file = Some(format!("{}/{}.fossil", cache_repo_dir, local_name));
self.checkout_dir = format!("{}/{}/", cache_vcs_dir, local_name);
@@ -75,7 +79,7 @@ impl FossilDriver {
pub(crate) fn check_fossil(&self) -> anyhow::Result<()> {
let mut ignored_output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "version"].map(|s| s.to_string()).to_vec(),
&mut ignored_output,
None,
@@ -84,7 +88,7 @@ impl FossilDriver {
return Err(RuntimeException {
message: format!(
"fossil was not found, check that it is installed and in your PATH env.\n\n{}",
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
@@ -115,27 +119,23 @@ impl FossilDriver {
// update the repo if it is a valid fossil repository
if is_file(&repo_file)
&& is_dir(&self.checkout_dir)
- && self.inner.process.execute(
+ && self.inner.process.borrow_mut().execute_args(
&["fossil", "info"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.checkout_dir.clone()),
) == 0
{
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "pull"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.checkout_dir.clone()),
) != 0
{
- self.inner.io.write_error(
- PhpMixed::String(format!(
- "<error>Failed to update {}, package information from this repository may be outdated ({})</error>",
- self.inner.url,
- self.inner.process.get_error_output()
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(&format!(
+ "<error>Failed to update {}, package information from this repository may be outdated ({})</error>",
+ self.inner.url,
+ self.inner.process.borrow().get_error_output()
+ ), true, io_interface::NORMAL);
}
} else {
// clean up directory and do a fresh clone into it
@@ -144,7 +144,7 @@ impl FossilDriver {
fs.ensure_directory_exists(&self.checkout_dir)?;
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "clone", "--", &self.inner.url, &repo_file]
.map(|s| s.to_string())
.to_vec(),
@@ -152,7 +152,7 @@ impl FossilDriver {
None,
) != 0
{
- let output = self.inner.process.get_error_output();
+ let output = self.inner.process.borrow().get_error_output();
return Err(RuntimeException {
message: format!(
"Failed to clone {} to repository {}\n\n{}",
@@ -163,7 +163,7 @@ impl FossilDriver {
.into());
}
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "open", "--nested", "--", &repo_file]
.map(|s| s.to_string())
.to_vec(),
@@ -171,7 +171,7 @@ impl FossilDriver {
Some(self.checkout_dir.clone()),
) != 0
{
- let output = self.inner.process.get_error_output();
+ let output = self.inner.process.borrow().get_error_output();
return Err(RuntimeException {
message: format!(
"Failed to open repository {} in {}\n\n{}",
@@ -222,7 +222,7 @@ impl FossilDriver {
}
let mut content = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "cat", "-r", identifier, "--", file]
.map(|s| s.to_string())
.to_vec(),
@@ -239,7 +239,7 @@ impl FossilDriver {
pub fn get_change_date(&self, _identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "finfo", "-b", "-n", "1", "composer.json"]
.map(|s| s.to_string())
.to_vec(),
@@ -257,12 +257,12 @@ impl FossilDriver {
if self.tags.is_none() {
let mut tags: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "tag", "list"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.checkout_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
tags.insert(tag.clone(), tag);
}
self.tags = Some(tags);
@@ -274,13 +274,13 @@ impl FossilDriver {
if self.branches.is_none() {
let mut branches: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "branch", "list"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.checkout_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
- let branch = Preg::replace(r"/^\*/", "", branch.trim().to_string());
+ for branch in self.inner.process.borrow().split_lines(&output) {
+ let branch = Preg::replace(r"/^\*/", "", &branch.trim());
let branch = branch.trim().to_string();
branches.insert(branch.clone(), branch);
}
@@ -312,7 +312,7 @@ impl FossilDriver {
let process = ProcessExecutor::new(io);
let mut output = String::new();
- if process.execute(
+ if process.execute_args(
&["fossil", "info"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(url),
diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
index 05e7c61..689c0e8 100644
--- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_key_exists,
array_search_mixed, extension_loaded, http_build_query_mixed, implode, in_array, is_array,
@@ -58,11 +58,14 @@ pub struct GitBitbucketDriver {
impl GitBitbucketDriver {
/// @inheritDoc
pub fn initialize(&mut self) -> Result<()> {
- let matched = Preg::is_match_strict_groups(
+ let mut m: indexmap::IndexMap<CaptureKey, String> = indexmap::IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^https?://bitbucket\.org/([^/]+)/([^/]+?)(?:\.git|/?)?$#i",
&self.inner.url,
- );
- if matched.is_none() {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
return Err(InvalidArgumentException {
message: sprintf(
"The Bitbucket repository URL %s is invalid. It must be the HTTPS URL of a Bitbucket repository.",
@@ -72,10 +75,9 @@ impl GitBitbucketDriver {
}
.into());
}
- let m = matched.unwrap();
- self.owner = m.get(1).cloned().unwrap_or_default();
- self.repository = m.get(2).cloned().unwrap_or_default();
+ self.owner = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ self.repository = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
self.inner.origin_url = "bitbucket.org".to_string();
self.inner.cache = Some(Cache::new(
&*self.inner.io,
@@ -84,6 +86,7 @@ impl GitBitbucketDriver {
&[
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -98,6 +101,7 @@ impl GitBitbucketDriver {
self.inner.cache.as_mut().unwrap().set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -236,7 +240,12 @@ impl GitBitbucketDriver {
} {
// composer already set above
} else {
- composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
self.inner.cache.as_ref().unwrap().write(
@@ -664,16 +673,17 @@ impl GitBitbucketDriver {
url: &str,
fetching_repo_data: bool,
) -> Result<Response> {
- match self.inner.get_contents(url, false) {
+ match self.inner.get_contents(url) {
Ok(r) => Ok(r),
Err(e) => {
// TODO(phase-b): only handle TransportException
- let bitbucket_util = Bitbucket::new(
- &*self.inner.io,
- &self.inner.config,
- Some(self.inner.process.clone()),
- Some(self.inner.http_downloader.clone()),
- );
+ let mut bitbucket_util = Bitbucket::new(
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ None,
+ )?;
if let Some(te) = e.downcast_ref::<TransportException>() {
let code = te.get_code();
@@ -693,7 +703,7 @@ impl GitBitbucketDriver {
if !self.inner.io.has_authentication(&self.inner.origin_url)
&& bitbucket_util.authorize_oauth(&self.inner.origin_url)
{
- return self.inner.get_contents(url, false);
+ return self.inner.get_contents(url);
}
if !self.inner.io.is_interactive() && fetching_repo_data {
@@ -833,7 +843,9 @@ impl GitBitbucketDriver {
if !Preg::is_match(
r"#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)?$#i",
url,
- ) {
+ )
+ .unwrap_or(false)
+ {
return false;
}
diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs
index 33474b1..07836bf 100644
--- a/crates/shirabe/src/repository/vcs/git_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use chrono::TimeZone;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, RuntimeException, dirname, is_dir, is_writable, realpath,
sys_get_temp_dir,
@@ -32,7 +32,7 @@ impl GitDriver {
pub fn initialize(&mut self) -> anyhow::Result<()> {
let cache_url;
if Filesystem::is_local_path(&self.inner.url) {
- self.inner.url = Preg::replace(r"{[\\/]\.git/?$}", "", self.inner.url.clone())?;
+ self.inner.url = Preg::replace(r"{[\\/]\.git/?$}", "", &self.inner.url)?;
if !is_dir(&self.inner.url) {
return Err(RuntimeException {
message: format!(
@@ -49,6 +49,7 @@ impl GitDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -97,9 +98,9 @@ impl GitDriver {
let git_util = GitUtil::new(
&*self.inner.io,
- &self.inner.config,
- &self.inner.process,
- &Filesystem::new(None),
+ std::rc::Rc::clone(&self.inner.config),
+ std::rc::Rc::clone(&self.inner.process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
);
if !git_util.sync_mirror(&self.inner.url, &self.repo_dir)? {
if !is_dir(&self.repo_dir) {
@@ -112,14 +113,10 @@ impl GitDriver {
}
.into());
}
- self.inner.io.write_error(
- shirabe_php_shim::PhpMixed::String(format!(
- "<error>Failed to update {}, package information from this repository may be outdated</error>",
- self.inner.url
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(shirabe_php_shim::PhpMixed::String(format!(
+ "<error>Failed to update {}, package information from this repository may be outdated</error>",
+ self.inner.url
+ )), true, io_interface::NORMAL);
}
cache_url = self.inner.url.clone();
@@ -131,6 +128,7 @@ impl GitDriver {
let cache_repo_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -147,6 +145,7 @@ impl GitDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -162,9 +161,9 @@ impl GitDriver {
let git_util = GitUtil::new(
&*self.inner.io,
- &self.inner.config,
- &self.inner.process,
- &Filesystem::new(None),
+ std::rc::Rc::clone(&self.inner.config),
+ std::rc::Rc::clone(&self.inner.process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
);
if !Filesystem::is_local_path(&self.inner.url) {
let default_branch =
@@ -176,7 +175,7 @@ impl GitDriver {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"branch".to_string(),
@@ -185,12 +184,15 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- let branches = self.inner.process.split_lines(&output);
+ let branches = self.inner.process.borrow().split_lines(&output);
if !branches.contains(&"* master".to_string()) {
for branch in &branches {
if !branch.is_empty() {
- if let Some(caps) = Preg::match_strict_groups(r"{^\* +(\S+)}", branch) {
- if let Some(name) = caps.get("1") {
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r"{^\* +(\S+)}", branch, Some(&mut caps))
+ .unwrap_or(false)
+ {
+ if let Some(name) = caps.get(&CaptureKey::ByIndex(1)) {
self.root_identifier = Some(name.clone());
break;
}
@@ -236,7 +238,7 @@ impl GitDriver {
}
let mut content = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"show".to_string(),
@@ -274,9 +276,11 @@ impl GitDriver {
],
);
let mut output = String::new();
- self.inner
- .process
- .execute(&command, &mut output, Some(self.repo_dir.clone()));
+ self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(self.repo_dir.clone()),
+ );
let timestamp_str = GitUtil::parse_rev_list_output(&output, &self.inner.process);
let timestamp: i64 = timestamp_str.trim().parse().unwrap_or(0);
@@ -288,7 +292,7 @@ impl GitDriver {
self.tags = Some(IndexMap::new());
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"show-ref".to_string(),
@@ -298,13 +302,20 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
if !tag.is_empty() {
- if let Some(caps) = Preg::match_strict_groups(
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
r"{^([a-f0-9]{40}) refs/tags/(\S+?)(\^\{\})?$}",
&tag,
- ) {
- if let (Some(hash), Some(name)) = (caps.get("1"), caps.get("2")) {
+ Some(&mut caps),
+ )
+ .unwrap_or(false)
+ {
+ if let (Some(hash), Some(name)) = (
+ caps.get(&CaptureKey::ByIndex(1)),
+ caps.get(&CaptureKey::ByIndex(2)),
+ ) {
self.tags
.as_mut()
.unwrap()
@@ -323,7 +334,7 @@ impl GitDriver {
let mut branches = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"branch".to_string(),
@@ -334,15 +345,22 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty()
&& !Preg::is_match(r"{^ *[^/]+/HEAD }", &branch).unwrap_or(false)
{
- if let Some(caps) = Preg::match_strict_groups(
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
r"{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}",
&branch,
- ) {
- if let (Some(name), Some(hash)) = (caps.get("1"), caps.get("2")) {
+ Some(&mut caps),
+ )
+ .unwrap_or(false)
+ {
+ if let (Some(name), Some(hash)) = (
+ caps.get(&CaptureKey::ByIndex(1)),
+ caps.get(&CaptureKey::ByIndex(2)),
+ ) {
if !name.starts_with('-') {
branches.insert(name.clone(), hash.clone());
}
@@ -378,9 +396,9 @@ impl GitDriver {
return Ok(false);
}
- let process = ProcessExecutor::new(io);
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)));
let mut output = String::new();
- if process.execute(
+ if process.borrow_mut().execute_args(
&["git".to_string(), "tag".to_string()],
&mut output,
Some(url.clone()),
@@ -388,15 +406,27 @@ impl GitDriver {
{
return Ok(true);
}
- GitUtil::check_for_repo_ownership_error(&process.get_error_output(), &url);
+ GitUtil::check_for_repo_ownership_error(&process.borrow().get_error_output(), &url);
}
if !deep {
return Ok(false);
}
- let process = ProcessExecutor::new(io);
- let git_util = GitUtil::new(io, _config, &process, &Filesystem::new(None));
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)));
+ // TODO(phase-b): supports() takes &Config; GitUtil now needs Rc<RefCell<Config>>.
+ // Skipping clean Rc construction since we cannot reconstruct one from a borrowed &Config.
+ let _ = _config;
+ return Err(anyhow::anyhow!(
+ "GitDriver::supports requires Rc<RefCell<Config>>: not yet ported"
+ ));
+ #[allow(unreachable_code)]
+ let git_util = GitUtil::new(
+ io.clone_box(),
+ todo!(),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
+ );
GitUtil::clean_env(&process);
let result = git_util.run_commands(
diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs
index 7bf15bf..93bfcdb 100644
--- a/crates/shirabe/src/repository/vcs/github_driver.rs
+++ b/crates/shirabe/src/repository/vcs/github_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, RuntimeException, array_diff, array_key_exists, array_map,
array_search_mixed, base64_decode, basename, count, empty, explode, extension_loaded, in_array,
@@ -18,6 +18,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::github::GitHub;
use crate::util::http::response::Response;
@@ -45,31 +46,43 @@ pub struct GitHubDriver {
impl GitHubDriver {
pub fn initialize(&mut self) -> Result<()> {
- let match_ = match Preg::is_match_strict_groups(
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$#",
&self.inner.url,
- ) {
- Some(m) => m,
- None => {
- return Err(InvalidArgumentException {
- message: sprintf(
- "The GitHub repository URL %s is invalid.",
- &[PhpMixed::String(self.inner.url.clone())],
- ),
- code: 0,
- }
- .into());
+ Some(&mut match_),
+ )
+ .unwrap_or(false)
+ {
+ return Err(InvalidArgumentException {
+ message: sprintf(
+ "The GitHub repository URL %s is invalid.",
+ &[PhpMixed::String(self.inner.url.clone())],
+ ),
+ code: 0,
}
- };
+ .into());
+ }
- self.owner = match_.get(3).cloned().unwrap_or_default();
- self.repository = match_.get(4).cloned().unwrap_or_default();
+ self.owner = match_
+ .get(&CaptureKey::ByIndex(3))
+ .cloned()
+ .unwrap_or_default();
+ self.repository = match_
+ .get(&CaptureKey::ByIndex(4))
+ .cloned()
+ .unwrap_or_default();
self.inner.origin_url = strtolower(
&match_
- .get(1)
+ .get(&CaptureKey::ByIndex(1))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get(2).cloned().unwrap_or_default()),
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default()
+ }),
);
if self.inner.origin_url == "www.github.com" {
self.inner.origin_url = "github.com".to_string();
@@ -80,6 +93,7 @@ impl GitHubDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -95,6 +109,7 @@ impl GitHubDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -111,7 +126,13 @@ impl GitHubDriver {
self.allow_git_fallback = false;
}
- if self.inner.config.get("use-github-api").as_bool() == Some(false)
+ if self
+ .inner
+ .config
+ .borrow_mut()
+ .get("use-github-api")
+ .as_bool()
+ == Some(false)
|| self
.inner
.repo_config
@@ -230,7 +251,12 @@ impl GitHubDriver {
.unwrap_or_default();
JsonFile::parse_json(&res, None)?
} else {
- let composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
@@ -384,7 +410,7 @@ impl GitHubDriver {
] {
let mut options: IndexMap<String, PhpMixed> = IndexMap::new();
options.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false));
- let response = self.inner.http_downloader.get(
+ let response = self.inner.http_downloader.borrow_mut().get(
file_url,
&PhpMixed::Array(options.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
@@ -436,20 +462,26 @@ impl GitHubDriver {
let mut result: Vec<IndexMap<String, PhpMixed>> = vec![];
let mut key: Option<String> = None;
- for line in Preg::split(r"{\r?\n}", &funding) {
+ for line in Preg::split(r"{\r?\n}", &funding).unwrap_or_default() {
let line = trim(&line, None);
- if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*(.+)$}", &line) {
- let g1 = m.get(1).cloned().unwrap_or_default();
- let g2 = m.get(2).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^(\w+)\s*:\s*(.+)$}", &line, Some(&mut m))
+ .unwrap_or(false)
+ {
+ let g1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let g2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if g2 == "[" {
key = Some(g1);
continue;
}
- if let Some(m2) = Preg::is_match_strict_groups(r"{^\[(.*?)\](?:\s*#.*)?$}", &g2) {
- let inner = m2.get(1).cloned().unwrap_or_default();
+ let mut m2: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^\[(.*?)\](?:\s*#.*)?$}", &g2, Some(&mut m2))
+ .unwrap_or(false)
+ {
+ let inner = m2.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
for item in array_map(
|s: &String| trim(s, None),
- &Preg::split(r#"{[\'\"]?\s*,\s*[\'\"]?}"#, &inner),
+ &Preg::split(r#"{[\'\"]?\s*,\s*[\'\"]?}"#, &inner).unwrap_or_default(),
) {
let mut entry = IndexMap::new();
entry.insert("type".to_string(), PhpMixed::String(g1.clone()));
@@ -459,30 +491,40 @@ impl GitHubDriver {
);
result.push(entry);
}
- } else if let Some(m2) =
- Preg::is_match_strict_groups(r"{^([^#].*?)(?:\s+#.*)?$}", &g2)
+ } else if Preg::is_match_strict_groups3(
+ r"{^([^#].*?)(?:\s+#.*)?$}",
+ &g2,
+ Some(&mut m2),
+ )
+ .unwrap_or(false)
{
let mut entry = IndexMap::new();
entry.insert("type".to_string(), PhpMixed::String(g1.clone()));
entry.insert(
"url".to_string(),
PhpMixed::String(trim(
- &m2.get(1).cloned().unwrap_or_default(),
+ &m2.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
Some("\"' "),
)),
);
result.push(entry);
}
key = None;
- } else if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*#\s*$}", &line) {
- key = Some(m.get(1).cloned().unwrap_or_default());
- } else if key.is_some()
- && (Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line).is_some()
- || Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line).is_some())
+ } else if Preg::is_match_strict_groups3(r"{^(\w+)\s*:\s*#\s*$}", &line, Some(&mut m))
+ .unwrap_or(false)
{
- let m = Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line)
- .or_else(|| Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line))
- .unwrap();
+ key = Some(m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default());
+ } else if key.is_some() && {
+ let mut tmp: IndexMap<CaptureKey, String> = IndexMap::new();
+ Preg::is_match_strict_groups3(r"{^-\s*(.+)(?:\s+#.*)?$}", &line, Some(&mut m))
+ .unwrap_or(false)
+ || Preg::is_match_strict_groups3(r"{^(.+),(?:\s*#.*)?$}", &line, Some(&mut tmp))
+ .unwrap_or(false)
+ && {
+ m = tmp;
+ true
+ }
+ } {
let mut entry = IndexMap::new();
entry.insert(
"type".to_string(),
@@ -490,7 +532,10 @@ impl GitHubDriver {
);
entry.insert(
"url".to_string(),
- PhpMixed::String(trim(&m.get(1).cloned().unwrap_or_default(), Some("\"' "))),
+ PhpMixed::String(trim(
+ &m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ Some("\"' "),
+ )),
);
result.push(entry);
} else if key.is_some() && line == "]" {
@@ -623,11 +668,11 @@ impl GitHubDriver {
continue;
}
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<warning>Funding URL {} not in a supported format.</warning>",
item_url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -874,21 +919,31 @@ impl GitHubDriver {
}
pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, _deep: bool) -> bool {
- let matches = match Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^((?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$#",
url,
- ) {
- Some(m) => m,
- None => return false,
- };
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return false;
+ }
let origin_url = matches
- .get(2)
+ .get(&CaptureKey::ByIndex(2))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| matches.get(3).cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ matches
+ .get(&CaptureKey::ByIndex(3))
+ .cloned()
+ .unwrap_or_default()
+ });
if !in_array(
- PhpMixed::String(strtolower(&Preg::replace(r"{^www\.}i", "", origin_url))),
+ PhpMixed::String(strtolower(
+ &Preg::replace(r"{^www\.}i", "", &origin_url).unwrap_or_default(),
+ )),
&config.get("github-domains"),
false,
) {
@@ -896,11 +951,11 @@ impl GitHubDriver {
}
if !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping GitHub driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -945,11 +1000,11 @@ impl GitHubDriver {
Ok(r) => Ok(r),
Err(e) => {
let mut git_hub_util = GitHub::new(
- self.inner.io.as_ref(),
- &self.inner.config,
- &self.inner.process,
- &self.inner.http_downloader,
- );
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ )?;
match e.code {
401 | 404 => {
@@ -1057,11 +1112,11 @@ impl GitHubDriver {
if !self.inner.io.has_authentication(&self.inner.origin_url) {
if !self.inner.io.is_interactive() {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>GitHub API limit exhausted. Failed to get metadata for the {} repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>",
self.inner.url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1083,14 +1138,14 @@ impl GitHubDriver {
let rate_limit = git_hub_util.get_rate_limit(
e.get_headers().map(|h| h.as_slice()).unwrap_or(&[]),
);
- self.inner.io.write_error(
- PhpMixed::String(sprintf(
+ self.inner.io.write_error3(
+ &sprintf(
"<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>",
&[
rate_limit.get("limit").cloned().unwrap_or(PhpMixed::Null),
rate_limit.get("reset").cloned().unwrap_or(PhpMixed::Null),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1206,11 +1261,11 @@ impl GitHubDriver {
Err(setup_err) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your GitHub credentials</error>",
self.generate_ssh_url()
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1233,8 +1288,8 @@ impl GitHubDriver {
repo_config,
self.inner.io.clone(),
self.inner.config.clone(),
- self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.http_downloader),
+ std::rc::Rc::clone(&self.inner.process),
);
git_driver.initialize()?;
self.git_driver = Some(git_driver);
@@ -1249,8 +1304,11 @@ impl GitHubDriver {
let links = explode(",", &header);
for link in &links {
- if let Some(m) = Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) {
- return Some(m.get(1).cloned().unwrap_or_default());
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, link, Some(&mut m))
+ .unwrap_or(false)
+ {
+ return Some(m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default());
}
}
diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
index dea1bf6..e00bbf8 100644
--- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs
+++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_search_mixed,
array_shift, ctype_alnum, empty, explode, extension_loaded, implode, in_array, is_array,
@@ -18,6 +18,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::gitlab::GitLab;
use crate::util::http::response::Response;
use crate::util::http_downloader::HttpDownloader;
@@ -57,30 +58,43 @@ impl GitLabDriver {
///
/// SSH urls use https by default. Set "secure-http": false on the repository config to use http instead.
pub fn initialize(&mut self) -> Result<()> {
- let match_ = match Preg::is_match_strict_groups(Self::URL_REGEX, &self.inner.url) {
- Some(m) => m,
- None => {
- return Err(InvalidArgumentException {
- message: sprintf(
- "The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.",
- &[PhpMixed::String(self.inner.url.clone())],
- ),
- code: 0,
- }
- .into());
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(Self::URL_REGEX, &self.inner.url, Some(&mut match_))
+ .unwrap_or(false)
+ {
+ return Err(InvalidArgumentException {
+ message: sprintf(
+ "The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.",
+ &[PhpMixed::String(self.inner.url.clone())],
+ ),
+ code: 0,
}
- };
+ .into());
+ }
let guessed_domain = match_
- .get("domain")
+ .get(&CaptureKey::ByName("domain".to_string()))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get("domain2").cloned().unwrap_or_default());
- let configured_domains = self.inner.config.get("gitlab-domains");
- let mut url_parts: Vec<String> =
- explode("/", &match_.get("parts").cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByName("domain2".to_string()))
+ .cloned()
+ .unwrap_or_default()
+ });
+ let configured_domains = self.inner.config.borrow_mut().get("gitlab-domains");
+ let mut url_parts: Vec<String> = explode(
+ "/",
+ &match_
+ .get(&CaptureKey::ByName("parts".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ );
- let scheme_match = match_.get("scheme").cloned().unwrap_or_default();
+ let scheme_match = match_
+ .get(&CaptureKey::ByName("scheme".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.scheme = if in_array(
PhpMixed::String(scheme_match.clone()),
&PhpMixed::List(vec![
@@ -101,7 +115,7 @@ impl GitLabDriver {
} else {
"https".to_string()
};
- let port = match_.get("port").cloned();
+ let port = match_.get(&CaptureKey::ByName("port".to_string())).cloned();
let origin = Self::determine_origin(
&configured_domains,
guessed_domain,
@@ -123,7 +137,7 @@ impl GitLabDriver {
};
self.inner.origin_url = origin;
- let protocol_value = self.inner.config.get("gitlab-protocol");
+ let protocol_value = self.inner.config.borrow_mut().get("gitlab-protocol");
if let Some(protocol) = protocol_value
.as_string()
.filter(|_| is_string(&protocol_value))
@@ -161,8 +175,12 @@ impl GitLabDriver {
self.repository = Preg::replace(
r"#(\.git)$#",
"",
- match_.get("repo").cloned().unwrap_or_default(),
- );
+ &match_
+ .get(&CaptureKey::ByName("repo".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ )
+ .unwrap_or_default();
self.inner.cache = Some(Cache::new(
self.inner.io.as_ref(),
@@ -170,6 +188,7 @@ impl GitLabDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -185,6 +204,7 @@ impl GitLabDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -200,7 +220,10 @@ impl GitLabDriver {
/// Mainly useful for tests.
///
/// @internal
- pub fn set_http_downloader(&mut self, http_downloader: HttpDownloader) {
+ pub fn set_http_downloader(
+ &mut self,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ ) {
self.inner.http_downloader = http_downloader;
}
@@ -229,7 +252,12 @@ impl GitLabDriver {
.unwrap_or_default();
JsonFile::parse_json(&res, None)?
} else {
- let composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
@@ -679,11 +707,11 @@ impl GitLabDriver {
Err(e) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your credentials</error>",
url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -721,8 +749,8 @@ impl GitLabDriver {
repo_config,
self.inner.io.clone(),
self.inner.config.clone(),
- self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.http_downloader),
+ std::rc::Rc::clone(&self.inner.process),
);
git_driver.initialize()?;
self.git_driver = Some(git_driver);
@@ -780,11 +808,8 @@ impl GitLabDriver {
}
if !more_than_guest_access {
- self.inner.io.write_error(
- PhpMixed::String(
- "<warning>GitLab token with Guest or Planner only access detected</warning>"
- .to_string(),
- ),
+ self.inner.io.write_error3(
+ "<warning>GitLab token with Guest or Planner only access detected</warning>",
true,
io_interface::NORMAL,
);
@@ -840,11 +865,11 @@ impl GitLabDriver {
}
Err(e) => {
let mut git_lab_util = GitLab::new(
- self.inner.io.as_ref(),
- &self.inner.config,
- &self.inner.process,
- &self.inner.http_downloader,
- );
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ )?;
match e.code {
401 | 404 => {
@@ -882,11 +907,11 @@ impl GitLabDriver {
.unwrap()
.unwrap());
}
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<warning>Failed to download {}/{}:{}</warning>",
self.namespace, self.repository, e.message
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -938,25 +963,39 @@ impl GitLabDriver {
/// Uses the config `gitlab-domains` to see if the driver supports the url for the
/// repository given.
pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, _deep: bool) -> bool {
- let match_ = match Preg::is_match_strict_groups(Self::URL_REGEX, url) {
- Some(m) => m,
- None => return false,
- };
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(Self::URL_REGEX, url, Some(&mut match_)).unwrap_or(false)
+ {
+ return false;
+ }
- let scheme = match_.get("scheme").cloned().unwrap_or_default();
+ let scheme = match_
+ .get(&CaptureKey::ByName("scheme".to_string()))
+ .cloned()
+ .unwrap_or_default();
let guessed_domain = match_
- .get("domain")
+ .get(&CaptureKey::ByName("domain".to_string()))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get("domain2").cloned().unwrap_or_default());
- let mut url_parts: Vec<String> =
- explode("/", &match_.get("parts").cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByName("domain2".to_string()))
+ .cloned()
+ .unwrap_or_default()
+ });
+ let mut url_parts: Vec<String> = explode(
+ "/",
+ &match_
+ .get(&CaptureKey::ByName("parts".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ );
if Self::determine_origin(
&config.get("gitlab-domains"),
guessed_domain,
&mut url_parts,
- match_.get("port").cloned(),
+ match_.get(&CaptureKey::ByName("port".to_string())).cloned(),
)
.is_none()
{
@@ -964,11 +1003,11 @@ impl GitLabDriver {
}
if scheme == "https" && !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping GitLab driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -993,8 +1032,16 @@ impl GitLabDriver {
let links = explode(",", &header);
for link in &links {
- if let Some(match_) = Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) {
- return Some(match_.get(1).cloned().unwrap_or_default());
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, link, Some(&mut match_))
+ .unwrap_or(false)
+ {
+ return Some(
+ match_
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ );
}
}
@@ -1048,7 +1095,7 @@ impl GitLabDriver {
false,
) || (port_number.is_some()
&& in_array(
- PhpMixed::String(Preg::replace(r"{:\d+}", "", guessed_domain.clone())),
+ PhpMixed::String(Preg::replace(r"{:\d+}", "", &guessed_domain)),
configured_domains,
false,
))
diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs
index 68393ed..f7c0c16 100644
--- a/crates/shirabe/src/repository/vcs/hg_driver.rs
+++ b/crates/shirabe/src/repository/vcs/hg_driver.rs
@@ -30,6 +30,7 @@ impl HgDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -58,30 +59,32 @@ impl HgDriver {
}.into());
}
- self.inner
- .config
- .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &self.inner.url,
+ Some(&*self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
- let hg_utils = HgUtils::new(&*self.inner.io, &self.inner.config, &self.inner.process);
+ let hg_utils = HgUtils::new(
+ &*self.inner.io,
+ &*self.inner.config.borrow(),
+ &self.inner.process,
+ );
if is_dir(&self.repo_dir)
- && self.inner.process.execute(
+ && self.inner.process.borrow_mut().execute_args(
&["hg", "summary"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.repo_dir.clone()),
) == 0
{
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["hg", "pull"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.repo_dir.clone()),
) != 0
{
- self.inner.io.write_error(
- format!("<error>Failed to update {}, package information from this repository may be outdated ({})</error>", self.inner.url, self.inner.process.get_error_output()).into(),
- true,
- crate::io::io_interface::NORMAL,
- );
+ self.inner.io.write_error3(format!("<error>Failed to update {}, package information from this repository may be outdated ({})</error>", self.inner.url, self.inner.process.borrow().get_error_output()).into(), true, crate::io::io_interface::NORMAL);
}
} else {
let fs2 = Filesystem::new(None);
@@ -112,14 +115,14 @@ impl HgDriver {
pub fn get_root_identifier(&mut self) -> anyhow::Result<String> {
if self.root_identifier.is_none() {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "tip", "--template", "{node}"]
.map(|s| s.to_string())
.to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- let lines = self.inner.process.split_lines(&output);
+ let lines = self.inner.process.borrow().split_lines(&output);
self.root_identifier = lines.into_iter().next();
}
@@ -163,9 +166,11 @@ impl HgDriver {
file.to_string(),
];
let mut content = String::new();
- self.inner
- .process
- .execute(&resource, &mut content, Some(self.repo_dir.clone()));
+ self.inner.process.borrow_mut().execute_args(
+ &resource,
+ &mut content,
+ Some(self.repo_dir.clone()),
+ );
if content.trim().is_empty() {
return Ok(None);
@@ -187,7 +192,7 @@ impl HgDriver {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"hg",
"log",
@@ -210,12 +215,12 @@ impl HgDriver {
if self.tags.is_none() {
let mut tags: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "tags"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
if !tag.is_empty() {
if let Some(m) = Preg::match_(r"^([^\s]+)\s+\d+:(.*)$", &tag) {
tags.insert(
@@ -239,12 +244,12 @@ impl HgDriver {
let mut bookmarks: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "branches"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty() {
if let Some(m) = Preg::match_(r"^([^\s]+)\s+\d+:([a-f0-9]+)", &branch) {
let name = m.get("1").cloned().unwrap_or_default();
@@ -256,12 +261,12 @@ impl HgDriver {
}
output.clear();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "bookmarks"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty() {
if let Some(m) = Preg::match_(r"^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$", &branch) {
let name = m.get("1").cloned().unwrap_or_default();
@@ -298,7 +303,7 @@ impl HgDriver {
let process = crate::util::process_executor::ProcessExecutor::new(io);
let mut output = String::new();
- if process.execute(
+ if process.execute_args(
&["hg", "summary"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(url),
@@ -314,7 +319,7 @@ impl HgDriver {
let process = crate::util::process_executor::ProcessExecutor::new(io);
let mut ignored = String::new();
- let exit = process.execute(
+ let exit = process.execute_args(
&["hg", "identify", "--", url]
.map(|s| s.to_string())
.to_vec(),
diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs
index ee09dee..e3aa868 100644
--- a/crates/shirabe/src/repository/vcs/perforce_driver.rs
+++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs
@@ -59,6 +59,7 @@ impl PerforceDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 2e649df..8218563 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -3,7 +3,7 @@
use anyhow::Result;
use chrono::{DateTime, TimeZone, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, RuntimeException, array_key_exists,
is_array, max, sprintf, stripos, strrpos, strtr, substr, trim,
@@ -90,6 +90,7 @@ impl SvnDriver {
"{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -102,6 +103,7 @@ impl SvnDriver {
self.inner.cache.as_mut().unwrap().set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -135,7 +137,10 @@ impl SvnDriver {
}
pub(crate) fn should_cache(&self, identifier: &str) -> bool {
- self.inner.cache.is_some() && Preg::is_match(r"{@\d+$}", identifier)
+ self.inner.cache.is_some()
+ && Preg::is_match(r"{@\d+$}", identifier)
+ .unwrap_or(false)
+ .unwrap_or(false)
}
pub fn get_composer_information(
@@ -170,22 +175,30 @@ impl SvnDriver {
}
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
- let composer: Option<IndexMap<String, PhpMixed>> =
- match self.inner.get_base_composer_information(identifier) {
- Ok(c) => c,
- Err(e) => {
- // TODO(phase-b): downcast to TransportException
- let _te: &TransportException = todo!("downcast e to TransportException");
- let message = e.to_string();
- if stripos(&message, "path not found").is_none()
- && stripos(&message, "svn: warning: W160013").is_none()
- {
- return Err(e);
- }
- // remember a not-existent composer.json
- None
+ let base_result =
+ self.get_file_content("composer.json", identifier)
+ .and_then(|file_content| {
+ VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )
+ });
+ let composer: Option<IndexMap<String, PhpMixed>> = match base_result {
+ Ok(c) => c,
+ Err(e) => {
+ // TODO(phase-b): downcast to TransportException
+ let _te: &TransportException = todo!("downcast e to TransportException");
+ let message = e.to_string();
+ if stripos(&message, "path not found").is_none()
+ && stripos(&message, "svn: warning: W160013").is_none()
+ {
+ return Err(e);
}
- };
+ // remember a not-existent composer.json
+ None
+ }
+ };
if self.should_cache(identifier) {
let encoded = JsonFile::encode(
@@ -282,12 +295,17 @@ impl SvnDriver {
vec!["svn".to_string(), "info".to_string()],
&format!("{}{}{}", self.base_url, path, rev),
)?;
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^Last Changed Date: ([^(]+)}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^Last Changed Date: ([^(]+)}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let date_str = m.get(1).cloned().unwrap_or_default();
+ let date_str = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
// PHP: new \DateTimeImmutable($match[1], new \DateTimeZone('UTC'))
return Ok(Utc
.datetime_from_str(date_str.trim(), "%Y-%m-%d %H:%M:%S %z")
@@ -313,15 +331,23 @@ impl SvnDriver {
.unwrap_or_default();
if !output.is_empty() {
let mut last_rev: i64 = 0;
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 =
- m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path =
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
last_rev = rev;
} else {
@@ -360,14 +386,22 @@ impl SvnDriver {
)
.unwrap_or_default();
if !output.is_empty() {
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 = m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
let identifier = self.build_identifier(
&format!("/{}", self.trunk_path.clone().unwrap_or_default()),
@@ -393,15 +427,28 @@ impl SvnDriver {
.unwrap_or_default();
if !output.is_empty() {
let mut last_rev: i64 = 0;
- for line in self.inner.process.split_lines(&trim(&output, None)) {
+ for line in self
+ .inner
+ .process
+ .borrow()
+ .split_lines(&trim(&output, None))
+ {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 =
- m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path =
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
last_rev = rev;
} else {
@@ -426,7 +473,10 @@ impl SvnDriver {
pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, deep: bool) -> bool {
let url = Self::normalize_url(url);
- if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url) {
+ if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url)
+ .unwrap_or(false)
+ .unwrap_or(false)
+ {
return true;
}
@@ -437,7 +487,7 @@ impl SvnDriver {
let mut process = ProcessExecutor::new(io);
let mut ignored_output = String::new();
- let exit = process.execute(
+ let exit = process.execute_args(
&[
"svn".to_string(),
"info".to_string(),
@@ -516,7 +566,7 @@ impl SvnDriver {
message: format!(
"Failed to load {}, svn was not found, check that it is installed and in your PATH env.\n\n{}",
self.inner.url,
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
),
code: 0,
}
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs
index 162792e..e356a6f 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs
@@ -1,5 +1,6 @@
//! ref: composer/src/Composer/Repository/Vcs/VcsDriver.php
+use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{
@@ -23,9 +24,9 @@ pub struct VcsDriverBase {
pub origin_url: String,
pub repo_config: IndexMap<String, PhpMixed>,
pub io: Box<dyn IOInterface>,
- pub config: Config,
- pub process: ProcessExecutor,
- pub http_downloader: HttpDownloader,
+ pub config: std::rc::Rc<std::cell::RefCell<Config>>,
+ pub process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ pub http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
pub info_cache: IndexMap<String, Option<IndexMap<String, PhpMixed>>>,
pub cache: Option<Cache>,
}
@@ -34,9 +35,9 @@ impl VcsDriverBase {
pub fn new(
repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
- process: ProcessExecutor,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
let url = repo_config
.get("url")
@@ -74,7 +75,52 @@ impl VcsDriverBase {
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader.get(url, &options)
+ self.http_downloader.borrow_mut().get(url, &options)
+ }
+
+ // Helper for concrete drivers: produces the same value as the trait default
+ // `get_base_composer_information`, but receives a pre-fetched composer.json
+ // body and a lazy change-date callback. Concrete drivers in the Rust port
+ // wrap `VcsDriverBase` as `self.inner` instead of inheriting from it, so
+ // they cannot dispatch back into a base method that calls `get_file_content`
+ // / `get_change_date` hooks; the caller threads those calls in itself.
+ pub fn finish_base_composer_information(
+ identifier: &str,
+ composer_file_content: Option<String>,
+ change_date: impl FnOnce() -> anyhow::Result<Option<DateTime<Utc>>>,
+ ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> {
+ let content = match composer_file_content {
+ None => return Ok(None),
+ Some(c) if c.is_empty() => return Ok(None),
+ Some(c) => c,
+ };
+
+ let parsed = JsonFile::parse_json(
+ Some(&content),
+ Some(&format!("{}:composer.json", identifier)),
+ )?;
+
+ let array = match parsed {
+ PhpMixed::Array(a) if !a.is_empty() => a,
+ _ => return Ok(None),
+ };
+
+ // PHP arrays own their nested values; the Rust representation wraps them
+ // in Box<PhpMixed>. Unbox the outer level so callers can mutate keys.
+ let mut composer: IndexMap<String, PhpMixed> =
+ array.into_iter().map(|(k, v)| (k, *v)).collect();
+
+ if !composer.contains_key("time")
+ || composer
+ .get("time")
+ .map_or(true, |v| v.as_string().map_or(true, |s| s.is_empty()))
+ {
+ if let Some(d) = change_date()? {
+ composer.insert("time".to_string(), PhpMixed::String(d.to_rfc3339()));
+ }
+ }
+
+ Ok(Some(composer))
}
}
@@ -93,8 +139,7 @@ pub trait VcsDriver: VcsDriverInterface {
fn config_mut(&mut self) -> &mut Config;
fn process(&self) -> &ProcessExecutor;
fn process_mut(&mut self) -> &mut ProcessExecutor;
- fn http_downloader(&self) -> &HttpDownloader;
- fn http_downloader_mut(&mut self) -> &mut HttpDownloader;
+ fn http_downloader(&self) -> &std::rc::Rc<std::cell::RefCell<HttpDownloader>>;
fn info_cache(&self) -> &IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
fn info_cache_mut(&mut self) -> &mut IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
fn cache(&self) -> Option<&Cache>;
@@ -195,7 +240,7 @@ pub trait VcsDriver: VcsDriverInterface {
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader().get(url, &options)
+ self.http_downloader().borrow_mut().get(url, &options)
}
fn cleanup(&self) {}
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
index 5b15c2c..236b9b4 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
@@ -6,7 +6,7 @@ use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
-pub trait VcsDriverInterface {
+pub trait VcsDriverInterface: std::fmt::Debug {
fn initialize(&mut self) -> anyhow::Result<()>;
fn get_composer_information(
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index e53392f..1a511fd 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -45,7 +45,7 @@ pub struct VcsRepository {
/// @var IOInterface
pub(crate) io: Box<dyn IOInterface>,
/// @var Config
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var VersionParser
pub(crate) version_parser: Option<VersionParser>,
/// @var string
@@ -55,9 +55,9 @@ pub struct VcsRepository {
/// @var array<string, mixed>
pub(crate) repo_config: IndexMap<String, PhpMixed>,
/// @var HttpDownloader
- pub(crate) http_downloader: HttpDownloader,
+ pub(crate) http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
/// @var ProcessExecutor
- pub(crate) process_executor: ProcessExecutor,
+ pub(crate) process_executor: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var bool
pub(crate) branch_error_occurred: bool,
/// @var array<string, class-string<VcsDriverInterface>>
@@ -86,10 +86,10 @@ impl VcsRepository {
pub fn new(
mut repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
drivers: Option<IndexMap<String, String>>,
version_cache: Option<Box<dyn VersionCacheInterface>>,
) -> Result<Self> {
@@ -154,8 +154,11 @@ impl VcsRepository {
.to_string();
let is_verbose = io.is_verbose();
let is_very_verbose = io.is_very_verbose();
- let process_executor =
- process.unwrap_or_else(|| ProcessExecutor::new(Some(Box::new(&*io)), None));
+ let process_executor = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ Box::new(&*io),
+ ))))
+ });
Ok(Self {
inner,
@@ -477,7 +480,7 @@ impl VcsRepository {
// broken package, version doesn't match tag
if version_normalized != parsed_tag {
if is_very_verbose {
- if Preg::is_match(r"{(^dev-|[.-]?dev$)}i", &parsed_tag) {
+ if Preg::is_match(r"{(^dev-|[.-]?dev$)}i", &parsed_tag).unwrap_or(false) {
self.io.write_error(&format!(
"<warning>Skipped tag {}, invalid tag name, tags can not use dev prefixes or suffixes</warning>",
tag
@@ -706,7 +709,7 @@ impl VcsRepository {
}
// TODO(phase-b): Box<dyn BasePackage> -> Box<dyn PackageInterface> coercion
self.inner.add_package(
- crate::package::package_interface::PackageInterface::clone_box(&*package),
+ <dyn crate::package::package_interface::PackageInterface>::clone_box(&*package),
)?;
Ok(())
})();
diff --git a/crates/shirabe/src/repository/version_cache_interface.rs b/crates/shirabe/src/repository/version_cache_interface.rs
index ce0b591..6dfeec0 100644
--- a/crates/shirabe/src/repository/version_cache_interface.rs
+++ b/crates/shirabe/src/repository/version_cache_interface.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Repository/VersionCacheInterface.php
-pub trait VersionCacheInterface {
+pub trait VersionCacheInterface: std::fmt::Debug {
// No class implementing this interface exists in Composer's codebase; a plugin may provide
// one, but plugin support is not yet decided. Using () as a placeholder until then.
fn get_version_package(&self, version: &str, identifier: &str) -> ();
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index 793478f..05c6bfa 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -2,7 +2,9 @@
use crate::installer::installation_manager::InstallationManager;
use crate::repository::array_repository::ArrayRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use anyhow::Result;
+use shirabe_php_shim::Countable;
#[derive(Debug)]
pub struct WritableArrayRepository {
@@ -12,6 +14,16 @@ pub struct WritableArrayRepository {
}
impl WritableArrayRepository {
+ pub fn new(
+ packages: Vec<Box<dyn crate::package::package_interface::PackageInterface>>,
+ ) -> Result<Self> {
+ Ok(Self {
+ inner: ArrayRepository::new(packages)?,
+ dev_package_names: Vec::new(),
+ dev_mode: None,
+ })
+ }
+
/// Returns true if dev requirements were installed, false if --no-dev was used, None if yet unknown.
pub fn get_dev_mode(&self) -> Option<bool> {
self.dev_mode
@@ -72,4 +84,12 @@ impl WritableArrayRepository {
// TODO(phase-b): delegate to inner ArrayRepository::get_packages
Vec::new()
}
+
+ pub fn get_repo_name(&self) -> String {
+ self.inner.get_repo_name()
+ }
+
+ pub fn count(&self) -> i64 {
+ Countable::count(&self.inner)
+ }
}
diff --git a/crates/shirabe/src/self_update/versions.rs b/crates/shirabe/src/self_update/versions.rs
index ebc992b..36c6dea 100644
--- a/crates/shirabe/src/self_update/versions.rs
+++ b/crates/shirabe/src/self_update/versions.rs
@@ -12,8 +12,8 @@ use shirabe_php_shim::{
pub struct Versions {
pub channels: Vec<String>,
- http_downloader: HttpDownloader,
- config: Config,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
channel: Option<String>,
versions_data: Option<PhpMixed>,
}
@@ -30,7 +30,10 @@ impl Versions {
pub const CHANNELS: &'static [&'static str] =
&["stable", "preview", "snapshot", "1", "2", "2.2"];
- pub fn new(config: Config, http_downloader: HttpDownloader) -> Self {
+ pub fn new(
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ ) -> Self {
Self {
channels: Self::CHANNELS.iter().map(|s| s.to_string()).collect(),
http_downloader,
@@ -47,7 +50,11 @@ impl Versions {
let channel_file = format!(
"{}/update-channel",
- self.config.get("home").as_string().unwrap_or("")
+ self.config
+ .borrow_mut()
+ .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();
@@ -79,12 +86,16 @@ impl Versions {
let channel_file = format!(
"{}/update-channel",
- self.config.get("home").as_string().unwrap_or("")
+ self.config
+ .borrow_mut()
+ .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
- let stored_channel = if Preg::is_match(r"^\d+$", &channel) {
+ let stored_channel = if Preg::is_match(r"^\d+$", &channel)? {
"stable".to_string()
} else {
channel.clone()
@@ -148,7 +159,7 @@ impl Versions {
fn get_versions_data(&mut self) -> anyhow::Result<PhpMixed> {
if self.versions_data.is_none() {
- let protocol = if self.config.get("disable-tls").as_bool() == Some(true) {
+ let protocol = if self.config.borrow_mut().get("disable-tls").as_bool() == Some(true) {
"http"
} else {
"https"
@@ -156,7 +167,11 @@ impl Versions {
self.versions_data = Some(
self.http_downloader
- .get(&format!("{}://getcomposer.org/versions", protocol))?
+ .borrow_mut()
+ .get(
+ &format!("{}://getcomposer.org/versions", protocol),
+ IndexMap::new(),
+ )?
.decode_json()?,
);
}
diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs
index 384d7af..43e13ea 100644
--- a/crates/shirabe/src/util/auth_helper.rs
+++ b/crates/shirabe/src/util/auth_helper.rs
@@ -17,9 +17,10 @@ use crate::util::bitbucket::Bitbucket;
use crate::util::github::GitHub;
use crate::util::gitlab::GitLab;
+#[derive(Debug)]
pub struct AuthHelper {
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var array<string, string> Map of origins to message displayed
displayed_origin_authentications: IndexMap<String, String>,
/// @var array<string, bool> Map of URLs and whether they already retried with authentication from Bitbucket
@@ -40,7 +41,7 @@ pub enum StoreAuth {
}
impl AuthHelper {
- pub fn new(io: Box<dyn IOInterface>, config: Config) -> Self {
+ pub fn new(io: Box<dyn IOInterface>, config: std::rc::Rc<std::cell::RefCell<Config>>) -> Self {
Self {
io,
config,
@@ -53,7 +54,8 @@ impl AuthHelper {
pub fn store_auth(&self, origin: &str, store_auth: StoreAuth) -> Result<()> {
// TODO(phase-b): config.get_auth_config_source() and ConfigSource methods are stubs
let mut store: Option<()> = None;
- let config_source = self.config.get_auth_config_source();
+ let config = self.config.borrow();
+ let config_source = config.get_auth_config_source();
if matches!(store_auth, StoreAuth::Bool(true)) {
store = Some(());
} else if matches!(store_auth, StoreAuth::Prompt) {
@@ -120,7 +122,7 @@ impl AuthHelper {
) -> Result<PromptAuthResult> {
let mut store_auth: StoreAuth = StoreAuth::Bool(false);
- let github_domains = self.config.get("github-domains");
+ let github_domains = self.config.borrow_mut().get("github-domains");
let github_domain_list = match github_domains.as_array() {
Some(arr) => arr.clone(),
None => IndexMap::new(),
@@ -129,7 +131,7 @@ impl AuthHelper {
.values()
.any(|v| v.as_string() == Some(origin));
- let gitlab_domains = self.config.get("gitlab-domains");
+ let gitlab_domains = self.config.borrow_mut().get("gitlab-domains");
let gitlab_domain_list = match gitlab_domains.as_array() {
Some(arr) => arr.clone(),
None => IndexMap::new(),
@@ -155,7 +157,7 @@ impl AuthHelper {
let sso_url = git_hub_util.get_sso_url(&headers);
message = format!(
"GitHub API token requires SSO authorization. Authorize this token at {}\n",
- sso_url,
+ sso_url.as_deref().unwrap_or(""),
);
self.io.write_error3(&message, true, io_interface::NORMAL);
if !self.io.is_interactive() {
@@ -231,7 +233,7 @@ impl AuthHelper {
if !git_hub_util.authorize_oauth(origin)
&& (!self.io.is_interactive()
- || !git_hub_util.authorize_oauth_interactively(origin, &message))
+ || !git_hub_util.authorize_oauth_interactively(origin, Some(&message))?)
{
return Err(TransportException::new(
format!("Could not authenticate against {}", origin),
@@ -289,8 +291,8 @@ impl AuthHelper {
|| !git_lab_util.authorize_oauth_interactively(
scheme.as_string().unwrap_or(""),
origin,
- &message,
- ))
+ Some(&message),
+ )?)
{
return Err(TransportException::new(
format!("Could not authenticate against {}", origin),
@@ -379,7 +381,8 @@ impl AuthHelper {
)?;
if !bit_bucket_util.authorize_oauth(&origin)
&& (!self.io.is_interactive()
- || !bit_bucket_util.authorize_oauth_interactively(&origin, &message))
+ || !bit_bucket_util
+ .authorize_oauth_interactively(&origin, Some(&message))?)
{
return Err(TransportException::new(
format!("Could not authenticate against {}", origin),
@@ -442,11 +445,8 @@ impl AuthHelper {
.into());
}
- self.io.write_error(
- PhpMixed::String(format!(
- " Authentication required (<info>{}</info>):",
- origin,
- )),
+ self.io.write_error3(
+ &format!(" Authentication required (<info>{}</info>):", origin,),
true,
io_interface::NORMAL,
);
@@ -459,7 +459,7 @@ impl AuthHelper {
);
// PHP: $this->config->get('store-auths') returns 'prompt'|bool
// TODO(phase-b): decode the PhpMixed result into StoreAuth
- store_auth = match self.config.get("store-auths") {
+ store_auth = match self.config.borrow_mut().get("store-auths") {
PhpMixed::Bool(b) => StoreAuth::Bool(b),
PhpMixed::String(ref s) if s == "prompt" => StoreAuth::Prompt,
_ => StoreAuth::Bool(false),
@@ -589,7 +589,7 @@ impl AuthHelper {
}
} else if origin == "github.com" && password == "x-oauth-basic" {
// only add the access_token if it is actually a github API URL
- if Preg::is_match(r"{^https?://api\.github\.com/}", url) {
+ if Preg::is_match(r"{^https?://api\.github\.com/}", url)? {
headers.push(PhpMixed::String(format!(
"Authorization: token {}",
username,
@@ -609,6 +609,7 @@ impl AuthHelper {
PhpMixed::String(origin.to_string()),
&PhpMixed::List(
self.config
+ .borrow_mut()
.get("gitlab-domains")
.as_array()
.map(|a| a.values().cloned().collect())
@@ -668,11 +669,8 @@ impl AuthHelper {
let already_displayed =
self.displayed_origin_authentications.get(origin) == Some(display_message);
if !already_displayed {
- self.io.write_error(
- PhpMixed::String(display_message.clone()),
- true,
- io_interface::DEBUG,
- );
+ self.io
+ .write_error3(display_message, true, io_interface::DEBUG);
self.displayed_origin_authentications
.insert(origin.to_string(), display_message.clone());
}
diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs
index ca9aabd..cd4a9fa 100644
--- a/crates/shirabe/src/util/bitbucket.rs
+++ b/crates/shirabe/src/util/bitbucket.rs
@@ -12,12 +12,16 @@ use crate::io::io_interface::IOInterface;
use crate::util::http_downloader::HttpDownloader;
use crate::util::process_executor::ProcessExecutor;
+fn transport_error_code(err: &anyhow::Error) -> Option<i64> {
+ err.downcast_ref::<TransportException>().map(|te| te.code)
+}
+
#[derive(Debug)]
pub struct Bitbucket {
io: Box<dyn IOInterface>,
- config: Config,
- process: ProcessExecutor,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
token: Option<IndexMap<String, PhpMixed>>,
time: Option<i64>,
}
@@ -28,15 +32,23 @@ impl Bitbucket {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- http_downloader: Option<HttpDownloader>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
time: Option<i64>,
) -> anyhow::Result<Self> {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ io.clone_box(),
+ ))))
+ });
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader(
+ &*io,
+ &config,
+ IndexMap::new(),
+ )?)),
};
Ok(Self {
io,
@@ -64,21 +76,23 @@ impl Bitbucket {
return false;
}
- let mut output = String::new();
- if self.process.execute(
- &[
- "git".to_string(),
- "config".to_string(),
- "bitbucket.accesstoken".to_string(),
- ],
- &mut output,
- None,
- ) == 0
+ let mut output = PhpMixed::Null;
+ if self
+ .process
+ .borrow_mut()
+ .execute(
+ PhpMixed::from(vec!["git", "config", "bitbucket.accesstoken"]),
+ Some(&mut output),
+ None,
+ )
+ .unwrap_or(1)
+ == 0
{
+ let output_str = output.as_string().unwrap_or("").trim().to_string();
self.io.set_authentication(
origin_url.to_string(),
"x-token-auth".to_string(),
- Some(output.trim().to_string()),
+ Some(output_str),
);
return true;
}
@@ -104,68 +118,59 @@ impl Bitbucket {
Box::new(PhpMixed::Bool(false)),
);
options.insert("http".to_string(), Box::new(PhpMixed::Array(http)));
- let options = PhpMixed::Array(options);
+ let options: IndexMap<String, PhpMixed> =
+ options.into_iter().map(|(k, v)| (k, *v)).collect();
let response = match self
.http_downloader
- .get(Self::OAUTH2_ACCESS_TOKEN_URL, &options)
+ .borrow_mut()
+ .get(Self::OAUTH2_ACCESS_TOKEN_URL, options)
{
Ok(r) => r,
Err(te) => {
- if te.code == 400 {
- self.io.write_error(
- PhpMixed::String(
- "<error>Invalid OAuth consumer provided.</error>".to_string(),
- ),
+ let code = transport_error_code(&te).unwrap_or(0);
+ if code == 400 {
+ self.io.write_error3(
+ "<error>Invalid OAuth consumer provided.</error>",
+ true,
+ io_interface::NORMAL,
+ );
+ self.io.write_error3(
+ "This can have three reasons:",
+ true,
+ io_interface::NORMAL,
+ );
+ self.io.write_error3(
+ "1. You are authenticating with a bitbucket username/password combination",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String("This can have three reasons:".to_string()),
+ self.io.write_error3(
+ "2. You are using an OAuth consumer, but didn't configure a (dummy) callback url",
+ true,
+ io_interface::NORMAL,
+ );
+ self.io.write_error3(
+ "3. You are using an OAuth consumer, but didn't configure it as private consumer",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "1. You are authenticating with a bitbucket username/password combination".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String(
- "2. You are using an OAuth consumer, but didn't configure a (dummy) callback url".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String(
- "3. You are using an OAuth consumer, but didn't configure it as private consumer".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
return Ok(false);
}
- if te.code == 403 || te.code == 401 {
- self.io.write_error(
- PhpMixed::String(
- "<error>Invalid OAuth consumer provided.</error>".to_string(),
- ),
+ if code == 403 || code == 401 {
+ self.io.write_error3(
+ "<error>Invalid OAuth consumer provided.</error>",
+ true,
+ io_interface::NORMAL,
+ );
+ self.io.write_error3(
+ "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"".to_string(),
- ),
- true,
- io_interface::NORMAL,
- );
return Ok(false);
}
- return Err(te.into());
+ return Err(te);
}
};
@@ -204,43 +209,31 @@ impl Bitbucket {
message: Option<&str>,
) -> anyhow::Result<bool> {
if let Some(msg) = message {
- self.io.write_error(
- PhpMixed::String(msg.to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(msg, true, io_interface::NORMAL);
}
- let local_auth_config = self.config.get_local_auth_config_source();
+ let config_ref = self.config.borrow();
+ let local_auth_config = config_ref.get_local_auth_config_source();
let url =
"https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/";
- self.io.write_error(
- PhpMixed::String("Follow the instructions here:".to_string()),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String(url.to_string()),
- true,
- io_interface::NORMAL,
- );
- let auth_config_source_name = self.config.get_auth_config_source().get_name();
+ self.io
+ .write_error3("Follow the instructions here:", true, io_interface::NORMAL);
+ self.io.write_error3(url, true, io_interface::NORMAL);
+ let auth_config_source_name = config_ref.get_auth_config_source().get_name();
let local_name_prefix = local_auth_config
.as_ref()
.map(|c| format!("{} OR ", c.get_name()))
.unwrap_or_default();
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"to create a consumer. It will be stored in \"{}\" for future use by Composer.",
local_name_prefix + &auth_config_source_name
- )),
+ ),
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "Ensure you enter a \"Callback URL\" (http://example.com is fine) or it will not be possible to create an Access Token (this callback url will not be used by composer)".to_string(),
- ),
+ self.io.write_error3(
+ "Ensure you enter a \"Callback URL\" (http://example.com is fine) or it will not be possible to create an Access Token (this callback url will not be used by composer)",
true,
io_interface::NORMAL,
);
@@ -262,15 +255,13 @@ impl Bitbucket {
.to_string();
if consumer_key.is_empty() {
- self.io.write_error(
- PhpMixed::String("<warning>No consumer key given, aborting.</warning>".to_string()),
+ self.io.write_error3(
+ "<warning>No consumer key given, aborting.</warning>",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"".to_string(),
- ),
+ self.io.write_error3(
+ "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"",
true,
io_interface::NORMAL,
);
@@ -285,17 +276,13 @@ impl Bitbucket {
.to_string();
if consumer_secret.is_empty() {
- self.io.write_error(
- PhpMixed::String(
- "<warning>No consumer secret given, aborting.</warning>".to_string(),
- ),
+ self.io.write_error3(
+ "<warning>No consumer secret given, aborting.</warning>",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"".to_string(),
- ),
+ self.io.write_error3(
+ "You can also add it manually later by using \"composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>\"",
true,
io_interface::NORMAL,
);
@@ -312,10 +299,15 @@ impl Bitbucket {
return Ok(false);
}
- let use_local =
- store_in_local_auth_config && self.config.get_local_auth_config_source().is_some();
+ let use_local = store_in_local_auth_config
+ && self
+ .config
+ .borrow()
+ .get_local_auth_config_source()
+ .is_some();
if use_local {
- let mut auth_config_source = self.config.get_local_auth_config_source().unwrap();
+ let mut auth_config_source =
+ self.config.borrow().get_local_auth_config_source().unwrap();
self.store_in_auth_config(
&mut *auth_config_source,
origin_url,
@@ -323,7 +315,7 @@ impl Bitbucket {
&consumer_secret,
)?;
} else {
- let mut auth_config_source = self.config.get_auth_config_source();
+ let mut auth_config_source = self.config.borrow().get_auth_config_source();
self.store_in_auth_config(
&mut *auth_config_source,
origin_url,
@@ -333,11 +325,12 @@ impl Bitbucket {
}
self.config
+ .borrow()
.get_auth_config_source()
.remove_config_setting(&format!("http-basic.{}", origin_url))?;
- self.io.write_error(
- PhpMixed::String("<info>Consumer stored successfully.</info>".to_string()),
+ self.io.write_error3(
+ "<info>Consumer stored successfully.</info>",
true,
io_interface::NORMAL,
);
@@ -371,9 +364,14 @@ impl Bitbucket {
return Ok(String::new());
}
- let use_local = self.config.get_local_auth_config_source().is_some();
+ let use_local = self
+ .config
+ .borrow()
+ .get_local_auth_config_source()
+ .is_some();
if use_local {
- let mut auth_config_source = self.config.get_local_auth_config_source().unwrap();
+ let mut auth_config_source =
+ self.config.borrow().get_local_auth_config_source().unwrap();
self.store_in_auth_config(
&mut *auth_config_source,
origin_url,
@@ -381,7 +379,7 @@ impl Bitbucket {
consumer_secret,
)?;
} else {
- let mut auth_config_source = self.config.get_auth_config_source();
+ let mut auth_config_source = self.config.borrow().get_auth_config_source();
self.store_in_auth_config(
&mut *auth_config_source,
origin_url,
@@ -415,6 +413,7 @@ impl Bitbucket {
consumer_secret: &str,
) -> anyhow::Result<()> {
self.config
+ .borrow()
.get_config_source()
.remove_config_setting(&format!("bitbucket-oauth.{}", origin_url))?;
@@ -460,16 +459,19 @@ impl Bitbucket {
Box::new(PhpMixed::Int(t + expires_in)),
);
- self.config.get_auth_config_source().add_config_setting(
- &format!("bitbucket-oauth.{}", origin_url),
- PhpMixed::Array(consumer),
- )?;
+ self.config
+ .borrow()
+ .get_auth_config_source()
+ .add_config_setting(
+ &format!("bitbucket-oauth.{}", origin_url),
+ PhpMixed::Array(consumer),
+ )?;
Ok(())
}
fn get_token_from_config(&mut self, origin_url: &str) -> bool {
- let auth_config = self.config.get("bitbucket-oauth");
+ let auth_config = self.config.borrow_mut().get("bitbucket-oauth");
let auth_map = match auth_config.as_array() {
Some(m) => m.clone(),
diff --git a/crates/shirabe/src/util/config_validator.rs b/crates/shirabe/src/util/config_validator.rs
index 972191a..66fcadb 100644
--- a/crates/shirabe/src/util/config_validator.rs
+++ b/crates/shirabe/src/util/config_validator.rs
@@ -127,13 +127,17 @@ impl ConfigValidator {
let spdx_license = license_validator.get_license_by_identifier(license);
if let Some(spdx_license) = spdx_license {
if spdx_license[3] {
- if Preg::is_match(r"{^[AL]?GPL-[123](\.[01])?\+$}i", license) {
+ if Preg::is_match(r"{^[AL]?GPL-[123](\.[01])?\+$}i", license)
+ .unwrap_or(false)
+ {
warnings.push(format!(
"License \"{}\" is a deprecated SPDX license identifier, use \"{}-or-later\" instead",
license,
license.replace('+', "")
));
- } else if Preg::is_match(r"{^[AL]?GPL-[123](\.[01])?$}i", license) {
+ } else if Preg::is_match(r"{^[AL]?GPL-[123](\.[01])?$}i", license)
+ .unwrap_or(false)
+ {
warnings.push(format!(
"License \"{}\" is a deprecated SPDX license identifier, use \"{}-only\" or \"{}-or-later\" instead",
license, license, license
@@ -154,7 +158,7 @@ impl ConfigValidator {
}
if let Some(PhpMixed::String(name)) = manifest.get("name") {
- if !name.is_empty() && Preg::is_match(r"{[A-Z]}", name) {
+ if !name.is_empty() && Preg::is_match(r"{[A-Z]}", name).unwrap_or(false) {
let suggest_name = Preg::replace(
r"{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}",
r"\1\3-\2\4",
@@ -230,7 +234,7 @@ impl ConfigValidator {
packages.extend(require_dev);
for (package, version) in &packages {
if let PhpMixed::String(version_str) = version.as_ref() {
- if Preg::is_match(r"{#}", version_str) {
+ if Preg::is_match(r"{#}", version_str).unwrap_or(false) {
warnings.push(format!(
"The package \"{}\" is pointing to a commit-ref, this is bad practice and can cause unforeseen issues.",
package
diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs
index ce2abf8..2ab08da 100644
--- a/crates/shirabe/src/util/filesystem.rs
+++ b/crates/shirabe/src/util/filesystem.rs
@@ -21,11 +21,11 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct Filesystem {
- process_executor: Option<ProcessExecutor>,
+ process_executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
}
impl Filesystem {
- pub fn new(executor: Option<ProcessExecutor>) -> Self {
+ pub fn new(executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>) -> Self {
Self {
process_executor: executor,
}
@@ -191,7 +191,7 @@ impl Filesystem {
return Ok(Some(true));
}
- if Preg::is_match("{^(?:[a-z]:)?[/\\\\]+$}i", directory, None).unwrap_or(false) {
+ if Preg::is_match3("{^(?:[a-z]:)?[/\\\\]+$}i", directory, None).unwrap_or(false) {
return Err(RuntimeException {
message: format!("Aborting an attempted deletion of {}, this was probably not intended, if it is a real use case please report it.", directory),
code: 0,
@@ -532,7 +532,7 @@ impl Filesystem {
let mut common_path = to.clone();
while strpos(&format!("{}/", from), &format!("{}/", common_path)) != Some(0)
&& "/" != common_path
- && !Preg::is_match("{^[A-Z]:/?$}i", &common_path, None).unwrap_or(false)
+ && !Preg::is_match3("{^[A-Z]:/?$}i", &common_path, None).unwrap_or(false)
{
common_path = strtr(&dirname(&common_path), "\\", "/");
}
@@ -545,7 +545,7 @@ impl Filesystem {
common_path = format!("{}/", rtrim(&common_path, "/"));
let source_path_depth =
substr_count(&substr(&from, strlen(&common_path) as isize, None), "/");
- let common_path_code = str_repeat("../", source_path_depth);
+ let common_path_code = str_repeat("../", source_path_depth as usize);
// allow top level /foo & /bar dirs to be addressed relatively as this is common in Docker setups
if !prefer_relative && "/" == common_path && source_path_depth > 1 {
@@ -593,7 +593,7 @@ impl Filesystem {
let mut common_path = to.clone();
while strpos(&format!("{}/", from), &format!("{}/", common_path)) != Some(0)
&& "/" != common_path
- && !Preg::is_match("{^[A-Z]:/?$}i", &common_path, None).unwrap_or(false)
+ && !Preg::is_match3("{^[A-Z]:/?$}i", &common_path, None).unwrap_or(false)
&& "." != common_path
{
common_path = strtr(&dirname(&common_path), "\\", "/");
@@ -692,7 +692,7 @@ impl Filesystem {
// extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive:
let mut prefix_match: Vec<String> = vec![];
- if Preg::is_match_strict_groups(
+ if Preg::is_match_strict_groups3(
"{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix",
&path,
Some(&mut prefix_match),
@@ -734,7 +734,7 @@ impl Filesystem {
/// And other possible unforeseen disasters, see https://github.com/composer/composer/pull/9422
pub fn trim_trailing_slash(path: &str) -> String {
let mut path = path.to_string();
- if !Preg::is_match("{^[/\\\\]+$}", &path, None).unwrap_or(false) {
+ if !Preg::is_match3("{^[/\\\\]+$}", &path, None).unwrap_or(false) {
path = rtrim(&path, "/\\");
}
@@ -746,7 +746,7 @@ impl Filesystem {
// on windows, \\foo indicates network paths so we exclude those from local paths, however it is unsafe
// on linux as file:////foo (which would be a network path \\foo on windows) will resolve to /foo which could be a local path
if Platform::is_windows() {
- return Preg::is_match(
+ return Preg::is_match3(
"{^(file://(?!//)|/(?!/)|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i",
path,
None,
@@ -754,7 +754,7 @@ impl Filesystem {
.unwrap_or(false);
}
- Preg::is_match(
+ Preg::is_match3(
"{^(file://|/|/?[a-z]:[\\\\/]|\\.\\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i",
path,
None,
@@ -809,12 +809,14 @@ impl Filesystem {
size
}
- pub(crate) fn get_process(&mut self) -> &mut ProcessExecutor {
+ pub(crate) fn get_process(&mut self) -> std::cell::RefMut<'_, ProcessExecutor> {
if self.process_executor.is_none() {
- self.process_executor = Some(ProcessExecutor::new(None));
+ self.process_executor = Some(std::rc::Rc::new(std::cell::RefCell::new(
+ ProcessExecutor::new(None),
+ )));
}
- self.process_executor.as_mut().unwrap()
+ self.process_executor.as_ref().unwrap().borrow_mut()
}
/// delete symbolic link implementation (commonly known as "unlink()")
@@ -960,7 +962,7 @@ impl Filesystem {
let stat = lstat(junction);
// S_ISDIR test (S_IFDIR is 0x4000, S_IFMT is 0xF000 bitmask)
- if let Some(arr) = stat.as_array() {
+ if let Some(arr) = stat {
let mode = arr.get("mode").and_then(|v| v.as_int()).unwrap_or(0);
return 0x4000 != (mode & 0xF000);
}
diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs
index 3aaff91..88df409 100644
--- a/crates/shirabe/src/util/forgejo.rs
+++ b/crates/shirabe/src/util/forgejo.rs
@@ -9,12 +9,16 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct Forgejo {
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
}
impl Forgejo {
- pub fn new(io: Box<dyn IOInterface>, config: Config, http_downloader: HttpDownloader) -> Self {
+ pub fn new(
+ io: Box<dyn IOInterface>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ ) -> Self {
Self {
io,
config,
@@ -39,7 +43,7 @@ impl Forgejo {
io_interface::NORMAL,
);
self.io.write_error3(&url, true, io_interface::NORMAL);
- let local_auth_config = self.config.get_local_auth_config_source();
+ let local_auth_config = self.config.borrow().get_local_auth_config_source();
self.io.write_error3(
&format!(
"Tokens will be stored in plain text in \"{}\" for future use by Composer.",
@@ -47,7 +51,7 @@ impl Forgejo {
.as_ref()
.map(|s| format!("{} OR ", s.get_name()))
.unwrap_or_default()
- + self.config.get_auth_config_source().get_name()
+ + self.config.borrow().get_auth_config_source().get_name()
),
true,
io_interface::NORMAL,
@@ -92,7 +96,7 @@ impl Forgejo {
self.io
.set_authentication(origin_url.to_string(), username.clone(), token.clone());
- match self.http_downloader.get(
+ match self.http_downloader.borrow_mut().get(
&format!("https://{}/api/v1/version", origin_url),
indexmap::indexmap! {
"retry-auth-failure".to_string() => false.into(),
@@ -117,15 +121,16 @@ impl Forgejo {
}
// store value in local/user config
- let local_auth_config = self.config.get_local_auth_config_source();
+ let local_auth_config = self.config.borrow().get_local_auth_config_source();
let auth_config_source = if store_in_local_auth_config {
local_auth_config
.as_ref()
- .unwrap_or_else(|| self.config.get_auth_config_source())
+ .unwrap_or_else(|| self.config.borrow().get_auth_config_source())
} else {
- self.config.get_auth_config_source()
+ self.config.borrow().get_auth_config_source()
};
self.config
+ .borrow()
.get_config_source()
.remove_config_setting(&format!("forgejo-token.{}", origin_url));
auth_config_source.add_config_setting(
diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs
index 8fe66a4..3093734 100644
--- a/crates/shirabe/src/util/git.rs
+++ b/crates/shirabe/src/util/git.rs
@@ -5,7 +5,7 @@ use anyhow::Result;
use indexmap::IndexMap;
use std::sync::Mutex;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, PHP_EOL, PhpMixed, RuntimeException, array_map,
array_merge_recursive, clearstatcache, count, explode, implode, in_array, is_array,
@@ -28,10 +28,10 @@ use crate::util::url::Url;
#[derive(Debug)]
pub struct Git {
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
- pub(crate) process: ProcessExecutor,
- pub(crate) filesystem: Filesystem,
- pub(crate) http_downloader: Option<HttpDownloader>,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
+ pub(crate) http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
}
/// @var string|false|null
@@ -40,9 +40,9 @@ static VERSION: Mutex<Option<Option<String>>> = Mutex::new(None);
impl Git {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: ProcessExecutor,
- fs: Filesystem,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ fs: std::rc::Rc<std::cell::RefCell<Filesystem>>,
) -> Self {
Self {
io,
@@ -73,8 +73,8 @@ impl Git {
.into());
}
Some(io) => {
- io.write_error(
- PhpMixed::String(format!("<warning>{}</warning>", msg)),
+ io.write_error3(
+ &format!("<warning>{}</warning>", msg),
true,
io_interface::NORMAL,
);
@@ -84,7 +84,10 @@ impl Git {
Ok(())
}
- pub fn set_http_downloader(&mut self, http_downloader: HttpDownloader) {
+ pub fn set_http_downloader(
+ &mut self,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ ) {
self.http_downloader = Some(http_downloader);
}
@@ -114,7 +117,7 @@ impl Git {
map.insert("%url%".to_string(), url.to_string());
map.insert(
"%sanitizedUrl%".to_string(),
- Preg::replace(r"{://([^@]+?):(.+?)@}", "://", url.to_string()),
+ Preg::replace(r"{://([^@]+?):(.+?)@}", "://", &url),
);
array_map(
@@ -144,8 +147,11 @@ impl Git {
let mut last_command: PhpMixed = PhpMixed::String(String::new());
// Ensure we are allowed to use this URL by config
- self.config
- .prohibit_url_by_config(url, Some(self.io.as_ref()), &IndexMap::new())?;
+ self.config.borrow_mut().prohibit_url_by_config(
+ url,
+ Some(self.io.as_ref()),
+ &IndexMap::new(),
+ )?;
let orig_cwd: Option<String> = if initial_clone {
cwd.map(|s| s.to_string())
@@ -184,7 +190,7 @@ impl Git {
} else {
cwd_string.clone()
};
- status = this_process.execute(&cmd, &mut local_output, exec_cwd);
+ status = this_process.execute_args(&cmd, &mut local_output, exec_cwd);
if collect_outputs {
outputs.push(local_output);
}
@@ -217,36 +223,46 @@ impl Git {
if !initial_clone {
// capture username/password from URL if there is one and we have no auth configured yet
let mut output = String::new();
- self.process.execute(
+ self.process.borrow_mut().execute_args(
&vec!["git".to_string(), "remote".to_string(), "-v".to_string()],
&mut output,
cwd.map(|s| s.to_string()),
);
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im",
&output,
- ) {
- let m3 = m.get(3).cloned().unwrap_or_default();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let m3 = m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default();
if !self.io.has_authentication(&m3) {
self.io.set_authentication(
m3.clone(),
- rawurldecode(&m.get(1).cloned().unwrap_or_default()),
- Some(rawurldecode(&m.get(2).cloned().unwrap_or_default())),
+ rawurldecode(&m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default()),
+ Some(rawurldecode(
+ &m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ )),
);
}
}
}
- let protocols = self.config.get("github-protocols");
+ let protocols = self.config.borrow_mut().get("github-protocols");
// public github, autoswitch protocols
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
- if let Some(m) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
&format!(
"{{^(?:https?|git)://{}/(.*)}}",
- Self::get_github_domains_regex(&self.config)
+ Self::get_github_domains_regex(&*self.config.borrow())
),
url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
let mut messages: Vec<String> = vec![];
let protocols_list: Vec<String> = match &protocols {
PhpMixed::List(l) => l
@@ -256,8 +272,8 @@ impl Git {
_ => vec![],
};
for protocol in &protocols_list {
- let m1 = m.get(1).cloned().unwrap_or_default();
- let m2 = m.get(2).cloned().unwrap_or_default();
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
let proto_url = if protocol == "ssh" {
format!("git@{}:{}", m1, m2)
} else {
@@ -266,7 +282,7 @@ impl Git {
if run_commands_inline(
&proto_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -276,12 +292,17 @@ impl Git {
messages.push(format!(
"- {}\n{}",
proto_url,
- Preg::replace(r"#^#m", " ", self.process.get_error_output().to_string())
+ Preg::replace(
+ r"#^#m",
+ " ",
+ &self.process.borrow().get_error_output().to_string()
+ )
+ .unwrap_or_default()
));
if initial_clone {
if let Some(ref orig) = orig_cwd {
- self.filesystem.remove_directory(orig);
+ self.filesystem.borrow_mut().remove_directory(orig);
}
}
}
@@ -302,7 +323,7 @@ impl Git {
}
// if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https
- let protocols_list: Vec<String> = match self.config.get("github-protocols") {
+ let protocols_list: Vec<String> = match self.config.borrow_mut().get("github-protocols") {
PhpMixed::List(l) => l
.iter()
.filter_map(|v| v.as_string().map(|s| s.to_string()))
@@ -312,7 +333,7 @@ impl Git {
let bypass_ssh_for_github = Preg::is_match(
&format!(
"{{^git@{}:(.+?)\\.git$}}i",
- Self::get_github_domains_regex(&self.config)
+ Self::get_github_domains_regex(&*self.config.borrow())
),
url,
)
@@ -333,40 +354,43 @@ impl Git {
if bypass_ssh_for_github
|| 0 != run_commands_inline(
url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
)
{
- let mut error_msg = self.process.get_error_output().to_string();
+ let mut error_msg = self.process.borrow().get_error_output().to_string();
// private github repository without ssh key access, try https with auth
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
- let github_ssh_match = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ let github_matched = Preg::is_match_strict_groups3(
&format!(
"{{^git@{}:(.+?)\\.git$}}i",
- Self::get_github_domains_regex(&self.config)
+ Self::get_github_domains_regex(&*self.config.borrow())
),
url,
- );
- let github_https_match = Preg::is_match_strict_groups(
- &format!(
- "{{^https?://{}/(.*?)(?:\\.git)?$}}i",
- Self::get_github_domains_regex(&self.config)
- ),
- url,
- );
- if let Some(m) = github_ssh_match.or(github_https_match) {
- let m1 = m.get(1).cloned().unwrap_or_default();
- let m2 = m.get(2).cloned().unwrap_or_default();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ || Preg::is_match_strict_groups3(
+ &format!(
+ "{{^https?://{}/(.*?)(?:\\.git)?$}}i",
+ Self::get_github_domains_regex(&*self.config.borrow())
+ ),
+ url,
+ Some(&mut m),
+ )
+ .unwrap_or(false);
+ if github_matched {
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if !self.io.has_authentication(&m1) {
let mut git_hub_util = GitHub::new(
- self.io.as_ref(),
- &self.config,
- &self.process,
- self.http_downloader
- .as_ref()
- .unwrap_or(&HttpDownloader::default()),
- );
+ self.io.clone_box(),
+ std::rc::Rc::clone(&self.config),
+ Some(std::rc::Rc::clone(&self.process)),
+ self.http_downloader.clone(),
+ )?;
let message = "Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos";
if !git_hub_util.authorize_oauth(&m1) && self.io.is_interactive() {
@@ -396,7 +420,7 @@ impl Git {
);
if run_commands_inline(
&auth_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -405,27 +429,35 @@ impl Git {
}
credentials = vec![rawurlencode(&username), rawurlencode(&password)];
- error_msg = self.process.get_error_output().to_string();
+ error_msg = self.process.borrow().get_error_output().to_string();
}
- } else if let Some(m) = Preg::is_match_strict_groups(
- r"{^(https?)://(bitbucket\.org)/(.*?)(?:\.git)?$}i",
- url,
- )
- .or_else(|| {
- Preg::is_match_strict_groups(r"{^(git)@(bitbucket\.org):(.+?\.git)$}i", url)
- }) {
+ } else if {
+ let bb_matched = Preg::is_match_strict_groups3(
+ r"{^(https?)://(bitbucket\.org)/(.*?)(?:\.git)?$}i",
+ url,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ || Preg::is_match_strict_groups3(
+ r"{^(git)@(bitbucket\.org):(.+?\.git)$}i",
+ url,
+ Some(&mut m),
+ )
+ .unwrap_or(false);
+ bb_matched
+ } {
// bitbucket either through oauth or app password, with fallback to ssh.
let mut bitbucket_util = Bitbucket::new(
- self.io.as_ref(),
- &self.config,
- &self.process,
- self.http_downloader
- .as_ref()
- .unwrap_or(&HttpDownloader::default()),
- );
+ self.io.clone_box(),
+ std::rc::Rc::clone(&self.config),
+ Some(std::rc::Rc::clone(&self.process)),
+ self.http_downloader.clone(),
+ None,
+ )?;
- let domain = m.get(2).cloned().unwrap_or_default();
- let mut repo_with_git_part = m.get(3).cloned().unwrap_or_default();
+ let domain = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
+ let mut repo_with_git_part =
+ m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default();
if !str_ends_with(&repo_with_git_part, ".git") {
repo_with_git_part.push_str(".git");
}
@@ -476,7 +508,7 @@ impl Git {
if run_commands_inline(
&auth_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -523,7 +555,7 @@ impl Git {
);
if run_commands_inline(
&auth_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -535,17 +567,14 @@ impl Git {
}
// Falling back to ssh
let ssh_url = format!("git@bitbucket.org:{}", repo_with_git_part);
- self.io.write_error(
- PhpMixed::String(
- " No bitbucket authentication configured. Falling back to ssh."
- .to_string(),
- ),
+ self.io.write_error3(
+ " No bitbucket authentication configured. Falling back to ssh.",
true,
io_interface::NORMAL,
);
if run_commands_inline(
&ssh_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -553,39 +582,42 @@ impl Git {
return Ok(());
}
- error_msg = self.process.get_error_output().to_string();
- } else if let Some(m) = Preg::is_match_strict_groups(
- &format!(
- "{{^(git)@{}:(.+?\\.git)$}}i",
- Self::get_gitlab_domains_regex(&self.config)
- ),
- url,
- )
- .or_else(|| {
- Preg::is_match_strict_groups(
+ error_msg = self.process.borrow().get_error_output().to_string();
+ } else if {
+ let gl_matched = Preg::is_match_strict_groups3(
&format!(
- "{{^(https?)://{}/(.*)}}i",
- Self::get_gitlab_domains_regex(&self.config)
+ "{{^(git)@{}:(.+?\\.git)$}}i",
+ Self::get_gitlab_domains_regex(&*self.config.borrow())
),
url,
+ Some(&mut m),
)
- }) {
- let mut m1 = m.get(1).cloned().unwrap_or_default();
- let m2 = m.get(2).cloned().unwrap_or_default();
- let m3 = m.get(3).cloned().unwrap_or_default();
+ .unwrap_or(false)
+ || Preg::is_match_strict_groups3(
+ &format!(
+ "{{^(https?)://{}/(.*)}}i",
+ Self::get_gitlab_domains_regex(&*self.config.borrow())
+ ),
+ url,
+ Some(&mut m),
+ )
+ .unwrap_or(false);
+ gl_matched
+ } {
+ let mut m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
+ let m3 = m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default();
if m1 == "git" {
m1 = "https".to_string();
}
if !self.io.has_authentication(&m2) {
let mut git_lab_util = GitLab::new(
- self.io.as_ref(),
- &self.config,
- &self.process,
- self.http_downloader
- .as_ref()
- .unwrap_or(&HttpDownloader::default()),
- );
+ self.io.clone_box(),
+ std::rc::Rc::clone(&self.config),
+ Some(std::rc::Rc::clone(&self.process)),
+ self.http_downloader.clone(),
+ )?;
let message =
"Cloning failed, enter your GitLab credentials to access private repos";
@@ -635,7 +667,7 @@ impl Git {
if run_commands_inline(
&auth_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
@@ -644,13 +676,13 @@ impl Git {
}
credentials = vec![rawurlencode(&username), rawurlencode(&password)];
- error_msg = self.process.get_error_output().to_string();
+ error_msg = self.process.borrow().get_error_output().to_string();
}
} else if let Some(m) = self.get_authentication_failure(url) {
// private non-github/gitlab/bitbucket repo that failed to authenticate
- let mut m1 = m.get(1).cloned().unwrap_or_default();
- let mut m2 = m.get(2).cloned().unwrap_or_default();
- let m3 = m.get(3).cloned().unwrap_or_default();
+ let mut m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let mut m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
+ let m3 = m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default();
let mut auth_parts: Option<String> = None;
if str_contains(&m2, "@") {
let parts = explode("@", &m2);
@@ -674,16 +706,13 @@ impl Git {
}
}
- self.io.write_error(
- PhpMixed::String(format!(
- " Authentication required (<info>{}</info>):",
- m2
- )),
+ self.io.write_error3(
+ &format!(" Authentication required (<info>{}</info>):", m2),
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(format!("<warning>{}</warning>", trim(&error_msg, None))),
+ self.io.write_error3(
+ &format!("<warning>{}</warning>", trim(&error_msg, None)),
true,
io_interface::VERBOSE,
);
@@ -706,7 +735,7 @@ impl Git {
self.io.ask_and_hide_answer(" Password: ".to_string()),
);
auth = Some(auth_map);
- store_auth = self.config.get("store-auths");
+ store_auth = self.config.borrow_mut().get("store-auths");
}
if let Some(auth_inner) = auth.as_ref() {
@@ -731,27 +760,28 @@ impl Git {
if run_commands_inline(
&auth_url,
- &mut self.process,
+ &mut *self.process.borrow_mut(),
&mut last_command,
command_output.as_deref_mut(),
) == 0
{
self.io
.set_authentication(m2.clone(), username, Some(password));
- let mut auth_helper = AuthHelper::new(self.io.as_ref(), &self.config);
+ let mut auth_helper =
+ AuthHelper::new(self.io.clone_box(), std::rc::Rc::clone(&self.config));
auth_helper.store_auth(&m2, &store_auth);
return Ok(());
}
credentials = vec![rawurlencode(&username), rawurlencode(&password)];
- error_msg = self.process.get_error_output().to_string();
+ error_msg = self.process.borrow().get_error_output().to_string();
}
}
if initial_clone {
if let Some(ref orig) = orig_cwd {
- self.filesystem.remove_directory(orig);
+ self.filesystem.borrow_mut().remove_directory(orig);
}
}
@@ -765,7 +795,7 @@ impl Git {
}
_ => last_command.as_string().unwrap_or("").to_string(),
};
- let mut error_msg = self.process.get_error_output().to_string();
+ let mut error_msg = self.process.borrow().get_error_output().to_string();
if (credentials.len() as i64) > 0 {
last_command_str = self.mask_credentials(&last_command_str, &credentials);
error_msg = self.mask_credentials(&error_msg, &credentials);
@@ -787,11 +817,11 @@ impl Git {
.unwrap_or(false)
&& composer_disable_network.as_deref() != Some("prime")
{
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"<warning>Aborting git mirror sync of {} as network is disabled</warning>",
url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -802,7 +832,7 @@ impl Git {
// update the repo if it is a valid git repository
let mut output = String::new();
if is_dir(dir)
- && self.process.execute(
+ && self.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"rev-parse".to_string(),
@@ -855,8 +885,8 @@ impl Git {
);
if let Err(e) = try_result {
- self.io.write_error(
- PhpMixed::String(format!("<error>Sync mirror failed: {}</error>", e)),
+ self.io.write_error3(
+ &format!("<error>Sync mirror failed: {}</error>", e),
true,
io_interface::DEBUG,
);
@@ -866,10 +896,10 @@ impl Git {
return Ok(true);
}
- Self::check_for_repo_ownership_error(self.process.get_error_output(), dir, None)?;
+ Self::check_for_repo_ownership_error(self.process.borrow().get_error_output(), dir, None)?;
// clean up directory and do a fresh clone into it
- self.filesystem.remove_directory(dir);
+ self.filesystem.borrow_mut().remove_directory(dir);
self.run_commands(
vec![vec![
@@ -915,15 +945,12 @@ impl Git {
if Preg::is_match(r"{^[a-f0-9]{40}$}", r#ref).unwrap_or(false)
&& pretty_version.is_some()
{
- let branch = Preg::replace(
- r"{(?:^dev-|(?:\.x)?-dev$)}i",
- "",
- pretty_version.unwrap().to_string(),
- );
+ let branch =
+ Preg::replace(r"{(?:^dev-|(?:\.x)?-dev$)}i", "", &pretty_version.unwrap());
let mut branches: Option<String> = None;
let mut tags: Option<String> = None;
let mut output = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&vec!["git".to_string(), "branch".to_string()],
&mut output,
Some(dir.to_string()),
@@ -932,7 +959,7 @@ impl Git {
branches = Some(output);
}
let mut output = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&vec!["git".to_string(), "tag".to_string()],
&mut output,
Some(dir.to_string()),
@@ -972,7 +999,9 @@ impl Git {
Ok(false)
}
- pub fn get_no_show_signature_flag(process: &ProcessExecutor) -> String {
+ pub fn get_no_show_signature_flag(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> String {
let git_version = Self::get_version(process);
if let Some(v) = git_version {
if version_compare(&v, "2.10.0-rc0", ">=") {
@@ -984,7 +1013,9 @@ impl Git {
}
/// @return list<string>
- pub fn get_no_show_signature_flags(process: &ProcessExecutor) -> Vec<String> {
+ pub fn get_no_show_signature_flags(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> Vec<String> {
let flags = Self::get_no_show_signature_flag(process);
if flags.is_empty() {
return vec![];
@@ -996,7 +1027,9 @@ impl Git {
/// Checks if git version supports --no-commit-header flag (git 2.33+)
///
/// @internal
- pub fn supports_no_commit_header_flag(process: &ProcessExecutor) -> bool {
+ pub fn supports_no_commit_header_flag(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> bool {
let git_version = Self::get_version(process);
git_version
@@ -1010,7 +1043,7 @@ impl Git {
/// @param list<string> $arguments Additional arguments for git rev-list
/// @return non-empty-list<string>
pub fn build_rev_list_command(
- process: &ProcessExecutor,
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
arguments: Vec<String>,
) -> Vec<String> {
let mut command = vec!["git".to_string(), "rev-list".to_string()];
@@ -1028,20 +1061,23 @@ impl Git {
/// "commit <hash>" before formatted output. This removes those lines.
///
/// @internal
- pub fn parse_rev_list_output(output: &str, process: &ProcessExecutor) -> String {
+ pub fn parse_rev_list_output(
+ output: &str,
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> String {
// If git supports --no-commit-header, output is already clean
if Self::supports_no_commit_header_flag(process) {
return output.to_string();
}
// Filter out "commit <hash>" lines for older git versions
- Preg::replace(r"{^commit [a-f0-9]{40}\n?}m", "", output.to_string())
+ Preg::replace(r"{^commit [a-f0-9]{40}\n?}m", "", output).unwrap_or_default()
}
fn check_ref_is_in_mirror(&mut self, dir: &str, r#ref: &str) -> Result<bool> {
let mut output = String::new();
if is_dir(dir)
- && self.process.execute(
+ && self.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"rev-parse".to_string(),
@@ -1053,7 +1089,7 @@ impl Git {
&& trim(&output, None) == "."
{
let mut ignored_output = String::new();
- let exit_code = self.process.execute(
+ let exit_code = self.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"rev-parse".to_string(),
@@ -1068,14 +1104,19 @@ impl Git {
return Ok(true);
}
}
- Self::check_for_repo_ownership_error(self.process.get_error_output(), dir, None)?;
+ Self::check_for_repo_ownership_error(self.process.borrow().get_error_output(), dir, None)?;
Ok(false)
}
/// @return array<int, string>|null
- fn get_authentication_failure(&self, url: &str) -> Option<IndexMap<i32, String>> {
- let m = Preg::is_match_strict_groups(r"{^(https?://)([^/]+)(.*)$}i", url)?;
+ fn get_authentication_failure(&self, url: &str) -> Option<IndexMap<CaptureKey, String>> {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(r"{^(https?://)([^/]+)(.*)$}i", url, Some(&mut m))
+ .unwrap_or(false)
+ {
+ return None;
+ }
let auth_failures = [
"fatal: Authentication failed",
@@ -1085,7 +1126,7 @@ impl Git {
"fatal: could not read Username",
];
- let error_output = self.process.get_error_output();
+ let error_output = self.process.borrow().get_error_output();
for auth_failure in &auth_failures {
if strpos(error_output, auth_failure).is_some() {
return Some(m);
@@ -1112,7 +1153,7 @@ impl Git {
let mut output_mixed = PhpMixed::String(String::new());
if is_local_path_repository {
let mut output = String::new();
- self.process.execute(
+ self.process.borrow_mut().execute_args(
&vec![
"git".to_string(),
"remote".to_string(),
@@ -1154,12 +1195,23 @@ impl Git {
let lines = self
.process
+ .borrow()
.split_lines(output_mixed.as_string().unwrap_or(""));
for line in lines {
- if let Some(matches) =
- Preg::is_match_strict_groups(r"{^\s*HEAD branch:\s(.+)\s*$}m", &line)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*HEAD branch:\s(.+)\s*$}m",
+ &line,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
{
- return Ok(Some(matches.get(1).cloned().unwrap_or_default()));
+ return Ok(Some(
+ matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ ));
}
}
@@ -1168,11 +1220,11 @@ impl Git {
match result {
Ok(v) => v,
Err(e) => {
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"<error>Failed to fetch root identifier from remote: {}</error>",
e
- )),
+ ),
true,
io_interface::DEBUG,
);
@@ -1181,7 +1233,7 @@ impl Git {
}
}
- pub fn clean_env(process: &ProcessExecutor) {
+ pub fn clean_env(process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>) {
// PHP: $process ?? new ProcessExecutor()
let git_version = Self::get_version(process);
if let Some(v) = git_version {
@@ -1249,7 +1301,7 @@ impl Git {
clearstatcache();
let mut ignored_output = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&vec!["git".to_string(), "--version".to_string()],
&mut ignored_output,
None,
@@ -1259,7 +1311,7 @@ impl Git {
message: Url::sanitize(format!(
"Failed to clone {}, git was not found, check that it is installed and in your PATH env.\n\n{}",
url,
- self.process.get_error_output()
+ self.process.borrow().get_error_output()
)),
code: 0,
}
@@ -1276,7 +1328,9 @@ impl Git {
/// Retrieves the current git version.
///
/// @return string|null The git version number, if present.
- pub fn get_version(process: &ProcessExecutor) -> Option<String> {
+ pub fn get_version(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> Option<String> {
let mut version = VERSION.lock().unwrap();
if version.is_none() {
*version = Some(None);
@@ -1285,10 +1339,15 @@ impl Git {
// For now, mimic the call signature (compilation fix is Phase B)
let exit_code: i64 = 0; // process.execute(&["git", "--version"].map(String::from).to_vec(), &mut output, None);
if exit_code == 0 {
- if let Some(matches) =
- Preg::is_match_strict_groups(r"/^git version (\d+(?:\.\d+)+)/m", &output)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"/^git version (\d+(?:\.\d+)+)/m",
+ &output,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
{
- *version = Some(matches.get(1).cloned());
+ *version = Some(matches.get(&CaptureKey::ByIndex(1)).cloned());
}
}
}
diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs
index 5fa88a6..cfb3d0c 100644
--- a/crates/shirabe/src/util/github.rs
+++ b/crates/shirabe/src/util/github.rs
@@ -2,7 +2,7 @@
use crate::io::io_interface;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{PhpMixed, date, stripos, strtolower};
use crate::config::Config;
@@ -15,9 +15,9 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct GitHub {
io: Box<dyn IOInterface>,
- config: Config,
- process: ProcessExecutor,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
}
impl GitHub {
@@ -26,14 +26,20 @@ impl GitHub {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- http_downloader: Option<HttpDownloader>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
) -> anyhow::Result<Self> {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io)))
+ });
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader(
+ &*io,
+ &config,
+ IndexMap::new(),
+ )?)),
};
Ok(Self {
io,
@@ -44,7 +50,7 @@ impl GitHub {
}
pub fn authorize_oauth(&mut self, origin_url: &str) -> bool {
- let github_domains = self.config.get("github-domains");
+ let github_domains = self.config.borrow_mut().get("github-domains");
let domains = match github_domains.as_array() {
Some(arr) => arr.clone(),
None => return false,
@@ -55,7 +61,7 @@ impl GitHub {
}
let mut output = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"config".to_string(),
@@ -82,16 +88,13 @@ impl GitHub {
message: Option<&str>,
) -> anyhow::Result<bool> {
if let Some(msg) = message {
- self.io.write_error(
- PhpMixed::String(msg.to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(msg, true, io_interface::NORMAL);
}
let mut note = "Composer".to_string();
let expose_hostname = self
.config
+ .borrow_mut()
.get("github-expose-hostname")
.as_bool()
.unwrap_or(false);
@@ -99,7 +102,8 @@ impl GitHub {
let mut output = String::new();
if self
.process
- .execute(&["hostname".to_string()], &mut output, None)
+ .borrow_mut()
+ .execute_args(&["hostname".to_string()], &mut output, None)
== 0
{
note += &format!(" on {}", output.trim());
@@ -107,105 +111,87 @@ impl GitHub {
}
note += &format!(" {}", date("Y-m-d Hi", None));
- let local_auth_config = self.config.get_local_auth_config_source();
+ let local_auth_config = self.config.borrow().get_local_auth_config_source();
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(
- "You need to provide a GitHub access token.".to_string(),
- )),
- Box::new(PhpMixed::String(format!(
- "Tokens will be stored in plain text in \"{}\" for future use by Composer.",
- local_auth_config
- .as_ref()
- .map(|c| format!("{} OR ", c.get_name()))
- .unwrap_or_default()
- + &self.config.get_auth_config_source().get_name()
- ))),
- Box::new(PhpMixed::String(
- "Due to the security risk of tokens being exfiltrated, use tokens with short expiration times and only the minimum permissions necessary.".to_string(),
- )),
- Box::new(PhpMixed::String(String::new())),
- Box::new(PhpMixed::String(
- "Carefully consider the following options in order:".to_string(),
- )),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(
+ "You need to provide a GitHub access token.".to_string(),
+ )),
+ Box::new(PhpMixed::String(format!(
+ "Tokens will be stored in plain text in \"{}\" for future use by Composer.",
+ local_auth_config
+ .as_ref()
+ .map(|c| format!("{} OR ", c.get_name()))
+ .unwrap_or_default()
+ + &self.config.borrow().get_auth_config_source().get_name()
+ ))),
+ Box::new(PhpMixed::String(
+ "Due to the security risk of tokens being exfiltrated, use tokens with short expiration times and only the minimum permissions necessary.".to_string(),
+ )),
+ Box::new(PhpMixed::String(String::new())),
+ Box::new(PhpMixed::String(
+ "Carefully consider the following options in order:".to_string(),
+ )),
+ Box::new(PhpMixed::String(String::new())),
+ ]), true, io_interface::NORMAL);
let encoded_note = shirabe_php_shim::rawurlencode(&note).replace("%20", "+");
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(
- "1. When you don't use 'vcs' type 'repositories' in composer.json and do not need to clone source or download dist files".to_string(),
- )),
- Box::new(PhpMixed::String(
- "from private GitHub repositories over HTTPS, use a fine-grained token with read-only access to public information.".to_string(),
- )),
- Box::new(PhpMixed::String(
- "Use the following URL to create such a token:".to_string(),
- )),
- Box::new(PhpMixed::String(format!(
- "https://{}/settings/personal-access-tokens/new?name={}",
- origin_url, encoded_note
- ))),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(
+ "1. When you don't use 'vcs' type 'repositories' in composer.json and do not need to clone source or download dist files".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "from private GitHub repositories over HTTPS, use a fine-grained token with read-only access to public information.".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "Use the following URL to create such a token:".to_string(),
+ )),
+ Box::new(PhpMixed::String(format!(
+ "https://{}/settings/personal-access-tokens/new?name={}",
+ origin_url, encoded_note
+ ))),
+ Box::new(PhpMixed::String(String::new())),
+ ]), true, io_interface::NORMAL);
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(
- "2. When all relevant _private_ GitHub repositories belong to a single user or organisation, use a fine-grained token with".to_string(),
- )),
- Box::new(PhpMixed::String(
- "repository \"content\" read-only permissions. You can start with the following URL, but you may need to change the resource owner".to_string(),
- )),
- Box::new(PhpMixed::String(
- "to the right user or organisation. Additionally, you can scope permissions down to apply only to selected repositories.".to_string(),
- )),
- Box::new(PhpMixed::String(format!(
- "https://{}/settings/personal-access-tokens/new?contents=read&name={}",
- origin_url, encoded_note
- ))),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(
+ "2. When all relevant _private_ GitHub repositories belong to a single user or organisation, use a fine-grained token with".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "repository \"content\" read-only permissions. You can start with the following URL, but you may need to change the resource owner".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "to the right user or organisation. Additionally, you can scope permissions down to apply only to selected repositories.".to_string(),
+ )),
+ Box::new(PhpMixed::String(format!(
+ "https://{}/settings/personal-access-tokens/new?contents=read&name={}",
+ origin_url, encoded_note
+ ))),
+ Box::new(PhpMixed::String(String::new())),
+ ]), true, io_interface::NORMAL);
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(
- "3. A \"classic\" token grants broad permissions on your behalf to all repositories accessible by you.".to_string(),
- )),
- Box::new(PhpMixed::String(
- "This may include write permissions, even though not needed by Composer. Use it only when you need to access".to_string(),
- )),
- Box::new(PhpMixed::String(
- "private repositories across multiple organisations at the same time and using directory-specific authentication sources".to_string(),
- )),
- Box::new(PhpMixed::String(
- "is not an option. You can generate a classic token here:".to_string(),
- )),
- Box::new(PhpMixed::String(format!(
- "https://{}/settings/tokens/new?scopes=repo&description={}",
- origin_url, encoded_note
- ))),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(
+ "3. A \"classic\" token grants broad permissions on your behalf to all repositories accessible by you.".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "This may include write permissions, even though not needed by Composer. Use it only when you need to access".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "private repositories across multiple organisations at the same time and using directory-specific authentication sources".to_string(),
+ )),
+ Box::new(PhpMixed::String(
+ "is not an option. You can generate a classic token here:".to_string(),
+ )),
+ Box::new(PhpMixed::String(format!(
+ "https://{}/settings/tokens/new?scopes=repo&description={}",
+ origin_url, encoded_note
+ ))),
+ Box::new(PhpMixed::String(String::new())),
+ ]), true, io_interface::NORMAL);
- self.io.write_error(
- PhpMixed::String(
- "For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#github-oauth".to_string(),
- ),
+ self.io.write_error3(
+ "For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#github-oauth",
true,
io_interface::NORMAL,
);
@@ -227,15 +213,13 @@ impl GitHub {
.to_string();
if token.is_empty() {
- self.io.write_error(
- PhpMixed::String("<warning>No token given, aborting.</warning>".to_string()),
+ self.io.write_error3(
+ "<warning>No token given, aborting.</warning>",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "You can also add it manually later by using \"composer config --global --auth github-oauth.github.com <token>\"".to_string(),
- ),
+ self.io.write_error3(
+ "You can also add it manually later by using \"composer config --global --auth github-oauth.github.com <token>\"",
true,
io_interface::NORMAL,
);
@@ -263,20 +247,19 @@ impl GitHub {
match self
.http_downloader
+ .borrow_mut()
.get(&format!("https://{}", api_url), &http_options)
{
Ok(_) => {}
Err(te) => {
if te.code == 403 || te.code == 401 {
- self.io.write_error(
- PhpMixed::String("<error>Invalid token provided.</error>".to_string()),
+ self.io.write_error3(
+ "<error>Invalid token provided.</error>",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(
- "You can also add it manually later by using \"composer config --global --auth github-oauth.github.com <token>\"".to_string(),
- ),
+ self.io.write_error3(
+ "You can also add it manually later by using \"composer config --global --auth github-oauth.github.com <token>\"",
true,
io_interface::NORMAL,
);
@@ -286,12 +269,18 @@ impl GitHub {
}
}
- let use_local =
- store_in_local_auth_config && self.config.get_local_auth_config_source().is_some();
+ let use_local = store_in_local_auth_config
+ && self
+ .config
+ .borrow()
+ .get_local_auth_config_source()
+ .is_some();
let auth_config_source_name;
if use_local {
- let mut auth_config_source = self.config.get_local_auth_config_source().unwrap();
+ let mut auth_config_source =
+ self.config.borrow().get_local_auth_config_source().unwrap();
self.config
+ .borrow()
.get_config_source()
.remove_config_setting(&format!("github-oauth.{}", origin_url))?;
auth_config_source.add_config_setting(
@@ -299,8 +288,9 @@ impl GitHub {
PhpMixed::String(token),
)?;
} else {
- let mut auth_config_source = self.config.get_auth_config_source();
+ let mut auth_config_source = self.config.borrow().get_auth_config_source();
self.config
+ .borrow()
.get_config_source()
.remove_config_setting(&format!("github-oauth.{}", origin_url))?;
auth_config_source.add_config_setting(
@@ -309,8 +299,8 @@ impl GitHub {
)?;
}
- self.io.write_error(
- PhpMixed::String("<info>Token stored successfully.</info>".to_string()),
+ self.io.write_error3(
+ "<info>Token stored successfully.</info>",
true,
io_interface::NORMAL,
);
@@ -358,8 +348,11 @@ impl GitHub {
if stripos(header, "x-github-sso: required").is_none() {
continue;
}
- if let Some(caps) = Preg::match_strict_groups(r"{\burl=(?P<url>[^\s;]+)}", header) {
- return caps.get("url").cloned();
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r"{\burl=(?P<url>[^\s;]+)}", header, Some(&mut caps))
+ .unwrap_or(false)
+ {
+ return caps.get(&CaptureKey::ByName("url".to_string())).cloned();
}
}
diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs
index 191e69d..11b7a21 100644
--- a/crates/shirabe/src/util/gitlab.rs
+++ b/crates/shirabe/src/util/gitlab.rs
@@ -15,22 +15,28 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct GitLab {
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
- pub(crate) process: ProcessExecutor,
- pub(crate) http_downloader: HttpDownloader,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ pub(crate) http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
}
impl GitLab {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- http_downloader: Option<HttpDownloader>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
) -> anyhow::Result<Self> {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io)))
+ });
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(&*io, &config, IndexMap::new())?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader(
+ &*io,
+ &config,
+ IndexMap::new(),
+ )?)),
};
Ok(Self {
io,
@@ -45,7 +51,7 @@ impl GitLab {
let bc_origin_url =
Preg::replace("{:\\d+}", "", origin_url).unwrap_or_else(|_| origin_url.to_string());
- let gitlab_domains = self.config.get("gitlab-domains");
+ let gitlab_domains = self.config.borrow_mut().get("gitlab-domains");
let domains = match gitlab_domains.as_array() {
Some(arr) => arr.clone(),
None => return false,
@@ -60,7 +66,7 @@ impl GitLab {
// if available use token from git config
let mut output = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"config".to_string(),
@@ -81,7 +87,7 @@ impl GitLab {
// if available use deploy token from git config
let mut token_user = String::new();
let mut token_password = String::new();
- if self.process.execute(
+ if self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"config".to_string(),
@@ -90,7 +96,7 @@ impl GitLab {
&mut token_user,
None,
) == 0
- && self.process.execute(
+ && self.process.borrow_mut().execute_args(
&[
"git".to_string(),
"config".to_string(),
@@ -109,7 +115,7 @@ impl GitLab {
}
// if available use token from composer config
- let auth_tokens = self.config.get("gitlab-token");
+ let auth_tokens = self.config.borrow_mut().get("gitlab-token");
let mut token: Option<PhpMixed> = None;
@@ -168,68 +174,53 @@ impl GitLab {
message: Option<&str>,
) -> anyhow::Result<bool> {
if let Some(msg) = message {
- self.io.write_error(
- PhpMixed::String(msg.to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.io.write_error3(msg, true, io_interface::NORMAL);
}
- let local_auth_config = self.config.get_local_auth_config_source();
+ let local_auth_config = self.config.borrow().get_local_auth_config_source();
let personal_access_token_link = format!(
"{}://{}/-/user_settings/personal_access_tokens",
scheme, origin_url
);
let revoke_link = format!("{}://{}/-/user_settings/applications", scheme, origin_url);
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"A token will be created and stored in \"{}\", your password will never be stored",
local_auth_config
.as_ref()
.map(|c| format!("{} OR ", c.get_name()))
.unwrap_or_default()
- + &self.config.get_auth_config_source().get_name()
- )),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String("To revoke access to this token you can visit:".to_string()),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String(revoke_link.clone()),
- true,
- io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::String(
- "Alternatively you can setup an personal access token on:".to_string(),
+ + &self.config.borrow().get_auth_config_source().get_name()
),
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(personal_access_token_link.clone()),
+ self.io.write_error3(
+ "To revoke access to this token you can visit:",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String("and store it under \"gitlab-token\" see https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token for more details.".to_string()),
+ self.io
+ .write_error3(&revoke_link, true, io_interface::NORMAL);
+ self.io.write_error3(
+ "Alternatively you can setup an personal access token on:",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String("https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token".to_string()),
+ self.io
+ .write_error3(&personal_access_token_link, true, io_interface::NORMAL);
+ self.io.write_error3(
+ "and store it under \"gitlab-token\" see https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token for more details.",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String("for more details.".to_string()),
+ self.io.write_error3(
+ "https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token",
true,
io_interface::NORMAL,
);
+ self.io
+ .write_error3("for more details.", true, io_interface::NORMAL);
let mut store_in_local_auth_config = false;
if local_auth_config.is_some() {
@@ -261,41 +252,41 @@ impl GitLab {
.and_then(|v| v.as_string())
== Some("invalid_grant");
if is_invalid_grant {
- self.io.write_error(
- PhpMixed::String("Bad credentials. If you have two factor authentication enabled you will have to manually create a personal access token".to_string()),
+ self.io.write_error3(
+ "Bad credentials. If you have two factor authentication enabled you will have to manually create a personal access token",
true,
io_interface::NORMAL,
);
} else {
- self.io.write_error(
- PhpMixed::String("Bad credentials.".to_string()),
+ self.io.write_error3(
+ "Bad credentials.",
true,
io_interface::NORMAL,
);
}
} else {
- self.io.write_error(
- PhpMixed::String("Maximum number of login attempts exceeded. Please try again later.".to_string()),
+ self.io.write_error3(
+ "Maximum number of login attempts exceeded. Please try again later.",
true,
io_interface::NORMAL,
);
}
- self.io.write_error(
- PhpMixed::String("You can also manually create a personal access token enabling the \"read_api\" scope at:".to_string()),
+ self.io.write_error3(
+ "You can also manually create a personal access token enabling the \"read_api\" scope at:",
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(personal_access_token_link.clone()),
+ self.io.write_error3(
+ &personal_access_token_link,
true,
io_interface::NORMAL,
);
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Add it using \"composer config --global --auth gitlab-token.{} <token>\"",
origin_url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -342,7 +333,7 @@ impl GitLab {
)?;
}
} else {
- let mut auth_config_source = self.config.get_auth_config_source();
+ let mut auth_config_source = self.config.borrow().get_auth_config_source();
if has_expires_in {
auth_config_source.add_config_setting(
&format!("gitlab-oauth.{}", origin_url),
@@ -375,8 +366,8 @@ impl GitLab {
Ok(r) => r,
Err(e) => match e.downcast::<TransportException>() {
Ok(te) => {
- self.io.write_error(
- PhpMixed::String(format!("Couldn't refresh access token: {}", te.message)),
+ self.io.write_error3(
+ &format!("Couldn't refresh access token: {}", te.message),
true,
io_interface::NORMAL,
);
@@ -400,10 +391,13 @@ impl GitLab {
);
// store value in user config in auth file
- self.config.get_auth_config_source().add_config_setting(
- &format!("gitlab-oauth.{}", origin_url),
- Self::build_oauth_config(&response, &access_token),
- )?;
+ self.config
+ .borrow()
+ .get_auth_config_source()
+ .add_config_setting(
+ &format!("gitlab-oauth.{}", origin_url),
+ Self::build_oauth_config(&response, &access_token),
+ )?;
Ok(true)
}
@@ -454,23 +448,21 @@ impl GitLab {
let token = self
.http_downloader
+ .borrow_mut()
.get(
&format!("{}://{}/oauth/token", scheme, api_url),
&PhpMixed::Array(options),
)?
.decode_json()?;
- self.io.write_error(
- PhpMixed::String("Token successfully created".to_string()),
- true,
- io_interface::NORMAL,
- );
+ self.io
+ .write_error3("Token successfully created", true, io_interface::NORMAL);
Ok(token)
}
pub fn is_oauth_expired(&self, origin_url: &str) -> bool {
- let auth_tokens = self.config.get("gitlab-oauth");
+ let auth_tokens = self.config.borrow_mut().get("gitlab-oauth");
if let Some(map) = auth_tokens.as_array() {
if let Some(token_info) = map.get(origin_url) {
if let Some(token_map) = token_info.as_array() {
@@ -489,7 +481,7 @@ impl GitLab {
}
fn refresh_token(&mut self, scheme: &str, origin_url: &str) -> anyhow::Result<PhpMixed> {
- let auth_tokens = self.config.get("gitlab-oauth");
+ let auth_tokens = self.config.borrow_mut().get("gitlab-oauth");
let refresh_token = auth_tokens
.as_array()
.and_then(|map| map.get(origin_url))
@@ -543,22 +535,23 @@ impl GitLab {
let token = self
.http_downloader
+ .borrow_mut()
.get(
&format!("{}://{}/oauth/token", scheme, origin_url),
&PhpMixed::Array(options),
)?
.decode_json()?;
- self.io.write_error(
- PhpMixed::String("GitLab token successfully refreshed".to_string()),
+ self.io.write_error3(
+ "GitLab token successfully refreshed",
true,
io_interface::VERY_VERBOSE,
);
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"To revoke access to this token you can visit {}://{}/-/user_settings/applications",
scheme, origin_url
- )),
+ ),
true,
io_interface::VERY_VERBOSE,
);
diff --git a/crates/shirabe/src/util/hg.rs b/crates/shirabe/src/util/hg.rs
index c3f4b6e..fa25a78 100644
--- a/crates/shirabe/src/util/hg.rs
+++ b/crates/shirabe/src/util/hg.rs
@@ -14,12 +14,16 @@ static VERSION: OnceLock<Option<String>> = OnceLock::new();
#[derive(Debug)]
pub struct Hg {
io: Box<dyn IOInterface>,
- config: Config,
- process: ProcessExecutor,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
}
impl Hg {
- pub fn new(io: &dyn IOInterface, config: &Config, process: &ProcessExecutor) -> Self {
+ pub fn new(
+ io: &dyn IOInterface,
+ config: &Config,
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> Self {
todo!()
}
@@ -29,14 +33,19 @@ impl Hg {
url: String,
cwd: Option<String>,
) -> Result<()> {
- self.config.prohibit_url_by_config(&url, &*self.io)?;
+ self.config.borrow_mut().prohibit_url_by_config(
+ &url,
+ Some(&*self.io),
+ &indexmap::IndexMap::new(),
+ )?;
// Try as is
let command = command_callable(url.clone());
let mut ignored_output = String::new();
if self
.process
- .execute(&command, &mut ignored_output, cwd.clone())
+ .borrow_mut()
+ .execute_args(&command, &mut ignored_output, cwd.clone())
== 0
{
return Ok(());
@@ -82,11 +91,16 @@ impl Hg {
let command = command_callable(authenticated_url);
let mut ignored_output = String::new();
- if self.process.execute(&command, &mut ignored_output, cwd) == 0 {
+ if self
+ .process
+ .borrow_mut()
+ .execute_args(&command, &mut ignored_output, cwd)
+ == 0
+ {
return Ok(());
}
- let error = self.process.get_error_output();
+ let error = self.process.borrow().get_error_output();
return self
.throw_exception(&format!("Failed to clone {}, \n\n{}", url, error), &url);
}
@@ -106,7 +120,7 @@ impl Hg {
Url::sanitize(&format!(
"Failed to clone {}, hg was not found, check that it is installed and in your PATH env.\n\n{}",
url,
- self.process.get_error_output()
+ self.process.borrow().get_error_output()
))
);
}
@@ -114,11 +128,13 @@ impl Hg {
anyhow::bail!("{}", Url::sanitize(message));
}
- pub fn get_version(process: &ProcessExecutor) -> Option<&'static str> {
+ pub fn get_version(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> Option<&'static str> {
VERSION
.get_or_init(|| {
let mut output = String::new();
- if process.execute(
+ if process.borrow_mut().execute_args(
&["hg".to_string(), "--version".to_string()],
&mut output,
None,
diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs
index d335b8b..c475ad3 100644
--- a/crates/shirabe/src/util/http/curl_downloader.rs
+++ b/crates/shirabe/src/util/http/curl_downloader.rs
@@ -51,7 +51,7 @@ pub struct CurlDownloader {
/// @var IOInterface
io: Box<dyn IOInterface>,
/// @var Config
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var AuthHelper
auth_helper: AuthHelper,
/// @var float
@@ -124,7 +124,7 @@ impl CurlDownloader {
/// @param mixed[] $options
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
_options: IndexMap<String, PhpMixed>,
_disable_tls: bool,
) -> Self {
@@ -336,10 +336,11 @@ impl CurlDownloader {
let original_options = options.clone();
// check URL can be accessed (i.e. is not insecure), but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
- if !Preg::is_match(r"{^http://(repo\.)?packagist\.org/p/}", url)
+ if !Preg::is_match(r"{^http://(repo\.)?packagist\.org/p/}", url).unwrap_or(false)
|| (strpos(url, "$").is_none() && strpos(url, "%24").is_none())
{
self.config
+ .borrow_mut()
.prohibit_url_by_config(url, Some(&*self.io), &options)?;
}
@@ -681,13 +682,13 @@ impl CurlDownloader {
if attributes.get("redirects").and_then(|v| v.as_int()) == Some(0)
&& attributes.get("retries").and_then(|v| v.as_int()) == Some(0)
{
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Downloading {}{}{}",
Url::sanitize(url.to_string()),
using_proxy,
if_modified
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -830,11 +831,8 @@ impl CurlDownloader {
&& !TIMEOUT_WARNING.load(Ordering::Relaxed)
{
TIMEOUT_WARNING.store(true, Ordering::Relaxed);
- self.io.write_error(
- PhpMixed::String(
- "<warning>A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the command again prefixed with COMPOSER_DISABLE_NETWORK=1 to make Composer run in offline mode.</warning>"
- .to_string(),
- ),
+ self.io.write_error3(
+ "<warning>A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the command again prefixed with COMPOSER_DISABLE_NETWORK=1 to make Composer run in offline mode.</warning>",
true,
crate::io::io_interface::NORMAL,
);
@@ -904,8 +902,8 @@ impl CurlDownloader {
// CURLE_COULDNT_CONNECT, retry forcing IPv4 if no IP stack was selected
attributes.insert("ipResolve".to_string(), PhpMixed::Int(4));
}
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Retrying ({}) {} due to curl error {}",
job.get("attributes")
.and_then(|v| v.as_array())
@@ -920,7 +918,7 @@ impl CurlDownloader {
.to_string()
),
errno
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -936,8 +934,8 @@ impl CurlDownloader {
if errno == 55
/* CURLE_SEND_ERROR */
{
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Retrying ({}) {} due to curl error {}",
job.get("attributes")
.and_then(|v| v.as_array())
@@ -952,7 +950,7 @@ impl CurlDownloader {
.to_string()
),
errno
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -1073,8 +1071,8 @@ impl CurlDownloader {
.map(|(k, v)| (k.clone(), (**v).clone()))
.collect(),
));
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"[{}] {}",
status_code.unwrap_or(0),
Url::sanitize(
@@ -1083,7 +1081,7 @@ impl CurlDownloader {
.unwrap_or("")
.to_string()
)
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -1149,8 +1147,8 @@ impl CurlDownloader {
.map(|(k, v)| (k.clone(), (**v).clone()))
.collect(),
));
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"[{}] {}",
status_code.unwrap_or(0),
Url::sanitize(
@@ -1159,7 +1157,7 @@ impl CurlDownloader {
.unwrap_or("")
.to_string()
)
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -1292,8 +1290,8 @@ impl CurlDownloader {
.unwrap_or(0)
< self.max_retries
{
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Retrying ({}) {} due to status code {}",
job.get("attributes")
.and_then(|v| v.as_array())
@@ -1308,7 +1306,7 @@ impl CurlDownloader {
.to_string()
),
sc
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -1598,8 +1596,8 @@ impl CurlDownloader {
}
if !target_url.is_empty() {
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io.write_error3(
+ &sprintf(
"Following redirect (%u) %s",
&[
PhpMixed::Int(
@@ -1612,7 +1610,7 @@ impl CurlDownloader {
),
PhpMixed::String(Url::sanitize(target_url.clone())),
],
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -1697,7 +1695,7 @@ impl CurlDownloader {
}
// check for gitlab 404 when downloading archives
- let gitlab_domains = self.config.get("gitlab-domains");
+ let gitlab_domains = self.config.borrow_mut().get("gitlab-domains");
let gitlab_domains_list: Vec<Box<PhpMixed>> = match gitlab_domains {
PhpMixed::List(l) => l,
_ => Vec::new(),
@@ -1788,7 +1786,7 @@ impl CurlDownloader {
PhpMixed::Array(a) => a.into_iter().map(|(k, v)| (k, *v)).collect(),
_ => IndexMap::new(),
};
- let origin = Url::get_origin(&self.config, url);
+ let origin = Url::get_origin(&*self.config.borrow(), url);
let copy_to = job
.get("filename")
diff --git a/crates/shirabe/src/util/http/proxy_manager.rs b/crates/shirabe/src/util/http/proxy_manager.rs
index 5ed8269..2576dcb 100644
--- a/crates/shirabe/src/util/http/proxy_manager.rs
+++ b/crates/shirabe/src/util/http/proxy_manager.rs
@@ -50,10 +50,10 @@ impl ProxyManager {
request_url: &str,
) -> Result<RequestProxy, TransportException> {
if let Some(ref error) = self.error {
- return Err(TransportException::new(format!(
- "Unable to use a proxy: {}",
- error
- )));
+ return Err(TransportException::new(
+ format!("Unable to use a proxy: {}", error),
+ 0,
+ ));
}
let scheme = request_url.split("://").next().unwrap_or("").to_string();
@@ -85,19 +85,19 @@ impl ProxyManager {
// PHP_SAPI is always 'cli' for this application
let (env, name) = Self::get_proxy_env("http_proxy");
if let Some(env) = env {
- self.http_proxy = Some(ProxyItem::new(env, name));
+ self.http_proxy = Some(ProxyItem::new(env, name)?);
}
if self.http_proxy.is_none() {
let (env, name) = Self::get_proxy_env("cgi_http_proxy");
if let Some(env) = env {
- self.http_proxy = Some(ProxyItem::new(env, name));
+ self.http_proxy = Some(ProxyItem::new(env, name)?);
}
}
let (env, name) = Self::get_proxy_env("https_proxy");
if let Some(env) = env {
- self.https_proxy = Some(ProxyItem::new(env, name));
+ self.https_proxy = Some(ProxyItem::new(env, name)?);
}
let (env, _name) = Self::get_proxy_env("no_proxy");
@@ -122,7 +122,7 @@ impl ProxyManager {
fn no_proxy(&self, request_url: &str) -> bool {
match &self.no_proxy_handler {
None => false,
- Some(handler) => handler.test(request_url),
+ Some(handler) => handler.test(request_url).unwrap_or(false),
}
}
}
diff --git a/crates/shirabe/src/util/http/response.rs b/crates/shirabe/src/util/http/response.rs
index cf238c7..62458d6 100644
--- a/crates/shirabe/src/util/http/response.rs
+++ b/crates/shirabe/src/util/http/response.rs
@@ -41,7 +41,7 @@ impl Response {
pub fn get_status_message(&self) -> Option<String> {
let mut value = None;
for header in &self.headers {
- if Preg::is_match(r"(?i)^HTTP/\S+ \d+", header) {
+ if Preg::is_match(r"(?i)^HTTP/\S+ \d+", header).unwrap_or(false) {
// In case of redirects, headers contain the headers of all responses
// so we can not return directly and need to keep iterating
value = Some(header.clone());
diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs
index 97b1f6e..71385ce 100644
--- a/crates/shirabe/src/util/http_downloader.rs
+++ b/crates/shirabe/src/util/http_downloader.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use indexmap::IndexMap;
use crate::util::silencer::Silencer;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise::promise::Promise;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
@@ -34,7 +34,7 @@ pub struct HttpDownloader {
/// @var IOInterface
io: Box<dyn IOInterface>,
/// @var Config
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var array<Job>
jobs: IndexMap<i64, Job>,
/// @var mixed[]
@@ -88,13 +88,12 @@ impl HttpDownloader {
/// @param mixed[] $options The options
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
options: IndexMap<String, PhpMixed>,
disable_tls: bool,
) -> Self {
let disabled = Platform::get_env("COMPOSER_DISABLE_NETWORK")
- .as_bool()
- .unwrap_or(false);
+ .map_or(false, |s| !s.is_empty() && s != "0");
// Setup TLS options
// The cafile option can be set via config.json
@@ -125,8 +124,8 @@ impl HttpDownloader {
let curl = if Self::is_curl_enabled() {
Some(CurlDownloader::new(
- &*io,
- &config,
+ io.clone_box(),
+ std::rc::Rc::clone(&config),
options.clone(),
disable_tls,
))
@@ -135,8 +134,8 @@ impl HttpDownloader {
};
let rfs = Some(RemoteFilesystem::new(
- &*io,
- &config,
+ io.clone_box(),
+ std::rc::Rc::clone(&config),
options.clone(),
disable_tls,
));
@@ -328,7 +327,7 @@ impl HttpDownloader {
let id = self.id_gen;
self.id_gen += 1;
- let origin = Url::get_origin(&self.config, &request.url);
+ let origin = Url::get_origin(&*self.config.borrow(), &request.url);
let job = Job {
id,
@@ -354,13 +353,28 @@ impl HttpDownloader {
}
// capture username/password from URL if there is one
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^https?://([^:/]+):([^@/]+)@([^/]+)}i", &request.url)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^https?://([^:/]+):([^@/]+)@([^/]+)}i",
+ &request.url,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
self.io.set_authentication(
origin.clone(),
- rawurldecode(m.get(1).cloned().unwrap_or_default().as_str()),
- Some(rawurldecode(m.get(2).cloned().unwrap_or_default().as_str())),
+ rawurldecode(
+ m.get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default()
+ .as_str(),
+ ),
+ Some(rawurldecode(
+ m.get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default()
+ .as_str(),
+ )),
);
}
@@ -723,7 +737,7 @@ impl HttpDownloader {
return false;
}
- if !Preg::is_match(r"{^https?://}i", &job.request.url) {
+ if !Preg::is_match(r"{^https?://}i", &job.request.url).unwrap_or(false) {
return false;
}
diff --git a/crates/shirabe/src/util/loop.rs b/crates/shirabe/src/util/loop.rs
index 189b8a3..9ffee8f 100644
--- a/crates/shirabe/src/util/loop.rs
+++ b/crates/shirabe/src/util/loop.rs
@@ -10,21 +10,21 @@ use shirabe_php_shim::microtime;
#[derive(Debug)]
pub struct Loop {
- http_downloader: HttpDownloader,
- process_executor: Option<ProcessExecutor>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ process_executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
current_promises: IndexMap<i64, Vec<Box<dyn PromiseInterface>>>,
wait_index: i64,
}
impl Loop {
pub fn new(
- mut http_downloader: HttpDownloader,
- process_executor: Option<ProcessExecutor>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ process_executor: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- http_downloader.enable_async();
+ http_downloader.borrow_mut().enable_async();
- let process_executor = process_executor.map(|mut pe| {
- pe.enable_async();
+ let process_executor = process_executor.map(|pe| {
+ pe.borrow_mut().enable_async();
pe
});
@@ -36,11 +36,13 @@ impl Loop {
}
}
- pub fn get_http_downloader(&self) -> &HttpDownloader {
+ pub fn get_http_downloader(&self) -> &std::rc::Rc<std::cell::RefCell<HttpDownloader>> {
&self.http_downloader
}
- pub fn get_process_executor(&self) -> Option<&ProcessExecutor> {
+ pub fn get_process_executor(
+ &self,
+ ) -> Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>> {
self.process_executor.as_ref()
}
@@ -66,9 +68,9 @@ impl Loop {
if let Some(ref progress) = progress {
let mut total_jobs: i64 = 0;
- total_jobs += self.http_downloader.count_active_jobs();
+ total_jobs += self.http_downloader.borrow_mut().count_active_jobs(None);
if let Some(ref pe) = self.process_executor {
- total_jobs += pe.count_active_jobs();
+ total_jobs += pe.borrow_mut().count_active_jobs(None);
}
progress.start(total_jobs);
}
@@ -77,9 +79,9 @@ impl Loop {
loop {
let mut active_jobs: i64 = 0;
- active_jobs += self.http_downloader.count_active_jobs();
+ active_jobs += self.http_downloader.borrow_mut().count_active_jobs(None);
if let Some(ref pe) = self.process_executor {
- active_jobs += pe.count_active_jobs();
+ active_jobs += pe.borrow_mut().count_active_jobs(None);
}
if let Some(ref progress) = progress {
diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs
index a647dbb..d4d39ea 100644
--- a/crates/shirabe/src/util/perforce.rs
+++ b/crates/shirabe/src/util/perforce.rs
@@ -29,12 +29,12 @@ pub struct Perforce {
pub(crate) p4_client_spec: String,
pub(crate) p4_depot_type: Option<String>,
pub(crate) p4_branch: Option<String>,
- pub(crate) process: ProcessExecutor,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
pub(crate) unique_perforce_client_name: String,
pub(crate) windows_flag: bool,
pub(crate) command_result: String,
pub(crate) io: Box<dyn IOInterface>,
- pub(crate) filesystem: Option<Filesystem>,
+ pub(crate) filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
}
impl Perforce {
@@ -43,7 +43,7 @@ impl Perforce {
repo_config: IndexMap<String, PhpMixed>,
port: String,
path: String,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
is_windows: bool,
io: Box<dyn IOInterface>,
) -> Self {
@@ -75,7 +75,7 @@ impl Perforce {
repo_config: IndexMap<String, PhpMixed>,
port: String,
path: String,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
io: Box<dyn IOInterface>,
) -> Self {
Self::new(repo_config, port, path, process, Platform::is_windows(), io)
@@ -83,7 +83,7 @@ impl Perforce {
pub fn check_server_exists(url: &str, process_executor: &mut ProcessExecutor) -> bool {
let mut ignored_output = String::new();
- process_executor.execute(
+ process_executor.execute_args(
&vec![
"p4".to_string(),
"-p".to_string(),
@@ -152,7 +152,7 @@ impl Perforce {
));
let client_spec = self.get_p4_client_spec();
let file_system = self.get_filesystem();
- file_system.remove(&client_spec);
+ file_system.borrow_mut().remove(&client_spec);
}
/// @param non-empty-string|non-empty-list<string> $command
@@ -168,7 +168,8 @@ impl Perforce {
_ => vec![],
};
self.process
- .execute(&cmd_vec, &mut self.command_result, None)
+ .borrow_mut()
+ .execute_args(&cmd_vec, &mut self.command_result, None)
}
pub fn get_client(&mut self) -> String {
@@ -195,7 +196,7 @@ impl Perforce {
pub fn initialize_path(&mut self, path: &str) {
self.path = path.to_string();
let fs = self.get_filesystem();
- fs.ensure_directory_exists(path);
+ fs.borrow_mut().ensure_directory_exists(path);
}
pub(crate) fn get_port(&self) -> &str {
@@ -361,7 +362,7 @@ impl Perforce {
.collect(),
));
if exit_code != 0 {
- let error_output = self.process.get_error_output().to_string();
+ let error_output = self.process.borrow().get_error_output().to_string();
let user = self.get_user().unwrap_or_default();
let index = strpos(&error_output, &user);
if index.is_none() {
@@ -401,7 +402,7 @@ impl Perforce {
file_get_contents(&self.get_p4_client_spec()),
None,
);
- process.run(None, IndexMap::new());
+ process.run(None);
}
pub fn sync_code_base(&mut self, source_reference: Option<&str>) -> Result<()> {
@@ -558,7 +559,7 @@ impl Perforce {
None,
);
- process.run(None, IndexMap::new())
+ process.run(None)
}
pub fn p4_login(&mut self) -> Result<()> {
@@ -583,11 +584,14 @@ impl Perforce {
password,
None,
);
- process.run(None, IndexMap::new());
+ process.run(None);
if !process.is_successful() {
return Err(Exception {
- message: format!("Error logging in:{}", self.process.get_error_output()),
+ message: format!(
+ "Error logging in:{}",
+ self.process.borrow().get_error_output()
+ ),
code: 0,
}
.into());
@@ -847,15 +851,17 @@ impl Perforce {
Some(self.command_result.clone())
}
- pub fn get_filesystem(&mut self) -> &Filesystem {
+ pub fn get_filesystem(&mut self) -> &std::rc::Rc<std::cell::RefCell<Filesystem>> {
if self.filesystem.is_none() {
- self.filesystem = Some(Filesystem::new(&self.process));
+ self.filesystem = Some(std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(
+ Some(std::rc::Rc::clone(&self.process)),
+ ))));
}
self.filesystem.as_ref().unwrap()
}
- pub fn set_filesystem(&mut self, fs: Filesystem) {
+ pub fn set_filesystem(&mut self, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>) {
self.filesystem = Some(fs);
}
diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs
index ed1958d..d0410ff 100644
--- a/crates/shirabe/src/util/process_executor.rs
+++ b/crates/shirabe/src/util/process_executor.rs
@@ -5,7 +5,7 @@ use anyhow::Result;
use indexmap::IndexMap;
use std::sync::{LazyLock, Mutex};
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise::promise::Promise;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
@@ -79,7 +79,7 @@ impl ProcessExecutor {
const GIT_CMDS_NEED_GIT_DIR: &'static [&'static [&'static str]] =
&[&["show"], &["log"], &["branch"], &["remote", "set-url"]];
- pub fn new(io: Option<Box<dyn IOInterface>>, _: Option<()>) -> Self {
+ pub fn new(io: Option<Box<dyn IOInterface>>) -> Self {
let mut this = Self {
capture_output: false,
error_output: String::new(),
@@ -116,6 +116,28 @@ impl ProcessExecutor {
self.do_execute(command, cwd, false, None)
}
+ /// Convenience wrapper used by phase-A code that calls
+ /// `process.execute(&[String], &mut String, Option<&str>) == 0`.
+ /// Forwards to `execute`, returning the status code (0 on Err for compatibility).
+ pub fn execute_args<C: AsRef<str>>(
+ &mut self,
+ command: &[String],
+ output: &mut String,
+ cwd: Option<C>,
+ ) -> i64 {
+ let cmd = PhpMixed::List(
+ command
+ .iter()
+ .map(|s| Box::new(PhpMixed::String(s.clone())))
+ .collect(),
+ );
+ let mut buf = PhpMixed::String(String::new());
+ let cwd_str: Option<&str> = cwd.as_ref().map(|s| s.as_ref());
+ let rc = self.execute(cmd, Some(&mut buf), cwd_str).unwrap_or(1);
+ *output = buf.as_string().unwrap_or("").to_string();
+ rc
+ }
+
/// 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(None) {
@@ -142,14 +164,16 @@ impl ProcessExecutor {
if is_string(&command) {
let mut command_str = command.as_string().unwrap_or("").to_string();
if Platform::is_windows() {
- if let Some(m) = Preg::is_match_strict_groups(r"{^([^:/\\]++) }", &command_str) {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^([^:/\\]++) }", &command_str, Some(&mut m))
+ .unwrap_or(false)
+ {
+ let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
command_str = substr_replace(
&command_str,
- &Self::escape(PhpMixed::String(Self::get_executable(
- m.get(1).cloned().unwrap_or_default().as_str(),
- ))),
+ &Self::escape(PhpMixed::String(Self::get_executable(&m1))),
0,
- strlen(m.get(1).cloned().unwrap_or_default().as_str()) as usize,
+ strlen(&m1) as usize,
);
}
}
@@ -359,17 +383,15 @@ impl ProcessExecutor {
}
if Process::ERR == r#type {
- self.io.as_ref().unwrap().write_error_raw(
- PhpMixed::String(buffer.to_string()),
- false,
- io_interface::NORMAL,
- );
+ self.io
+ .as_mut()
+ .unwrap()
+ .write_error_raw3(buffer, false, io_interface::NORMAL);
} else {
- self.io.as_ref().unwrap().write_raw(
- PhpMixed::String(buffer.to_string()),
- false,
- io_interface::NORMAL,
- );
+ self.io
+ .as_mut()
+ .unwrap()
+ .write_raw3(buffer, false, io_interface::NORMAL);
}
}
@@ -600,13 +622,17 @@ impl ProcessExecutor {
if Preg::is_match(
GitHub::GITHUB_TOKEN_REGEX,
m.get("user").cloned().unwrap_or_default().as_str(),
- ) {
+ )
+ .unwrap_or(false)
+ {
return "://***:***@".to_string();
}
if Preg::is_match(
r"{^[a-f0-9]{12,}$}",
m.get("user").cloned().unwrap_or_default().as_str(),
- ) {
+ )
+ .unwrap_or(false)
+ {
return "://***:***@".to_string();
}
@@ -668,7 +694,7 @@ impl ProcessExecutor {
// PHP: Preg::replace('/(\\\\*)"/', '$1$1\\"', $argument, -1, $dquotes)
argument =
Preg::replace_with_count(r#"/(\\*)"/"#, r#"$1$1\""#, &argument, -1, &mut dquotes);
- let meta = dquotes > 0 || Preg::is_match(r"/%[^%]+%|![^!]+!/", &argument);
+ let meta = dquotes > 0 || Preg::is_match(r"/%[^%]+%|![^!]+!/", &argument).unwrap_or(false);
if !meta && !quote {
quote = strpbrk(&argument, "^&|<>()").is_some();
diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs
index d2658fe..7c10640 100644
--- a/crates/shirabe/src/util/remote_filesystem.rs
+++ b/crates/shirabe/src/util/remote_filesystem.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
FILTER_VALIDATE_BOOLEAN, PHP_URL_HOST, PHP_URL_PATH, PHP_VERSION_ID, PhpMixed,
RuntimeException, array_replace_recursive, base64_encode, explode, extension_loaded,
@@ -33,7 +33,7 @@ pub enum GetResult {
#[derive(Debug)]
pub struct RemoteFilesystem {
io: Box<dyn IOInterface>,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
scheme: String,
bytes_max: i64,
origin_url: String,
@@ -55,7 +55,7 @@ pub struct RemoteFilesystem {
impl RemoteFilesystem {
pub fn new(
io: Box<dyn IOInterface>,
- config: Config,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
options: IndexMap<String, PhpMixed>,
disable_tls: bool,
auth_helper: Option<AuthHelper>,
@@ -140,8 +140,14 @@ impl RemoteFilesystem {
pub fn find_status_code(headers: &[String]) -> Option<i64> {
let mut value: Option<i64> = None;
for header in headers {
- if let Ok(Some(m)) = Preg::is_match_strict_groups("{^HTTP/\\S+ (\\d+)}i", header) {
- value = Some(m["1"].parse().unwrap_or(0));
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3("{^HTTP/\\S+ (\\d+)}i", header, Some(&mut m))
+ .unwrap_or(false)
+ {
+ value = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .or(Some(0));
}
}
@@ -251,8 +257,8 @@ impl RemoteFilesystem {
let proxy = ProxyManager::get_instance().get_proxy_for_request(&file_url);
let using_proxy = proxy.get_status(" using proxy (%s)");
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"{}{}{}",
if strpos(&orig_file_url, "http") == Some(0) {
"Downloading "
@@ -261,7 +267,7 @@ impl RemoteFilesystem {
},
Url::sanitize(&orig_file_url),
using_proxy
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -270,12 +276,16 @@ impl RemoteFilesystem {
|| (strpos(&file_url, "$").is_none() && strpos(&file_url, "%24").is_none()))
&& !degraded_packagist
{
- self.config.prohibit_url_by_config(&file_url, &*self.io);
+ let _ = self.config.borrow_mut().prohibit_url_by_config(
+ &file_url,
+ Some(&*self.io),
+ &indexmap::IndexMap::new(),
+ );
}
if self.progress && !is_redirect {
- self.io.write_error(
- PhpMixed::String("Downloading (<comment>connecting...</comment>)".to_string()),
+ self.io.write_error3(
+ "Downloading (<comment>connecting...</comment>)",
false,
crate::io::io_interface::NORMAL,
);
@@ -344,13 +354,13 @@ impl RemoteFilesystem {
.unwrap_or_else(|_| self.normalize_result(result.as_deref()));
e.set_response(decoded);
- self.io.write_error(
- PhpMixed::String(format!(
+ self.io.write_error3(
+ &format!(
"Content-Length mismatch, received {} out of {} bytes: ({})",
Platform::strlen(result.as_deref().unwrap_or("")),
cl_int,
base64_encode(result.as_deref().unwrap_or(""))
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
@@ -395,22 +405,15 @@ impl RemoteFilesystem {
let msg_owned = format!("{}", e);
if !self.degraded_mode && strpos(&msg_owned, "Operation timed out").is_some() {
self.degraded_mode = true;
- self.io.write_error(
- PhpMixed::String("".to_string()),
- true,
- crate::io::io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(format!("<error>{}</error>", msg_owned))),
- Box::new(PhpMixed::String(
- "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
- .to_string(),
- )),
- ]),
- true,
- crate::io::io_interface::NORMAL,
- );
+ self.io
+ .write_error3("", true, crate::io::io_interface::NORMAL);
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(format!("<error>{}</error>", msg_owned))),
+ Box::new(PhpMixed::String(
+ "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
+ .to_string(),
+ )),
+ ]), true, crate::io::io_interface::NORMAL);
return self.get(
&self.origin_url.clone(),
@@ -460,6 +463,7 @@ impl RemoteFilesystem {
let gitlab_domains: Vec<String> = self
.config
+ .borrow_mut()
.get("gitlab-domains")
.and_then(|v| v.as_list())
.map(|l| {
@@ -494,8 +498,8 @@ impl RemoteFilesystem {
if code >= 400 && code <= 599 {
if !self.retry {
if self.progress && !is_redirect {
- self.io.overwrite_error(
- PhpMixed::String("Downloading (<error>failed</error>)".to_string()),
+ self.io.overwrite_error4(
+ "Downloading (<error>failed</error>)",
false,
None,
crate::io::io_interface::NORMAL,
@@ -522,15 +526,15 @@ impl RemoteFilesystem {
}
if self.progress && !self.retry && !is_redirect {
- self.io.overwrite_error(
- PhpMixed::String(format!(
+ self.io.overwrite_error4(
+ &format!(
"Downloading ({})",
if result.is_none() {
"<error>failed</error>"
} else {
"<comment>100%</comment>"
}
- )),
+ ),
false,
None,
crate::io::io_interface::NORMAL,
@@ -552,21 +556,17 @@ impl RemoteFilesystem {
}
self.degraded_mode = true;
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String("".to_string())),
- Box::new(PhpMixed::String(format!(
- "<error>Failed to decode response: {}</error>",
- e
- ))),
- Box::new(PhpMixed::String(
- "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
- .to_string(),
- )),
- ]),
- true,
- crate::io::io_interface::NORMAL,
- );
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String("".to_string())),
+ Box::new(PhpMixed::String(format!(
+ "<error>Failed to decode response: {}</error>",
+ e
+ ))),
+ Box::new(PhpMixed::String(
+ "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
+ .to_string(),
+ )),
+ ]), true, crate::io::io_interface::NORMAL);
return self.get(
&self.origin_url.clone(),
@@ -640,22 +640,15 @@ impl RemoteFilesystem {
let msg_owned = format!("{}", e);
if !self.degraded_mode && strpos(&msg_owned, "Operation timed out").is_some() {
self.degraded_mode = true;
- self.io.write_error(
- PhpMixed::String("".to_string()),
- true,
- crate::io::io_interface::NORMAL,
- );
- self.io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(format!("<error>{}</error>", msg_owned))),
- Box::new(PhpMixed::String(
- "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
- .to_string(),
- )),
- ]),
- true,
- crate::io::io_interface::NORMAL,
- );
+ self.io
+ .write_error3("", true, crate::io::io_interface::NORMAL);
+ self.io.write_error3(PhpMixed::List(vec![
+ Box::new(PhpMixed::String(format!("<error>{}</error>", msg_owned))),
+ Box::new(PhpMixed::String(
+ "<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>"
+ .to_string(),
+ )),
+ ]), true, crate::io::io_interface::NORMAL);
return self.get(
&self.origin_url.clone(),
@@ -771,11 +764,8 @@ impl RemoteFilesystem {
&& Some(progression) != self.last_progress
{
self.last_progress = Some(progression);
- self.io.overwrite_error(
- PhpMixed::String(format!(
- "Downloading (<comment>{}%</comment>)",
- progression
- )),
+ self.io.overwrite_error4(
+ &format!("Downloading (<comment>{}%</comment>)", progression),
false,
None,
crate::io::io_interface::NORMAL,
@@ -944,19 +934,16 @@ impl RemoteFilesystem {
if let Some(target_url) = target_url {
self.redirects += 1;
- self.io.write_error(
- PhpMixed::String("".to_string()),
- true,
- crate::io::io_interface::DEBUG,
- );
- self.io.write_error(
- PhpMixed::String(sprintf(
+ self.io
+ .write_error3("", true, crate::io::io_interface::DEBUG);
+ self.io.write_error3(
+ &sprintf(
"Following redirect (%u) %s",
&[
PhpMixed::Int(self.redirects),
PhpMixed::String(Url::sanitize(&target_url)),
],
- )),
+ ),
true,
crate::io::io_interface::DEBUG,
);
diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs
index 955e28c..107be1b 100644
--- a/crates/shirabe/src/util/svn.rs
+++ b/crates/shirabe/src/util/svn.rs
@@ -5,7 +5,7 @@ use std::sync::Mutex;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
LogicException, PHP_URL_HOST, PhpMixed, RuntimeException, empty, implode, parse_url,
parse_url_all, stripos, strpos, trim,
@@ -35,11 +35,11 @@ pub struct Svn {
/// @var bool
pub(crate) cache_credentials: bool,
/// @var ProcessExecutor
- pub(crate) process: ProcessExecutor,
+ pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var int
pub(crate) qty_auth_tries: i64,
/// @var Config
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
}
/// @var string|null
@@ -51,10 +51,12 @@ impl Svn {
pub fn new(
url: String,
io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io)))
+ });
Self {
url,
io,
@@ -91,7 +93,11 @@ impl Svn {
verbose: bool,
) -> Result<String> {
// Ensure we are allowed to use this URL by config
- self.config.prohibit_url_by_config(url, &*self.io)?;
+ self.config.borrow_mut().prohibit_url_by_config(
+ url,
+ Some(&*self.io),
+ &indexmap::IndexMap::new(),
+ )?;
self.execute_with_auth_retry(command, cwd, url, path, verbose)
.map(|o| o.unwrap_or_default())
@@ -149,14 +155,16 @@ impl Svn {
};
// TODO(phase-b): pass handler callback to process.execute
let mut handler_output = String::new();
- let status = self
- .process
- .execute(&command, &mut handler_output, cwd.map(String::from));
+ let status = self.process.borrow_mut().execute_args(
+ &command,
+ &mut handler_output,
+ cwd.map(String::from),
+ );
if 0 == status {
return Ok(output);
}
- let error_output = self.process.get_error_output();
+ let error_output = self.process.borrow().get_error_output();
let full_output = trim(
&implode("\n", &[output.clone().unwrap_or_default(), error_output]),
None,
@@ -341,12 +349,12 @@ impl Svn {
/// Create the auth params from the configuration file.
fn create_auth_from_config(&mut self) -> bool {
- if !self.config.has("http-basic") {
+ if !self.config.borrow().has("http-basic") {
self.has_auth = Some(false);
return false;
}
- let auth_config = self.config.get("http-basic");
+ let auth_config = self.config.borrow_mut().get("http-basic");
let host = parse_url(&self.url, PHP_URL_HOST);
let host_str = host.as_string().unwrap_or("");
@@ -419,16 +427,21 @@ impl Svn {
let mut cached = VERSION.lock().unwrap();
if cached.is_none() {
let mut output = String::new();
- if 0 == self.process.execute(
+ if 0 == self.process.borrow_mut().execute_args(
&["svn".to_string(), "--version".to_string()],
&mut output,
None,
) {
- // TODO(phase-b): Preg::is_match with captures should populate $match
- if let Ok(Some(matches)) =
- Preg::is_match_with_indexed_captures(r"{(\d+(?:\.\d+)+)}", &output)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(r"{(\d+(?:\.\d+)+)}", &output, Some(&mut matches))
+ .unwrap_or(false)
{
- *cached = Some(matches.get(1).cloned().unwrap_or_default());
+ *cached = Some(
+ matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ );
}
}
}
diff --git a/crates/shirabe/src/util/sync_helper.rs b/crates/shirabe/src/util/sync_helper.rs
index 22e4f1f..7d69db3 100644
--- a/crates/shirabe/src/util/sync_helper.rs
+++ b/crates/shirabe/src/util/sync_helper.rs
@@ -18,9 +18,9 @@ impl<'a> DownloaderOrManager<'a> {
package: &dyn PackageInterface,
path: &str,
prev_package: Option<&dyn PackageInterface>,
- ) -> Box<dyn PromiseInterface> {
+ ) -> Result<Box<dyn PromiseInterface>> {
match self {
- Self::Interface(d) => d.download(package, path, prev_package),
+ Self::Interface(d) => d.download3(package, path, prev_package),
Self::Manager(d) => d.borrow().download(package, path, prev_package),
}
}
@@ -31,16 +31,20 @@ impl<'a> DownloaderOrManager<'a> {
package: &dyn PackageInterface,
path: &str,
prev_package: Option<&dyn PackageInterface>,
- ) -> Box<dyn PromiseInterface> {
+ ) -> Result<Box<dyn PromiseInterface>> {
match self {
Self::Interface(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> {
+ fn install(
+ &self,
+ package: &dyn PackageInterface,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>> {
match self {
- Self::Interface(d) => d.install(package, path),
+ Self::Interface(d) => d.install2(package, path),
Self::Manager(d) => d.borrow().install(package, path),
}
}
@@ -50,7 +54,7 @@ impl<'a> DownloaderOrManager<'a> {
package: &dyn PackageInterface,
prev_package: &dyn PackageInterface,
path: &str,
- ) -> Box<dyn PromiseInterface> {
+ ) -> Result<Box<dyn PromiseInterface>> {
match self {
Self::Interface(d) => d.update(package, prev_package, path),
Self::Manager(d) => d.borrow().update(package, prev_package, path),
@@ -63,7 +67,7 @@ impl<'a> DownloaderOrManager<'a> {
package: &dyn PackageInterface,
path: &str,
prev_package: Option<&dyn PackageInterface>,
- ) -> Box<dyn PromiseInterface> {
+ ) -> Result<Box<dyn PromiseInterface>> {
match self {
Self::Interface(d) => d.cleanup(r#type, package, path, prev_package),
Self::Manager(d) => d.borrow().cleanup(r#type, package, path, prev_package),
@@ -90,18 +94,18 @@ impl SyncHelper {
let result: Result<()> = (|| {
Self::r#await(
r#loop,
- Some(downloader.download(package, &path, prev_package)),
+ Some(downloader.download(package, &path, prev_package)?),
)?;
Self::r#await(
r#loop,
- Some(downloader.prepare(r#type, package, &path, prev_package)),
+ Some(downloader.prepare(r#type, package, &path, prev_package)?),
)?;
if r#type == "update" {
if let Some(prev) = prev_package {
- Self::r#await(r#loop, Some(downloader.update(package, prev, &path)))?;
+ Self::r#await(r#loop, Some(downloader.update(package, prev, &path)?))?;
}
} else {
- Self::r#await(r#loop, Some(downloader.install(package, &path)))?;
+ Self::r#await(r#loop, Some(downloader.install(package, &path)?))?;
}
Ok(())
})();
@@ -109,14 +113,14 @@ impl SyncHelper {
if result.is_err() {
Self::r#await(
r#loop,
- Some(downloader.cleanup(r#type, package, &path, prev_package)),
+ Some(downloader.cleanup(r#type, package, &path, prev_package)?),
)?;
return result;
}
Self::r#await(
r#loop,
- Some(downloader.cleanup(r#type, package, &path, prev_package)),
+ Some(downloader.cleanup(r#type, package, &path, prev_package)?),
)?;
Ok(())
}
diff --git a/crates/shirabe/src/util/url.rs b/crates/shirabe/src/util/url.rs
index 7d0ab21..7dc6e4f 100644
--- a/crates/shirabe/src/util/url.rs
+++ b/crates/shirabe/src/util/url.rs
@@ -2,7 +2,8 @@
use crate::config::Config;
use crate::util::github::GitHub;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{PHP_URL_HOST, PHP_URL_PORT, PhpMixed, in_array, parse_url};
pub struct Url;
@@ -15,62 +16,80 @@ impl Url {
.unwrap_or_default();
if host == "api.github.com" || host == "github.com" || host == "www.github.com" {
- if let Some(m) = Preg::match_(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match3(
r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$",
&url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
url = format!(
"https://api.github.com/repos/{}/{}/{}ball/{}",
- m.get("1").unwrap_or(&String::new()),
- m.get("2").unwrap_or(&String::new()),
- m.get("3").unwrap_or(&String::new()),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default(),
r#ref
);
- } else if let Some(m) = Preg::match_(
+ } else if Preg::match3(
r"(?i)^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$",
&url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
url = format!(
"https://api.github.com/repos/{}/{}/{}ball/{}",
- m.get("1").unwrap_or(&String::new()),
- m.get("2").unwrap_or(&String::new()),
- m.get("3").unwrap_or(&String::new()),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default(),
r#ref
);
- } else if let Some(m) = Preg::match_(
+ } else if Preg::match3(
r"(?i)^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$",
&url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
url = format!(
"https://api.github.com/repos/{}/{}/{}ball/{}",
- m.get("1").unwrap_or(&String::new()),
- m.get("2").unwrap_or(&String::new()),
- m.get("3").unwrap_or(&String::new()),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default(),
r#ref
);
}
} else if host == "bitbucket.org" || host == "www.bitbucket.org" {
- if let Some(m) = Preg::match_(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match3(
r"(?i)^https?://(?:www\.)?bitbucket\.org/([^/]+)/([^/]+)/get/(.+)\.(zip|tar\.gz|tar\.bz2)$",
&url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
url = format!(
"https://bitbucket.org/{}/{}/get/{}.{}",
- m.get("1").unwrap_or(&String::new()),
- m.get("2").unwrap_or(&String::new()),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
r#ref,
- m.get("4").unwrap_or(&String::new())
+ m.get(&CaptureKey::ByIndex(4)).cloned().unwrap_or_default()
);
}
} else if host == "gitlab.com" || host == "www.gitlab.com" {
- if let Some(m) = Preg::match_(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match3(
r"(?i)^https?://(?:www\.)?gitlab\.com/api/v[34]/projects/([^/]+)/repository/archive\.(zip|tar\.gz|tar\.bz2|tar)\?sha=.+$",
&url,
- ) {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
url = format!(
"https://gitlab.com/api/v4/projects/{}/repository/archive.{}?sha={}",
- m.get("1").unwrap_or(&String::new()),
- m.get("2").unwrap_or(&String::new()),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
r#ref
);
}
@@ -82,8 +101,9 @@ impl Url {
url = Preg::replace(
r"(?i)(/repos/[^/]+/[^/]+/(zip|tar)ball)(?:/.+)?$",
&format!("$1/{}", r#ref),
- url,
- );
+ &url,
+ )
+ .unwrap_or(url);
} else if in_array(
PhpMixed::String(host.clone()),
&config.get("gitlab-domains"),
@@ -92,8 +112,9 @@ impl Url {
url = Preg::replace(
r"(?i)(/api/v[34]/projects/[^/]+/repository/archive\.(?:zip|tar\.gz|tar\.bz2|tar)\?sha=).+$",
&format!("${{1}}{}", r#ref),
- url,
- );
+ &url,
+ )
+ .unwrap_or(url);
}
assert!(!url.is_empty());
@@ -148,30 +169,29 @@ impl Url {
pub fn sanitize(url: String) -> String {
// GitHub repository rename result in redirect locations containing the access_token as GET parameter
// e.g. https://api.github.com/repositories/9999999999?access_token=github_token
- let url = Preg::replace(r"([&?]access_token=)[^&]+", "$1***", url);
+ let url = Preg::replace(r"([&?]access_token=)[^&]+", "$1***", &url).unwrap_or(url);
let url = Preg::replace_callback(
r"(?i)^(?P<prefix>[a-z0-9]+://)?(?P<user>[^:/\s@]+):(?P<password>[^@\s/]+)@",
|m| {
+ let user = m
+ .get(&CaptureKey::ByName("user".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let prefix = m
+ .get(&CaptureKey::ByName("prefix".to_string()))
+ .cloned()
+ .unwrap_or_default();
// if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx, github_pat_xxx) we obfuscate that
- if Preg::is_match(
- GitHub::GITHUB_TOKEN_REGEX,
- m.get("user").map(|s| s.as_str()).unwrap_or(""),
- ) {
- format!(
- "{}***:***@",
- m.get("prefix").map(|s| s.as_str()).unwrap_or("")
- )
+ if Preg::is_match(GitHub::GITHUB_TOKEN_REGEX, &user).unwrap_or(false) {
+ format!("{}***:***@", prefix)
} else {
- format!(
- "{}{}:***@",
- m.get("prefix").map(|s| s.as_str()).unwrap_or(""),
- m.get("user").map(|s| s.as_str()).unwrap_or("")
- )
+ format!("{}{}:***@", prefix, user)
}
},
- url,
- );
+ &url,
+ )
+ .unwrap_or(url);
url
}