aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-19 00:10:22 +0900
committernsfisis <nsfisis@gmail.com>2026-05-19 00:11:03 +0900
commitc839244d8d09f3036ebfee8eef7eb6b147e593ab (patch)
treefe48c94f2c2e62468beef5ff1a8f3cff6adeef4f /crates/shirabe/src/command
parent48839250146b217e2756ed3c0e624fd341b54d6c (diff)
downloadphp-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.tar.gz
php-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.tar.zst
php-shirabe-c839244d8d09f3036ebfee8eef7eb6b147e593ab.zip
fix(compile): fix various compile errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/command')
-rw-r--r--crates/shirabe/src/command/about_command.rs44
-rw-r--r--crates/shirabe/src/command/archive_command.rs105
-rw-r--r--crates/shirabe/src/command/audit_command.rs68
-rw-r--r--crates/shirabe/src/command/base_command.rs419
-rw-r--r--crates/shirabe/src/command/base_config_command.rs11
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs67
-rw-r--r--crates/shirabe/src/command/bump_command.rs139
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs62
-rw-r--r--crates/shirabe/src/command/clear_cache_command.rs150
-rw-r--r--crates/shirabe/src/command/completion_trait.rs278
-rw-r--r--crates/shirabe/src/command/config_command.rs376
-rw-r--r--crates/shirabe/src/command/create_project_command.rs144
-rw-r--r--crates/shirabe/src/command/depends_command.rs87
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs77
-rw-r--r--crates/shirabe/src/command/dump_autoload_command.rs92
-rw-r--r--crates/shirabe/src/command/exec_command.rs68
-rw-r--r--crates/shirabe/src/command/fund_command.rs47
-rw-r--r--crates/shirabe/src/command/global_command.rs88
-rw-r--r--crates/shirabe/src/command/home_command.rs69
-rw-r--r--crates/shirabe/src/command/init_command.rs115
-rw-r--r--crates/shirabe/src/command/install_command.rs118
-rw-r--r--crates/shirabe/src/command/licenses_command.rs54
-rw-r--r--crates/shirabe/src/command/outdated_command.rs86
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs59
-rw-r--r--crates/shirabe/src/command/prohibits_command.rs87
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs116
-rw-r--r--crates/shirabe/src/command/remove_command.rs82
-rw-r--r--crates/shirabe/src/command/repository_command.rs167
-rw-r--r--crates/shirabe/src/command/require_command.rs163
-rw-r--r--crates/shirabe/src/command/run_script_command.rs67
-rw-r--r--crates/shirabe/src/command/script_alias_command.rs45
-rw-r--r--crates/shirabe/src/command/search_command.rs66
-rw-r--r--crates/shirabe/src/command/self_update_command.rs232
-rw-r--r--crates/shirabe/src/command/show_command.rs139
-rw-r--r--crates/shirabe/src/command/status_command.rs73
-rw-r--r--crates/shirabe/src/command/suggests_command.rs62
-rw-r--r--crates/shirabe/src/command/update_command.rs106
-rw-r--r--crates/shirabe/src/command/validate_command.rs57
38 files changed, 1380 insertions, 2905 deletions
diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs
index f2fdb80..0503578 100644
--- a/crates/shirabe/src/command/about_command.rs
+++ b/crates/shirabe/src/command/about_command.rs
@@ -1,32 +1,30 @@
//! ref: composer/src/Composer/Command/AboutCommand.php
use crate::command::base_command::BaseCommand;
+use crate::command::base_command::BaseCommandData;
+use crate::command::base_command::HasBaseCommandData;
use crate::composer::Composer;
use crate::io::io_interface::IOInterface;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct AboutCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl AboutCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("about")
+ self.set_name("about")
.set_description("Shows a short information about Composer")
.set_help("<info>php composer.phar about</info>");
}
- pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
+ pub fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
let composer_version = Composer::get_version();
+ let _ = (input, output);
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>Composer - Dependency Manager for PHP - version {composer_version}</info>\n\
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.\n\
See https://getcomposer.org/ for more information.</comment>"
@@ -36,30 +34,12 @@ impl AboutCommand {
}
}
-impl BaseCommand for AboutCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for AboutCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for AboutCommand {}
diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs
index 623a7d5..c4255db 100644
--- a/crates/shirabe/src/command/archive_command.rs
+++ b/crates/shirabe/src/command/archive_command.rs
@@ -4,14 +4,11 @@ use std::any::Any;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{LogicException, get_debug_type};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::console::input::input_argument::InputArgument;
@@ -35,36 +32,24 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct ArchiveCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for ArchiveCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl ArchiveCommand {
const FORMATS: &'static [&'static str] = &["tar", "tar.gz", "tar.bz2", "zip"];
pub fn configure(&mut self) {
- let suggest_available_package = self.suggest_available_package();
- self.inner
+ // TODO(cli-completion): suggest_available_package(99) for `package` argument
+ self
.set_name("archive")
.set_description("Creates an archive of this composer package")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None, suggest_available_package),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None, vec![]),
- InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None, Self::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None, vec![]),
- InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None, vec![]),
- InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None),
+ InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None),
+ InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None),
+ InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None),
+ InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None),
])
.set_help(
"The <info>archive</info> command creates an archive of the specified format\n\
@@ -76,7 +61,7 @@ impl ArchiveCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
let mut config: Option<Config> = None;
if let Some(ref composer) = composer {
@@ -85,14 +70,19 @@ impl ArchiveCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"archive".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_dispatcher = composer.get_event_dispatcher();
- event_dispatcher.dispatch(command_event.get_name(), &command_event);
- event_dispatcher.dispatch_script(ScriptEvents::PRE_ARCHIVE_CMD, true);
+ event_dispatcher.dispatch(Some(command_event.get_name()), None);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::PRE_ARCHIVE_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
}
let config = match config {
@@ -125,7 +115,7 @@ impl ArchiveCommand {
});
let return_code = self.archive(
- self.inner.get_io(),
+ self.get_io(),
&config,
input
.get_argument("package")
@@ -150,9 +140,12 @@ impl ArchiveCommand {
if return_code == 0 {
if let Some(ref composer) = composer {
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_ARCHIVE_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_ARCHIVE_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
}
}
@@ -175,11 +168,14 @@ impl ArchiveCommand {
composer.get_archive_manager().clone_box()
} else {
let factory = Factory::new();
- let process = ProcessExecutor::new_default();
+ let process = ProcessExecutor::new(None, None);
let http_downloader = Factory::create_http_downloader(io, config)?;
let download_manager =
factory.create_download_manager(io, config, &http_downloader, &process)?;
- let loop_ = Loop::new(http_downloader, process);
+ let loop_ = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
+ http_downloader,
+ Some(process),
+ )));
factory.create_archive_manager(config, &download_manager, &loop_)?
};
@@ -189,7 +185,7 @@ impl ArchiveCommand {
None => return Ok(1),
}
} else {
- self.inner.require_composer()?.get_package().clone_box()
+ self.require_composer(None, None)?.get_package().clone_box()
};
io.write_error(&format!(
@@ -203,8 +199,9 @@ impl ArchiveCommand {
file_name.as_deref(),
ignore_filters,
)?;
- let fs = Filesystem::new();
- let short_path = fs.find_shortest_path(&Platform::get_cwd(), &package_path, true);
+ let fs = Filesystem::new(None);
+ let short_path =
+ fs.find_shortest_path(&Platform::get_cwd(false)?, &package_path, true, false);
io.write_error_no_newline("Created: ");
let display = if short_path.len() < package_path.len() {
@@ -229,7 +226,7 @@ impl ArchiveCommand {
let mut min_stability;
let repo;
- if let Some(composer) = self.inner.try_composer() {
+ if let Some(composer) = self.try_composer(None, None) {
let local_repo = composer.get_repository_manager().get_local_repository();
let mut repos: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
@@ -332,30 +329,12 @@ impl ArchiveCommand {
}
}
-impl BaseCommand for ArchiveCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
+impl HasBaseCommandData for ArchiveCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ArchiveCommand {}
diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs
index dda0ee7..cf4c49d 100644
--- a/crates/shirabe/src/command/audit_command.rs
+++ b/crates/shirabe/src/command/audit_command.rs
@@ -2,20 +2,19 @@
use crate::advisory::audit_config::AuditConfig;
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use crate::package::package_interface::PackageInterface;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::installed_repository::InstalledRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::repository_set::RepositorySet;
use crate::repository::repository_utils::RepositoryUtils;
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
+use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use shirabe_external_packages::symfony::{
- component::console::command::command::Command, console::input::input_interface::InputInterface,
-};
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_fill_keys, array_merge,
implode, in_array,
@@ -23,23 +22,21 @@ use shirabe_php_shim::{
#[derive(Debug)]
pub struct AuditCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl AuditCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("audit")
.set_description("Checks for security vulnerability advisories for installed packages")
.set_definition(vec![
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables auditing of require-dev packages.", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_TABLE.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Audit based on the lock file instead of the installed packages.", None, vec![]),
- InputOption::new("abandoned", None, Some(InputOption::VALUE_REQUIRED), "Behavior on abandoned packages. Must be \"ignore\", \"report\", or \"fail\".", None, Auditor::ABANDONEDS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("ignore-severity", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Ignore advisories of a certain severity level.", Some(PhpMixed::Array(indexmap::IndexMap::new())), vec!["low".to_string(), "medium".to_string(), "high".to_string(), "critical".to_string()]),
- InputOption::new("ignore-unreachable", None, Some(InputOption::VALUE_NONE), "Ignore repositories that are unreachable or return a non-200 status code.", None, vec![]),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables auditing of require-dev packages.", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_TABLE.to_string()))),
+ InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Audit based on the lock file instead of the installed packages.", None),
+ InputOption::new("abandoned", None, Some(InputOption::VALUE_REQUIRED), "Behavior on abandoned packages. Must be \"ignore\", \"report\", or \"fail\".", None),
+ InputOption::new("ignore-severity", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Ignore advisories of a certain severity level.", Some(PhpMixed::Array(indexmap::IndexMap::new()))),
+ InputOption::new("ignore-unreachable", None, Some(InputOption::VALUE_NONE), "Ignore repositories that are unreachable or return a non-200 status code.", None),
])
.set_help(
"The <info>audit</info> command checks for security vulnerability advisories for installed packages.\n\n\
@@ -54,13 +51,11 @@ impl AuditCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let packages = self.get_packages(&composer, input)?;
if packages.is_empty() {
- self.inner
- .get_io()
- .write_error("No packages - skipping audit.");
+ self.get_io().write_error("No packages - skipping audit.");
return Ok(0);
}
@@ -70,7 +65,8 @@ impl AuditCommand {
repo_set.add_repository(repo);
}
- let audit_config = AuditConfig::from_config(composer.get_config())?;
+ let audit_config =
+ AuditConfig::from_config(composer.get_config(), true, Auditor::FORMAT_SUMMARY)?;
let abandoned = input
.get_option("abandoned")
@@ -113,10 +109,10 @@ impl AuditCommand {
Ok(auditor
.audit(
- self.inner.get_io(),
+ self.get_io(),
&repo_set,
&packages,
- &self.inner.get_audit_format(input, "format"),
+ &self.get_audit_format(input, "format"),
false,
&audit_config.ignore_list_for_audit,
&abandoned,
@@ -161,30 +157,12 @@ impl AuditCommand {
}
}
-impl BaseCommand for AuditCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for AuditCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for AuditCommand {}
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs
index 428cc65..3e0ded0 100644
--- a/crates/shirabe/src/command/base_command.rs
+++ b/crates/shirabe/src/command/base_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/BaseCommand.php
+//! ref: composer/vendor/symfony/console/Command/Command.php
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::component::console::completion::completion_suggestions::CompletionSuggestions;
use shirabe_external_packages::symfony::component::console::helper::table::Table;
use shirabe_external_packages::symfony::component::console::helper::table_separator::TableSeparator;
+use shirabe_external_packages::symfony::component::console::input::input_definition::InputDefinition;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::console::terminal::Terminal;
@@ -34,40 +32,258 @@ use crate::plugin::plugin_events::PluginEvents;
use crate::plugin::pre_command_run_event::PreCommandRunEvent;
use crate::util::platform::Platform;
-/// Base class for Composer commands
+/// \Composer\Composer\Command\BaseCommand + \Symfony\Component\Console\Command\Command
pub trait BaseCommand {
- fn inner(&self) -> &CommandBase;
- fn inner_mut(&mut self) -> &mut CommandBase;
- fn composer(&self) -> Option<&Composer>;
- fn composer_mut(&mut self) -> &mut Option<Composer>;
- fn io(&self) -> Option<&dyn IOInterface>;
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>>;
+ fn new(_name: Option<&str>) -> Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
- /// Gets the application instance for this command.
- fn get_application(&self) -> Result<Application> {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast from generic Symfony Application
- let application_as_composer: Option<Application> = application;
- if application_as_composer.is_none() {
- return Err(RuntimeException {
- message: format!(
- "Composer commands can only work with an {} instance set",
- "Composer\\Console\\Application"
- ),
- code: 0,
- }
- .into());
- }
+ fn get_name(&self) -> Option<String> {
+ todo!()
+ }
+
+ fn set_name(&mut self, _name: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
- Ok(application_as_composer.unwrap())
+ fn get_description(&self) -> String {
+ todo!()
+ }
+
+ fn set_description(&mut self, _description: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_help(&mut self, _help: &str) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_definition(&mut self, _definition: PhpMixed) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn get_definition(&self) -> &InputDefinition {
+ todo!()
+ }
+
+ fn add_argument(
+ &mut self,
+ _name: &str,
+ _mode: Option<i64>,
+ _description: &str,
+ _default: PhpMixed,
+ ) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
}
+ fn add_option(
+ &mut self,
+ _name: &str,
+ _shortcut: Option<&str>,
+ _mode: Option<i64>,
+ _description: &str,
+ _default: PhpMixed,
+ ) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn set_aliases(&mut self, _aliases: &[String]) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn get_aliases(&self) -> Vec<String> {
+ todo!()
+ }
+
+ fn set_hidden(&mut self, _hidden: bool) -> &mut Self
+ where
+ Self: Sized,
+ {
+ todo!()
+ }
+
+ fn is_hidden(&self) -> bool {
+ todo!()
+ }
+
+ fn run(
+ &mut self,
+ _input: &mut dyn InputInterface,
+ _output: &mut dyn OutputInterface,
+ ) -> anyhow::Result<i64> {
+ todo!()
+ }
+
+ fn get_helper(&self, _name: &str) -> PhpMixed {
+ todo!()
+ }
+
+ fn get_helper_set(&self) -> PhpMixed {
+ todo!()
+ }
+
+ /// Gets the application instance for this command.
+ fn get_application(&self) -> Result<Application>;
+
/// @deprecated since Composer 2.3.0 use requireComposer or tryComposer depending on whether you have $required set to true or false
fn get_composer(
&mut self,
required: bool,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
+ ) -> Result<Option<Composer>>;
+
+ /// Retrieves the default Composer\Composer instance or throws
+ fn require_composer(
+ &mut self,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
+ ) -> Result<Composer>;
+
+ /// Retrieves the default Composer\Composer instance or null
+ fn try_composer(
+ &mut self,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
+ ) -> Option<Composer>;
+
+ fn set_composer(&mut self, composer: Composer);
+
+ /// Removes the cached composer instance
+ fn reset_composer(&mut self) -> Result<()>;
+
+ /// Whether or not this command is meant to call another command.
+ fn is_proxy_command(&self) -> bool;
+
+ fn get_io(&mut self) -> &mut dyn IOInterface;
+
+ fn set_io(&mut self, io: Box<dyn IOInterface>);
+
+ // TODO(cli-completion): fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions);
+
+ /// @inheritDoc
+ fn initialize(
+ &mut self,
+ input: &mut dyn InputInterface,
+ output: &mut dyn OutputInterface,
+ ) -> Result<()>;
+
+ /// Calls {@see Factory::create()} with the given arguments, taking into account flags and default states for disabling scripts and plugins
+ fn create_composer_instance(
+ &self,
+ input: &dyn InputInterface,
+ io: &dyn IOInterface,
+ config: Option<IndexMap<String, PhpMixed>>,
+ disable_plugins: bool,
+ disable_scripts: Option<bool>,
+ ) -> Result<Composer>;
+
+ /// Returns preferSource and preferDist values based on the configuration.
+ fn get_preferred_install_options(
+ &self,
+ config: &Config,
+ input: &dyn InputInterface,
+ keep_vcs_requires_prefer_source: bool,
+ ) -> Result<(bool, bool)>;
+
+ fn get_platform_requirement_filter(
+ &self,
+ input: &dyn InputInterface,
+ ) -> Result<Box<dyn PlatformRequirementFilterInterface>>;
+
+ /// @param array<string> $requirements
+ ///
+ /// @return array<string, string>
+ fn format_requirements(&self, requirements: Vec<String>) -> Result<IndexMap<String, String>>;
+
+ /// @param array<string> $requirements
+ ///
+ /// @return list<array{name: string, version?: string}>
+ fn normalize_requirements(
+ &self,
+ requirements: Vec<String>,
+ ) -> Result<Vec<IndexMap<String, String>>>;
+
+ /// @param array<TableSeparator|mixed[]> $table
+ fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface);
+
+ fn get_terminal_width(&self) -> i64;
+
+ /// @internal
+ /// @param 'format'|'audit-format' $optName
+ /// @return Auditor::FORMAT_*
+ fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String>;
+
+ /// Creates an AuditConfig from the Config object, optionally overriding security blocking based on input options
+ fn create_audit_config(
+ &self,
+ config: &Config,
+ input: &dyn InputInterface,
+ ) -> Result<AuditConfig>;
+}
+
+#[derive(Debug)]
+pub struct BaseCommandData {
+ pub(crate) composer: Option<Composer>,
+ pub(crate) io: Option<Box<dyn IOInterface>>,
+}
+
+pub trait HasBaseCommandData {
+ fn base_command_data(&self) -> &BaseCommandData;
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData;
+
+ fn composer(&self) -> Option<&Composer> {
+ self.base_command_data().composer.as_ref()
+ }
+
+ fn composer_mut(&mut self) -> &mut Option<Composer> {
+ &mut self.base_command_data_mut().composer
+ }
+
+ fn io(&self) -> Option<&dyn IOInterface> {
+ self.base_command_data().io.as_deref()
+ }
+
+ fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
+ &mut self.base_command_data_mut().io
+ }
+}
+
+impl<C: HasBaseCommandData> BaseCommand for C {
+ fn get_application(&self) -> Result<Application> {
+ // TODO(phase-b): requires inner Symfony Command access
+ todo!()
+ }
+
+ fn get_composer(
+ &mut self,
+ required: bool,
+ disable_plugins: Option<bool>,
+ disable_scripts: Option<bool>,
) -> Result<Option<Composer>> {
if required {
return Ok(Some(
@@ -78,20 +294,17 @@ pub trait BaseCommand {
Ok(self.try_composer(disable_plugins, disable_scripts))
}
- /// Retrieves the default Composer\Composer instance or throws
fn require_composer(
&mut self,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
) -> Result<Composer> {
if self.composer().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
+ // TODO(phase-b): requires inner Symfony Application access
+ let application: Option<Application> = todo!();
+ if let Some(app) = application {
*self.composer_mut() =
Some(app.get_composer(true, disable_plugins, disable_scripts)?);
- // PHP: assert($this->composer instanceof Composer) — Rust types guarantee this
} else {
return Err(RuntimeException {
message:
@@ -106,17 +319,15 @@ pub trait BaseCommand {
Ok(self.composer().clone().unwrap())
}
- /// Retrieves the default Composer\Composer instance or null
fn try_composer(
&mut self,
disable_plugins: Option<bool>,
disable_scripts: Option<bool>,
) -> Option<Composer> {
if self.composer().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
+ // TODO(phase-b): requires inner Symfony Application access
+ let application: Option<Application> = todo!();
+ if let Some(app) = application {
*self.composer_mut() = app
.get_composer(false, disable_plugins, disable_scripts)
.ok();
@@ -130,102 +341,42 @@ pub trait BaseCommand {
*self.composer_mut() = Some(composer);
}
- /// Removes the cached composer instance
fn reset_composer(&mut self) -> Result<()> {
*self.composer_mut() = None;
self.get_application()?.reset_composer();
Ok(())
}
- /// Whether or not this command is meant to call another command.
fn is_proxy_command(&self) -> bool {
false
}
- fn get_io(&mut self) -> &dyn IOInterface {
+ fn get_io(&mut self) -> &mut dyn IOInterface {
if self.io().is_none() {
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<Application> = application;
- if let Some(app) = application_as_composer {
- *self.io_mut() = Some(app.get_io());
- } else {
- *self.io_mut() = Some(Box::new(NullIO::new()));
- }
+ // TODO(phase-b): requires inner Symfony Application access
+ *self.io_mut() = Some(Box::new(NullIO::new()));
}
- &**self.io().as_ref().unwrap()
+ &mut **self.io_mut().as_mut().unwrap()
}
fn set_io(&mut self, io: Box<dyn IOInterface>) {
*self.io_mut() = Some(io);
}
- /// @inheritdoc
- ///
- /// Backport suggested values definition from symfony/console 6.1+
- fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) {
- let definition = self.inner().get_definition();
- let name = input.get_completion_name().to_string();
- if CompletionInput::TYPE_OPTION_VALUE == input.get_completion_type()
- && definition.has_option(&name)
- {
- let option = definition.get_option(&name);
- // TODO(phase-b): `$option instanceof InputOption` (our InputOption, not Symfony's)
- let option_as_input: Option<&InputOption> = None;
- if let Some(input_option) = option_as_input {
- input_option.complete(input, suggestions);
- let _ = option;
- return;
- }
- }
- if CompletionInput::TYPE_ARGUMENT_VALUE == input.get_completion_type()
- && definition.has_argument(&name)
- {
- let argument = definition.get_argument(&name);
- // TODO(phase-b): `$argument instanceof InputArgument` (our InputArgument, not Symfony's)
- let argument_as_input: Option<&InputArgument> = None;
- if let Some(input_argument) = argument_as_input {
- input_argument.complete(input, suggestions);
- let _ = argument;
- return;
- }
- }
- self.inner().complete(input, suggestions);
- }
+ // TODO(cli-completion): fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions)
- /// @inheritDoc
fn initialize(
&mut self,
input: &mut dyn InputInterface,
output: &mut dyn OutputInterface,
) -> Result<()> {
// initialize a plugin-enabled Composer instance, either local or global
- let mut disable_plugins =
- input.has_parameter_option(PhpMixed::String("--no-plugins".to_string()));
- let mut disable_scripts =
- input.has_parameter_option(PhpMixed::String("--no-scripts".to_string()));
+ let mut disable_plugins = input.has_parameter_option(&["--no-plugins"], false);
+ let mut disable_scripts = input.has_parameter_option(&["--no-scripts"], false);
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<&Application> = None;
- if let Some(app) = application_as_composer {
- if app.get_disable_plugins_by_default() {
- disable_plugins = true;
- }
- if app.get_disable_scripts_by_default() {
- disable_scripts = true;
- }
- }
- let _ = application;
-
- // TODO(phase-b): `$this instanceof SelfUpdateCommand` — not representable since
- // BaseCommand is a struct, not a base type
- let self_is_self_update: Option<&SelfUpdateCommand> = None;
- if self_is_self_update.is_some() {
- disable_plugins = true;
- disable_scripts = true;
- }
+ // TODO(phase-b): requires inner Symfony Application access for disable_plugins_by_default / disable_scripts_by_default
+ // TODO(phase-b): `$this instanceof SelfUpdateCommand` not representable
let composer = self.try_composer(Some(disable_plugins), Some(disable_scripts));
// TODO(phase-b): re-borrow self for get_io after try_composer move
@@ -242,10 +393,12 @@ pub trait BaseCommand {
composer
};
if let Some(composer) = composer.as_ref() {
+ // TODO(phase-b): requires inner Symfony Command get_name access
+ let command_name: String = todo!();
let pre_command_run_event = PreCommandRunEvent::new(
PluginEvents::PRE_COMMAND_RUN.to_string(),
- Box::new(input),
- self.inner().get_name().to_string(),
+ input,
+ command_name,
);
composer.get_event_dispatcher().dispatch(
pre_command_run_event.get_name(),
@@ -253,12 +406,7 @@ pub trait BaseCommand {
);
}
- if true
- == input.has_parameter_option(PhpMixed::List(vec![Box::new(PhpMixed::String(
- "--no-ansi".to_string(),
- ))]))
- && input.has_option("no-progress")
- {
+ if input.has_parameter_option(&["--no-ansi"], false) && input.has_option("no-progress") {
input.set_option("no-progress", PhpMixed::Bool(true));
}
@@ -340,10 +488,10 @@ pub trait BaseCommand {
}
}
- self.inner().initialize(input, output)
+ // TODO(phase-b): requires inner Symfony Command initialize
+ Ok(())
}
- /// Calls {@see Factory::create()} with the given arguments, taking into account flags and default states for disabling scripts and plugins
fn create_composer_instance(
&self,
input: &dyn InputInterface,
@@ -352,28 +500,16 @@ pub trait BaseCommand {
disable_plugins: bool,
disable_scripts: Option<bool>,
) -> Result<Composer> {
- let mut disable_plugins = disable_plugins == true
- || input.has_parameter_option(PhpMixed::String("--no-plugins".to_string()));
- let mut disable_scripts = disable_scripts == Some(true)
- || input.has_parameter_option(PhpMixed::String("--no-scripts".to_string()));
+ let mut disable_plugins =
+ disable_plugins == Some(true) || input.has_parameter_option(&["--no-plugins"], false);
+ let mut disable_scripts =
+ disable_scripts == Some(true) || input.has_parameter_option(&["--no-scripts"], false);
- let application = self.inner().get_application();
- // TODO(phase-b): `$application instanceof Application` downcast
- let application_as_composer: Option<&Application> = None;
- if let Some(app) = application_as_composer {
- if app.get_disable_plugins_by_default() {
- disable_plugins = true;
- }
- if app.get_disable_scripts_by_default() {
- disable_scripts = true;
- }
- }
- let _ = application;
+ // TODO(phase-b): requires inner Symfony Application access for disable_plugins_by_default / disable_scripts_by_default
Factory::create(io, config, disable_plugins, disable_scripts)
}
- /// Returns preferSource and preferDist values based on the configuration.
fn get_preferred_install_options(
&self,
config: &Config,
@@ -489,9 +625,6 @@ pub trait BaseCommand {
Ok(PlatformRequirementFilterFactory::ignore_nothing())
}
- /// @param array<string> $requirements
- ///
- /// @return array<string, string>
fn format_requirements(&self, requirements: Vec<String>) -> Result<IndexMap<String, String>> {
let mut requires: IndexMap<String, String> = IndexMap::new();
let requirements = self.normalize_requirements(requirements)?;
@@ -516,9 +649,6 @@ pub trait BaseCommand {
Ok(requires)
}
- /// @param array<string> $requirements
- ///
- /// @return list<array{name: string, version?: string}>
fn normalize_requirements(
&self,
requirements: Vec<String>,
@@ -529,7 +659,6 @@ pub trait BaseCommand {
parser.parse_name_version_pairs(requirements)
}
- /// @param array<TableSeparator|mixed[]> $table
fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface) {
let mut renderer = Table::new(output);
renderer.set_style("compact");
@@ -550,9 +679,6 @@ pub trait BaseCommand {
width
}
- /// @internal
- /// @param 'format'|'audit-format' $optName
- /// @return Auditor::FORMAT_*
fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String> {
if !input.has_option(opt_name) {
return Err(LogicException {
@@ -585,7 +711,6 @@ pub trait BaseCommand {
Ok(val.as_string().unwrap_or("").to_string())
}
- /// Creates an AuditConfig from the Config object, optionally overriding security blocking based on input options
fn create_audit_config(
&self,
config: &Config,
@@ -635,3 +760,7 @@ pub trait BaseCommand {
Ok(audit_config)
}
}
+
+// TODO(phase-b): bridge BaseCommand to Symfony Command for trait-object container usage.
+// Cannot blanket-impl a foreign trait for a local generic (orphan rule); each concrete
+// command must impl symfony Command itself, or a wrapper type must be introduced.
diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs
index 85bcb68..8b748b4 100644
--- a/crates/shirabe/src/command/base_config_command.rs
+++ b/crates/shirabe/src/command/base_config_command.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Command/BaseConfigCommand.php
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::config::Config;
use crate::config::json_config_source::JsonConfigSource;
use crate::factory::Factory;
@@ -25,13 +25,14 @@ pub trait BaseConfigCommand: BaseCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<()> {
- self.inner.initialize(input, output)?;
+ // TODO(phase-b): BaseCommand::initialize chained via Self::initialize would recurse;
+ // omitted until trait disambiguation is sorted.
if input.get_option("global").as_bool() && input.get_option("file").is_not_null() {
return Err(anyhow::anyhow!("--file and --global can not be combined"));
}
- let io = self.inner.get_io();
+ let io = self.get_io();
*self.config_mut() = Some(Factory::create_config(io)?);
let config = self.config().as_mut().unwrap();
@@ -46,14 +47,14 @@ pub trait BaseConfigCommand: BaseCommand {
// Create global composer.json if invoked using `composer global [config-cmd]`
if (config_file == "composer.json" || config_file == "./composer.json")
&& !std::path::Path::new(&config_file).exists()
- && std::fs::canonicalize(Platform::get_cwd()).ok()
+ && std::fs::canonicalize(Platform::get_cwd(false)?).ok()
== std::fs::canonicalize(config.get("home").to_string()).ok()
{
std::fs::write(&config_file, "{\n}\n")?;
}
let config = self.config().as_ref().unwrap();
- *self.config_file_mut() = Some(JsonFile::new(config_file.clone(), None, Some(io)));
+ *self.config_file_mut() = Some(JsonFile::new(config_file.clone(), None, Some(io))?);
*self.config_source_mut() =
Some(JsonConfigSource::new(self.config_file().as_ref().unwrap()));
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index 62b97ea..5ff9c4a 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -9,7 +9,7 @@ use shirabe_php_shim::{InvalidArgumentException, UnexpectedValueException};
use shirabe_semver::constraint::bound::Bound;
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::link::Link;
use crate::package::package::Package;
@@ -33,30 +33,8 @@ pub trait BaseDependencyCommand: BaseCommand {
fn colors(&self) -> &[String];
fn colors_mut(&mut self) -> &mut [String];
- fn set_name(&mut self, name: &str) -> &mut Self {
- self.inner.set_name(name);
- self
- }
-
- fn set_aliases(&mut self, aliases: Vec<String>) -> &mut Self {
- self.inner.set_aliases(aliases);
- self
- }
-
- fn set_description(&mut self, description: &str) -> &mut Self {
- self.inner.set_description(description);
- self
- }
-
- fn set_definition(&mut self, definition: Vec<shirabe_php_shim::PhpMixed>) -> &mut Self {
- self.inner.set_definition(definition);
- self
- }
-
- fn set_help(&mut self, help: &str) -> &mut Self {
- self.inner.set_help(help);
- self
- }
+ // TODO(phase-b): these wrappers existed to forward BaseCommand setters, but they
+ // shadowed the BaseCommand methods and caused ambiguity. Use BaseCommand directly.
fn do_execute(
&mut self,
@@ -64,8 +42,8 @@ pub trait BaseDependencyCommand: BaseCommand {
output: &dyn OutputInterface,
inverted: bool,
) -> anyhow::Result<i64> {
- let composer = self.inner.require_composer()?;
- // TODO(plugin): dispatch CommandEvent(PluginEvents::COMMAND, self.inner.get_name(), input, output) via composer.get_event_dispatcher()
+ let composer = self.require_composer(None, None)?;
+ // TODO(plugin): dispatch CommandEvent(PluginEvents::COMMAND, self.get_name(), input, output) via composer.get_event_dispatcher()
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
repos.push(Box::new(RootPackageRepository::new(
@@ -88,7 +66,7 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(Box::new(PlatformRepository::new(
vec![],
locker.get_platform_overrides(),
- )));
+ )?));
} else {
let local_repo = composer.get_repository_manager().get_local_repository();
let root_pkg = composer.get_package();
@@ -98,6 +76,7 @@ pub trait BaseDependencyCommand: BaseCommand {
{
output.writeln(
"<warning>No dependencies installed. Try running composer install or update, or use --locked.</warning>",
+ shirabe_external_packages::symfony::console::output::output_interface::OUTPUT_NORMAL,
);
return Ok(1);
@@ -105,11 +84,15 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(Box::new(local_repo));
- let platform_overrides = composer.get_config().get("platform").unwrap_or_default();
- repos.push(Box::new(PlatformRepository::new(
- vec![],
- platform_overrides,
- )));
+ let platform_overrides = composer
+ .get_config()
+ .get("platform")
+ .as_array()
+ .cloned()
+ .unwrap_or_default();
+ // TODO(phase-b): platform_overrides type adjustment; using empty for now
+ let _ = platform_overrides;
+ repos.push(Box::new(PlatformRepository::new(vec![], IndexMap::new())?));
}
let mut installed_repo = InstalledRepository::new(repos)?;
@@ -144,7 +127,7 @@ pub trait BaseDependencyCommand: BaseCommand {
);
if matched_package.is_none() {
let default_repos = CompositeRepository::new(RepositoryFactory::default_repos(
- Some(self.inner.get_io()),
+ Some(self.get_io()),
Some(composer.get_config()),
Some(&mut composer.get_repository_manager()),
)?);
@@ -169,7 +152,7 @@ pub trait BaseDependencyCommand: BaseCommand {
)))?;
}
} else {
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<error>Package \"{}\" could not be found with constraint \"{}\", results below will most likely be incomplete.</error>",
needle, text_constraint
));
@@ -186,7 +169,7 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
""
};
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>Package \"{} {}\" found in version \"{}\"{}.</info>",
needle,
text_constraint,
@@ -195,7 +178,7 @@ pub trait BaseDependencyCommand: BaseCommand {
));
} else if inverted {
let matched = matched_package.as_ref().unwrap();
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<comment>Package \"{}\" {} is already installed! To find out why, run `composer why {}`</comment>",
needle,
matched.get_pretty_version(),
@@ -254,7 +237,7 @@ pub trait BaseDependencyCommand: BaseCommand {
} else {
String::new()
};
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>There is no installed package depending on \"{}\"{}",
needle, extra
));
@@ -266,7 +249,7 @@ pub trait BaseDependencyCommand: BaseCommand {
.as_complete_package_interface()
.and_then(|c| c.get_description())
.unwrap_or("");
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>{}</info> {} {}",
root.get_pretty_name(),
root.get_pretty_version(),
@@ -297,7 +280,7 @@ pub trait BaseDependencyCommand: BaseCommand {
}
}
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"Not finding what you were looking for? Try calling `composer {} \"{}:{}\" --dry-run` to get another view on the problem.",
composer_command, needle, text_constraint
));
@@ -356,7 +339,7 @@ pub trait BaseDependencyCommand: BaseCommand {
new_table.extend(table);
table = new_table;
}
- self.inner.render_table(table, output);
+ self.render_table(table, output);
}
fn init_styles(&mut self, output: &dyn OutputInterface) {
@@ -438,7 +421,7 @@ pub trait BaseDependencyCommand: BaseCommand {
}
fn write_tree_line(&self, line: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let line = if !io.is_decorated() {
line.replace('â””', "`-")
.replace('├', "|-")
diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs
index 5c0cf6e..6b8ca1a 100644
--- a/crates/shirabe/src/command/bump_command.rs
+++ b/crates/shirabe/src/command/bump_command.rs
@@ -4,14 +4,11 @@ use crate::io::io_interface;
use crate::package::base_package;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, file_get_contents, file_put_contents, is_writable, strtolower};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -28,19 +25,7 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct BumpCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for BumpCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl BumpCommand {
@@ -48,8 +33,8 @@ impl BumpCommand {
const ERROR_LOCK_OUTDATED: i64 = 2;
pub fn configure(&mut self) {
- let suggest_root_requirement = self.suggest_root_requirement();
- self.inner
+ // TODO(cli-completion): suggest_root_requirement() for `packages` argument
+ self
.set_name("bump")
.set_description("Increases the lower limit of your composer.json requirements to the currently installed versions")
.set_definition(vec![
@@ -58,11 +43,10 @@ impl BumpCommand {
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"Optional package name(s) to restrict which packages are bumped.",
None,
- suggest_root_requirement,
),
- InputOption::new("dev-only", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require-dev\".", None, vec![]),
- InputOption::new("no-dev-only", Some(PhpMixed::String("R".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require\".", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the packages to bump, but will not execute anything.", None, vec![]),
+ InputOption::new("dev-only", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require-dev\".", None),
+ InputOption::new("no-dev-only", Some(PhpMixed::String("R".to_string())), Some(InputOption::VALUE_NONE), "Only bump requirements in \"require\".", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the packages to bump, but will not execute anything.", None),
])
.set_help(
"The <info>bump</info> command increases the lower limit of your composer.json requirements\n\
@@ -93,7 +77,7 @@ impl BumpCommand {
.unwrap_or_default();
self.do_bump(
- self.inner.get_io(),
+ self.get_io(),
input.get_option("dev-only").as_bool().unwrap_or(false),
input.get_option("no-dev-only").as_bool().unwrap_or(false),
input.get_option("dry-run").as_bool().unwrap_or(false),
@@ -111,29 +95,23 @@ impl BumpCommand {
packages_filter: Vec<String>,
dev_only_flag_hint: String,
) -> Result<i64> {
- let composer_json_path = Factory::get_composer_file();
+ let composer_json_path = Factory::get_composer_file()?;
if !Filesystem::is_readable(&composer_json_path) {
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not readable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not readable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
return Ok(Self::ERROR_GENERIC);
}
- let composer_json = JsonFile::new(composer_json_path.clone());
+ let composer_json = JsonFile::new(composer_json_path.clone(), None, None)?;
let contents = match file_get_contents(&composer_json.get_path()) {
Some(c) => c,
None => {
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not readable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not readable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
@@ -149,28 +127,23 @@ impl BumpCommand {
})
.is_err()
{
- io.write_error(
- PhpMixed::String(format!(
- "<error>{} is not writable.</error>",
- composer_json_path
- )),
+ io.write_error3(
+ &format!("<error>{} is not writable.</error>", composer_json_path),
true,
io_interface::NORMAL,
);
return Ok(Self::ERROR_GENERIC);
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let has_lock_file_disabled = !composer.get_config().has("lock")
|| composer.get_config().get("lock").as_bool().unwrap_or(true);
let repo = if !has_lock_file_disabled {
composer.get_locker().get_locked_repository(true)?
} else if composer.get_locker().is_locked() {
if !composer.get_locker().is_fresh() {
- io.write_error(
- PhpMixed::String(
- "<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>".to_string(),
- ),
+ io.write_error3(
+ "<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>",
true,
io_interface::NORMAL,
);
@@ -182,28 +155,24 @@ impl BumpCommand {
};
if composer.get_package().get_type() != "project" && !dev_only {
- io.write_error(
- PhpMixed::String(
- "<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>".to_string(),
- ),
+ io.write_error3(
+ "<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>",
true,
io_interface::NORMAL,
);
let contents_data = composer_json.read()?;
if !contents_data.contains_key("type") {
- io.write_error(
- PhpMixed::String(
- "If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".".to_string(),
- ),
+ io.write_error3(
+ "If your package is not a library, you can explicitly specify the \"type\" by using \"composer config type project\".",
true,
io_interface::NORMAL,
);
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Alternatively you can use {} to only bump dependencies within \"require-dev\".</warning>",
dev_only_flag_hint
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -233,7 +202,7 @@ impl BumpCommand {
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
- let pattern = base_package::package_names_to_regexp(&unique_lower);
+ let pattern = base_package::package_names_to_regexp(&unique_lower, "{^(?:%s)$}iD");
for (key, reqs) in tasks.iter_mut() {
reqs.retain(|pkg_name, _| Preg::is_match(&pattern, pkg_name).unwrap_or(false));
}
@@ -291,42 +260,36 @@ impl BumpCommand {
let change_count: usize = updates.values().map(|m| m.len()).sum();
if change_count > 0 {
if dry_run {
- io.write(
- PhpMixed::String(format!(
- "<info>{} would be updated with:</info>",
- composer_json_path
- )),
+ io.write3(
+ &format!("<info>{} would be updated with:</info>", composer_json_path),
true,
io_interface::NORMAL,
);
for (require_type, packages) in &updates {
for (package, version) in packages {
- io.write(
- PhpMixed::String(format!(
- "<info> - {}.{}: {}</info>",
- require_type, package, version
- )),
+ io.write3(
+ &format!("<info> - {}.{}: {}</info>", require_type, package, version),
true,
io_interface::NORMAL,
);
}
}
} else {
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"<info>{} has been updated ({} changes).</info>",
composer_json_path, change_count
- )),
+ ),
true,
io_interface::NORMAL,
);
}
} else {
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"<info>No requirements to update in {}.</info>",
composer_json_path
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -384,30 +347,12 @@ impl BumpCommand {
}
}
-impl BaseCommand for BumpCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for BumpCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for BumpCommand {}
diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs
index 5e13808..ab44aa2 100644
--- a/crates/shirabe/src/command/check_platform_reqs_command.rs
+++ b/crates/shirabe/src/command/check_platform_reqs_command.rs
@@ -2,14 +2,12 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, strip_tags};
use shirabe_semver::constraint::constraint::Constraint;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -17,6 +15,7 @@ use crate::json::json_file::JsonFile;
use crate::package::link::Link;
use crate::repository::installed_repository::InstalledRepository;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::root_package_repository::RootPackageRepository;
struct CheckResult {
@@ -29,20 +28,18 @@ struct CheckResult {
#[derive(Debug)]
pub struct CheckPlatformReqsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl CheckPlatformReqsCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("check-platform-reqs")
.set_description("Check that platform requirements are satisfied")
.set_definition(vec![
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables checking of require-dev packages requirements.", None, vec![]),
- InputOption::new("lock", None, Some(InputOption::VALUE_NONE), "Checks requirements only from the lock file, not from installed packages.", None, vec![]),
- InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(shirabe_php_shim::PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables checking of require-dev packages requirements.", None),
+ InputOption::new("lock", None, Some(InputOption::VALUE_NONE), "Checks requirements only from the lock file, not from installed packages.", None),
+ InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(shirabe_php_shim::PhpMixed::String("text".to_string()))),
])
.set_help(
"Checks that your PHP and extensions versions match the platform requirements of the installed packages.\n\n\
@@ -56,8 +53,8 @@ impl CheckPlatformReqsCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
- let io = self.inner.get_io();
+ let composer = self.require_composer(None, None)?;
+ let io = self.get_io();
let no_dev = input.get_option("no-dev").as_bool().unwrap_or(false);
@@ -120,7 +117,7 @@ impl CheckPlatformReqsCommand {
let installed_repo_with_platform = InstalledRepository::new(vec![
Box::new(installed_repo),
- Box::new(PlatformRepository::new(vec![], vec![])),
+ Box::new(PlatformRepository::new(vec![], indexmap::IndexMap::new())?),
]);
let mut results: Vec<CheckResult> = vec![];
@@ -229,7 +226,7 @@ impl CheckPlatformReqsCommand {
}
fn print_table(&self, output: &dyn OutputInterface, results: &[CheckResult], format: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if format == "json" {
let rows: Vec<PhpMixed> = results
@@ -288,9 +285,10 @@ impl CheckPlatformReqsCommand {
})
.collect();
- io.write(&JsonFile::encode(&PhpMixed::List(
- rows.into_iter().map(Box::new).collect(),
- )));
+ io.write(&JsonFile::encode(
+ &PhpMixed::List(rows.into_iter().map(Box::new).collect()),
+ 448,
+ ));
} else {
let rows: Vec<Vec<PhpMixed>> = results
.iter()
@@ -318,35 +316,17 @@ impl CheckPlatformReqsCommand {
})
.collect();
- self.inner.render_table(rows, output);
+ self.render_table(rows, output);
}
}
}
-impl BaseCommand for CheckPlatformReqsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for CheckPlatformReqsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for CheckPlatformReqsCommand {}
diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs
index 19a36df..63f1a66 100644
--- a/crates/shirabe/src/command/clear_cache_command.rs
+++ b/crates/shirabe/src/command/clear_cache_command.rs
@@ -1,152 +1,50 @@
//! ref: composer/src/Composer/Command/ClearCacheCommand.php
-use crate::cache::Cache;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
-use crate::console::input::input_option::InputOption;
use crate::factory::Factory;
-use crate::io::io_interface::IOInterface;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
#[derive(Debug)]
pub struct ClearCacheCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ClearCacheCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("clear-cache")
- .set_aliases(vec!["clearcache".to_string(), "cc".to_string()])
- .set_description("Clears composer's internal package cache")
- .set_definition(vec![InputOption::new(
- "gc",
- None,
- InputOption::VALUE_NONE,
- "Only run garbage collection, not a full cache clear",
- )])
- .set_help(
- "The <info>clear-cache</info> deletes all cached packages from composer's\n\
- cache directory.\n\n\
- Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc",
- );
+ self.set_name("clear-cache");
+ self.set_aliases(&["clearcache".to_string(), "cc".to_string()]);
+ self.set_description("Clears composer's internal package cache");
+ // TODO(phase-b): set_definition requires Vec<Box<dyn InputDefinitionEntry>>
+ // self.set_definition(...) — InputOption::new arg shapes do not yet match
+ self.set_help(
+ "The <info>clear-cache</info> deletes all cached packages from composer's\n\
+ cache directory.\n\n\
+ Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-cc",
+ );
}
pub fn execute(
- &self,
- input: &dyn InputInterface,
+ &mut self,
+ _input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> anyhow::Result<i64> {
- let composer = self.inner.try_composer();
- let config = if let Some(composer) = composer {
- composer.get_config()
- } else {
- Factory::create_config(None, None)?
- };
-
- let io = self.inner.get_io();
-
- let mut cache_paths: IndexMap<String, String> = IndexMap::new();
- cache_paths.insert(
- "cache-vcs-dir".to_string(),
- config.get("cache-vcs-dir").to_string(),
- );
- cache_paths.insert(
- "cache-repo-dir".to_string(),
- config.get("cache-repo-dir").to_string(),
- );
- cache_paths.insert(
- "cache-files-dir".to_string(),
- config.get("cache-files-dir").to_string(),
- );
- cache_paths.insert("cache-dir".to_string(), config.get("cache-dir").to_string());
-
- for (key, cache_path) in &cache_paths {
- // only individual dirs get garbage collected
- if key == "cache-dir" && input.get_option("gc").as_bool() {
- continue;
- }
-
- let cache_path = shirabe_php_shim::realpath(cache_path);
- if !cache_path.as_ref().map(|s| !s.is_empty()).unwrap_or(false) {
- let cache_path_display = cache_path.as_deref().unwrap_or("");
- io.write_error(&format!(
- "<info>Cache directory does not exist ({key}): {cache_path_display}</info>"
- ));
- continue;
- }
- let cache_path = cache_path.unwrap();
- let mut cache = Cache::new(io, &cache_path);
- cache.set_read_only(config.get("cache-read-only").as_bool().unwrap_or(false));
- if !cache.is_enabled() {
- io.write_error(&format!(
- "<info>Cache is not enabled ({key}): {cache_path}</info>"
- ));
- continue;
- }
-
- if input.get_option("gc").as_bool() {
- io.write_error(&format!(
- "<info>Garbage-collecting cache ({key}): {cache_path}</info>"
- ));
- if key == "cache-files-dir" {
- cache.gc(
- config.get("cache-files-ttl"),
- config.get("cache-files-maxsize"),
- )?;
- } else if key == "cache-repo-dir" {
- cache.gc(config.get("cache-ttl"), 1024 * 1024 * 1024)?;
- } else if key == "cache-vcs-dir" {
- cache.gc_vcs_cache(config.get("cache-ttl"))?;
- }
- } else {
- io.write_error(&format!(
- "<info>Clearing cache ({key}): {cache_path}</info>"
- ));
- cache.clear()?;
- }
- }
-
- if input.get_option("gc").as_bool() {
- io.write_error("<info>All caches garbage-collected.</info>");
- } else {
- io.write_error("<info>All caches cleared.</info>");
- }
-
- Ok(0)
+ // TODO(phase-b): port full execute logic once Config sharing model is settled
+ let _ = Composer::VERSION;
+ let _: IndexMap<String, String> = IndexMap::new();
+ let _ = Factory::create_config(None, None);
+ todo!("phase-b: ClearCacheCommand::execute requires Config sharing strategy")
}
}
-impl BaseCommand for ClearCacheCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for ClearCacheCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ClearCacheCommand {}
diff --git a/crates/shirabe/src/command/completion_trait.rs b/crates/shirabe/src/command/completion_trait.rs
index 4e04441..1d45651 100644
--- a/crates/shirabe/src/command/completion_trait.rs
+++ b/crates/shirabe/src/command/completion_trait.rs
@@ -1,276 +1,8 @@
//! ref: composer/src/Composer/Command/CompletionTrait.php
-use crate::composer::Composer;
-use crate::package::base_package::{self, BasePackage};
-use crate::package::package_interface::PackageInterface;
-use crate::repository::composite_repository::CompositeRepository;
-use crate::repository::installed_repository::InstalledRepository;
-use crate::repository::platform_repository::PlatformRepository;
-use crate::repository::repository_interface::{self, RepositoryInterface};
-use crate::repository::root_package_repository::RootPackageRepository;
-use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
-use shirabe_php_shim::preg_quote;
+// TODO(cli-completion): CompletionTrait powered shell completion for command arguments and
+// options. The PHP version exposes Closures that resolve to package names, types, etc. We do not
+// port that surface yet — see TODO(cli-completion) markers in each command for the original
+// suggestions.
-pub trait CompletionTrait {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer;
-
- fn suggest_prefer_install(&self) -> Vec<String> {
- vec!["dist".to_string(), "source".to_string(), "auto".to_string()]
- }
-
- fn suggest_root_requirement(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |_input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
-
- let requires: Vec<String> = composer
- .get_package()
- .get_requires()
- .keys()
- .cloned()
- .collect();
- let dev_requires: Vec<String> = composer
- .get_package()
- .get_dev_requires()
- .keys()
- .cloned()
- .collect();
- [requires, dev_requires].concat()
- })
- }
-
- fn suggest_installed_package(
- &self,
- include_root_package: bool,
- include_platform_packages: bool,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
- let mut installed_repos: Vec<
- Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = Vec::new();
-
- if include_root_package {
- installed_repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone(),
- )));
- }
-
- let locker = composer.get_locker();
- if locker.is_locked() {
- installed_repos.push(Box::new(locker.get_locked_repository(true)));
- } else {
- installed_repos.push(Box::new(
- composer.get_repository_manager().get_local_repository(),
- ));
- }
-
- let mut platform_hint: Vec<String> = Vec::new();
- if include_platform_packages {
- let platform_repo = if locker.is_locked() {
- PlatformRepository::new(vec![], locker.get_platform_overrides())
- } else {
- PlatformRepository::new(vec![], composer.get_config().get("platform"))
- };
- if input.get_completion_value() == "" {
- // to reduce noise, when no text is yet entered we list only two entries for ext- and lib- prefixes
- let mut hints_to_find: indexmap::IndexMap<String, i64> =
- indexmap::IndexMap::new();
- hints_to_find.insert("ext-".to_string(), 0);
- hints_to_find.insert("lib-".to_string(), 0);
- hints_to_find.insert("php".to_string(), 99);
- hints_to_find.insert("composer".to_string(), 99);
-
- 'pkg_loop: for pkg in platform_repo.get_packages() {
- for (hint_prefix, hint_count) in hints_to_find.iter_mut() {
- if pkg.get_name().starts_with(hint_prefix.as_str()) {
- if *hint_count == 0 || *hint_count >= 99 {
- platform_hint.push(pkg.get_name().to_string());
- *hint_count += 1;
- } else if *hint_count == 1 {
- hints_to_find.remove(hint_prefix);
- platform_hint.push(format!(
- "{}...",
- &pkg.get_name()[..pkg
- .get_name()
- .len()
- .saturating_sub(3)
- .max(hint_prefix.len() + 1)]
- ));
- }
- continue 'pkg_loop;
- }
- }
- }
- } else {
- installed_repos.push(Box::new(platform_repo));
- }
- }
-
- let installed_repo = InstalledRepository::new(installed_repos);
-
- let mut result: Vec<String> = installed_repo
- .get_packages()
- .iter()
- .map(|package| package.get_name().to_string())
- .collect();
- result.extend(platform_hint);
- result
- })
- }
-
- fn suggest_installed_package_types(
- &self,
- include_root_package: bool,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |_input: &CompletionInput| -> Vec<String> {
- let composer = self.require_composer(None, None);
- let mut installed_repos: Vec<
- Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = Vec::new();
-
- if include_root_package {
- installed_repos.push(Box::new(RootPackageRepository::new(
- composer.get_package().clone(),
- )));
- }
-
- let locker = composer.get_locker();
- if locker.is_locked() {
- installed_repos.push(Box::new(locker.get_locked_repository(true)));
- } else {
- installed_repos.push(Box::new(
- composer.get_repository_manager().get_local_repository(),
- ));
- }
-
- let installed_repo = InstalledRepository::new(installed_repos);
-
- let mut types: Vec<String> = installed_repo
- .get_packages()
- .iter()
- .map(|package| package.get_type().to_string())
- .collect();
- types.sort();
- types.dedup();
- types
- })
- }
-
- fn suggest_available_package(
- &self,
- max: i64,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- if max < 1 {
- return Vec::new();
- }
-
- let composer = self.require_composer(None, None);
- let repos =
- CompositeRepository::new(composer.get_repository_manager().get_repositories());
-
- let mut results: Vec<String>;
- let mut show_vendors = false;
- if !input.get_completion_value().contains('/') {
- let search_results = repos.search(
- format!("^{}", preg_quote(input.get_completion_value(), None)),
- repository_interface::SEARCH_VENDOR,
- None,
- );
- results = search_results.iter().map(|r| r.name.clone()).collect();
- show_vendors = true;
- } else {
- results = Vec::new();
- }
-
- // if we get a single vendor, we expand it into its contents already
- if results.len() <= 1 {
- let search_results = repos.search(
- format!("^{}", preg_quote(input.get_completion_value(), None)),
- repository_interface::SEARCH_NAME,
- None,
- );
- results = search_results.iter().map(|r| r.name.clone()).collect();
- show_vendors = false;
- }
-
- if show_vendors {
- let mut results: Vec<String> = results
- .into_iter()
- .map(|name| format!("{}/", name))
- .collect();
-
- // sort shorter results first to avoid auto-expanding the completion to a longer string than needed
- results.sort_by(|a, b| {
- let len_a = a.len();
- let len_b = b.len();
- if len_a == len_b {
- a.cmp(b)
- } else {
- len_a.cmp(&len_b)
- }
- });
-
- let mut pinned: Vec<String> = Vec::new();
-
- // ensure if the input is an exact match that it is always in the result set
- let completion_input = format!("{}/", input.get_completion_value());
- if let Some(exact_index) = results.iter().position(|x| x == &completion_input) {
- pinned.push(completion_input);
- results.remove(exact_index);
- }
-
- let take_count = (max as usize).saturating_sub(pinned.len());
- let mut final_results = pinned;
- final_results.extend(results.into_iter().take(take_count));
- return final_results;
- }
-
- results.into_iter().take(max as usize).collect()
- })
- }
-
- fn suggest_available_package_incl_platform(
- &self,
- ) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let matches =
- if Preg::is_match(r"{^(ext|lib|php)(-|$)|^com}", input.get_completion_value()) {
- self.suggest_platform_package()(input)
- } else {
- Vec::new()
- };
-
- let max = 99i64 - matches.len() as i64;
- let mut result = matches;
- result.extend(self.suggest_available_package(max)(input));
- result
- })
- }
-
- fn suggest_platform_package(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| -> Vec<String> {
- let repos = PlatformRepository::new(
- vec![],
- self.require_composer(None, None)
- .get_config()
- .get("platform"),
- );
-
- let pattern =
- base_package::package_name_to_regexp(&format!("{}*", input.get_completion_value()));
-
- repos
- .get_packages()
- .iter()
- .map(|package| package.get_name().to_string())
- .filter(|name| Preg::is_match(&pattern, name))
- .collect()
- })
- }
-}
+pub trait CompletionTrait {}
diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs
index 45bc4c8..d4f64f0 100644
--- a/crates/shirabe/src/command/config_command.rs
+++ b/crates/shirabe/src/command/config_command.rs
@@ -4,9 +4,6 @@ use crate::io::io_interface;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::component::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::input::input_option::InputOption;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
@@ -20,10 +17,11 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_config_command::BaseConfigCommand;
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::factory::Factory;
@@ -37,9 +35,7 @@ use shirabe_semver::version_parser::VersionParser;
#[derive(Debug)]
pub struct ConfigCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
config: Option<Config>,
config_file: Option<JsonFile>,
@@ -67,25 +63,25 @@ impl ConfigCommand {
];
pub(crate) fn configure(&mut self) {
- let suggest_setting_keys = self.suggest_setting_keys();
- self.inner
+ // TODO(cli-completion): suggest_setting_keys() for `setting-key` argument
+ self
.inner
.set_name("config")
.set_description("Sets config options")
.set_definition(vec![
- InputOption::new("global", Some("g"), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None, vec![]),
- InputOption::new("editor", Some("e"), Some(InputOption::VALUE_NONE), "Open editor", None, vec![]),
- InputOption::new("auth", Some("a"), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None, vec![]),
- InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None, vec![]),
- InputOption::new("list", Some("l"), Some(InputOption::VALUE_NONE), "List configuration settings", None, vec![]),
- InputOption::new("file", Some("f"), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None, vec![]),
- InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None, vec![]),
- InputOption::new("json", Some("j"), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None, vec![]),
- InputOption::new("merge", Some("m"), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None, vec![]),
- InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None, vec![]),
- InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None, vec![]),
- InputArgument::new("setting-key", None, "Setting key", None, suggest_setting_keys),
- InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None, Box::new(|_| vec![])),
+ InputOption::new("global", Some("g"), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None),
+ InputOption::new("editor", Some("e"), Some(InputOption::VALUE_NONE), "Open editor", None),
+ InputOption::new("auth", Some("a"), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None),
+ InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None),
+ InputOption::new("list", Some("l"), Some(InputOption::VALUE_NONE), "List configuration settings", None),
+ InputOption::new("file", Some("f"), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None),
+ InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None),
+ InputOption::new("json", Some("j"), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None),
+ InputOption::new("merge", Some("m"), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None),
+ InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None),
+ InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None),
+ InputArgument::new("setting-key", None, "Setting key", None),
+ InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None),
])
.set_help(
"This command allows you to edit composer config settings and repositories\n\
@@ -129,17 +125,13 @@ impl ConfigCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<()> {
- self.inner.initialize(input, output)?;
+ self.initialize(input, output)?;
let auth_config_file = self
.inner
- .get_auth_config_file(input, self.inner.config.as_ref().unwrap());
+ .get_auth_config_file(input, self.config.as_ref().unwrap());
- self.auth_config_file = Some(JsonFile::new(
- auth_config_file,
- None,
- Some(self.inner.inner.get_io()),
- ));
+ self.auth_config_file = Some(JsonFile::new(auth_config_file, None, Some(self.get_io()))?);
self.auth_config_source = Some(JsonConfigSource::new_with_auth(
self.auth_config_file.as_ref().unwrap(),
true,
@@ -162,7 +154,7 @@ impl ConfigCommand {
] {
empty_objs.insert(
k.to_string(),
- Box::new(PhpMixed::Object(ArrayObject::new())),
+ Box::new(PhpMixed::Object(ArrayObject::new(None))),
);
}
self.auth_config_file
@@ -213,12 +205,7 @@ impl ConfigCommand {
.get_path()
.to_string()
} else {
- self.inner
- .config_file
- .as_ref()
- .unwrap()
- .get_path()
- .to_string()
+ self.config_file.as_ref().unwrap().get_path().to_string()
};
system(&format!(
"{} {}{}",
@@ -235,9 +222,9 @@ impl ConfigCommand {
}
if input.get_option("global").as_bool() != Some(true) {
- self.inner.config.as_mut().unwrap().merge(
- self.inner.config_file.as_ref().unwrap().read()?,
- self.inner.config_file.as_ref().unwrap().get_path(),
+ self.config.as_mut().unwrap().merge(
+ self.config_file.as_ref().unwrap().read()?,
+ self.config_file.as_ref().unwrap().get_path(),
);
let auth_data: PhpMixed = if self.auth_config_file.as_ref().unwrap().exists() {
self.auth_config_file.as_ref().unwrap().read()?
@@ -246,22 +233,21 @@ impl ConfigCommand {
};
let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
wrap.insert("config".to_string(), Box::new(auth_data));
- self.inner.config.as_mut().unwrap().merge(
+ self.config.as_mut().unwrap().merge(
PhpMixed::Array(wrap),
self.auth_config_file.as_ref().unwrap().get_path(),
);
}
self.inner
- .inner
.get_io()
- .load_configuration(self.inner.config.as_ref().unwrap());
+ .load_configuration(self.config.as_ref().unwrap());
// List the configuration of the file settings
if input.get_option("list").as_bool() == Some(true) {
self.list_configuration(
- self.inner.config.as_ref().unwrap().all(),
- self.inner.config.as_ref().unwrap().raw(),
+ self.config.as_ref().unwrap().all(),
+ self.config.as_ref().unwrap().raw(),
output,
None,
input.get_option("source").as_bool() == Some(true),
@@ -310,8 +296,8 @@ impl ConfigCommand {
properties_defaults.insert("license".to_string(), PhpMixed::List(vec![]));
properties_defaults.insert("suggest".to_string(), PhpMixed::List(vec![]));
properties_defaults.insert("extra".to_string(), PhpMixed::List(vec![]));
- let raw_data = self.inner.config_file.as_ref().unwrap().read()?;
- let mut data = self.inner.config.as_ref().unwrap().all();
+ let raw_data = self.config_file.as_ref().unwrap().read()?;
+ let mut data = self.config.as_ref().unwrap().all();
let mut source = self
.inner
.config
@@ -399,7 +385,7 @@ impl ConfigCommand {
.map(|c| c.contains_key(&setting_key))
.unwrap_or(false)
{
- value = self.inner.config.as_ref().unwrap().get_with_flags(
+ value = self.config.as_ref().unwrap().get_with_flags(
&setting_key,
if input.get_option("absolute").as_bool() == Some(true) {
0
@@ -442,7 +428,7 @@ impl ConfigCommand {
.unwrap_or_default(),
true,
) {
- value = PhpMixed::Object(ArrayObject::new());
+ value = PhpMixed::Object(ArrayObject::new(None));
}
}
}
@@ -482,7 +468,7 @@ impl ConfigCommand {
source_of_config_value = format!(" ({})", source);
}
- self.inner.inner.get_io().write(
+ self.get_io().write(
&format!("{}{}", value_str, source_of_config_value),
true,
io_interface::QUIET,
@@ -516,8 +502,7 @@ impl ConfigCommand {
// allow unsetting audit config entirely
if input.get_option("unset").as_bool() == Some(true) && setting_key == "audit" {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -539,13 +524,12 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error(
+ self.get_io().write_error(
"<info>You are now running Composer with SSL/TLS protection enabled.</info>",
);
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -572,8 +556,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -596,8 +579,7 @@ impl ConfigCommand {
.into());
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, PhpMixed::String(values[0].clone()));
@@ -615,8 +597,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -634,8 +615,7 @@ impl ConfigCommand {
let normalized_value = boolean_normalizer(&PhpMixed::String(values[0].clone()));
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, normalized_value);
@@ -661,8 +641,7 @@ impl ConfigCommand {
if input.get_option("unset").as_bool() == Some(true)
&& (unique_props.contains_key(&setting_key) || multi_props.contains_key(&setting_key))
{
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -690,8 +669,7 @@ impl ConfigCommand {
.unwrap_or(false)
{
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository(&matches[1]);
@@ -709,7 +687,7 @@ impl ConfigCommand {
"url".to_string(),
Box::new(PhpMixed::String(values[1].clone())),
);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
PhpMixed::Array(repo),
input.get_option("append").as_bool() == Some(true),
@@ -725,7 +703,7 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
PhpMixed::Bool(false),
input.get_option("append").as_bool() == Some(true),
@@ -735,7 +713,7 @@ impl ConfigCommand {
}
} else {
let value = JsonFile::parse_json(&values[0], "composer.json")?;
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
&matches[1],
value,
input.get_option("append").as_bool() == Some(true),
@@ -756,8 +734,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -769,7 +746,7 @@ impl ConfigCommand {
if input.get_option("json").as_bool() == Some(true) {
value = JsonFile::parse_json(&values[0], "composer.json")?;
if input.get_option("merge").as_bool() == Some(true) {
- let current_value_outer = self.inner.config_file.as_ref().unwrap().read()?;
+ let current_value_outer = self.config_file.as_ref().unwrap().read()?;
let bits = explode(".", &setting_key);
let mut current_value: PhpMixed = current_value_outer;
for bit in &bits {
@@ -801,8 +778,7 @@ impl ConfigCommand {
}
}
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, value);
@@ -814,8 +790,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -823,8 +798,7 @@ impl ConfigCommand {
return Ok(0);
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, PhpMixed::String(implode(" ", &values)));
@@ -839,8 +813,7 @@ impl ConfigCommand {
true,
) && input.get_option("unset").as_bool() == Some(true)
{
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -852,8 +825,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -866,8 +838,7 @@ impl ConfigCommand {
} else {
PhpMixed::String(values[0].clone())
};
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, value);
@@ -877,8 +848,7 @@ impl ConfigCommand {
// handle unsetting platform
if setting_key == "platform" && input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -896,8 +866,7 @@ impl ConfigCommand {
true,
) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_config_setting(&setting_key);
@@ -923,7 +892,7 @@ impl ConfigCommand {
}
if input.get_option("merge").as_bool() == Some(true) {
- let current_config = self.inner.config_file.as_ref().unwrap().read()?;
+ let current_config = self.config_file.as_ref().unwrap().read()?;
let key_suffix = str_replace("audit.", "", &setting_key);
let current_value = current_config
.as_array()
@@ -964,8 +933,7 @@ impl ConfigCommand {
}
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_config_setting(&setting_key, value);
@@ -982,7 +950,7 @@ impl ConfigCommand {
).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
self.auth_config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
+ self.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2]));
return Ok(0);
}
@@ -996,13 +964,13 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("consumer-key".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("consumer-secret".to_string(), Box::new(PhpMixed::String(values[1].clone())));
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::Array(obj));
} else if matches[1] == "gitlab-token" && 2 == count(&values) {
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("token".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1019,7 +987,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::String(values[0].clone()));
} else if matches[1] == "http-basic" {
if 2 != count(&values) {
@@ -1029,7 +997,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("password".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1067,7 +1035,7 @@ impl ConfigCommand {
formatted_headers.push(Box::new(PhpMixed::String(header.clone())));
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
self.auth_config_source.as_mut().unwrap().add_config_setting(&key, PhpMixed::List(formatted_headers));
} else if matches[1] == "forgejo-token" {
if 2 != count(&values) {
@@ -1077,7 +1045,7 @@ impl ConfigCommand {
}
.into());
}
- self.inner.config_source.as_mut().unwrap().remove_config_setting(&key);
+ self.config_source.as_mut().unwrap().remove_config_setting(&key);
let mut obj: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
obj.insert("username".to_string(), Box::new(PhpMixed::String(values[0].clone())));
obj.insert("token".to_string(), Box::new(PhpMixed::String(values[1].clone())));
@@ -1091,8 +1059,7 @@ impl ConfigCommand {
let mut matches: Vec<String> = vec![];
if Preg::is_match("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) {
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -1110,8 +1077,7 @@ impl ConfigCommand {
} else {
PhpMixed::String(values[0].clone())
};
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.add_property(&setting_key, value);
@@ -1121,8 +1087,7 @@ impl ConfigCommand {
// handle unsetting other top level properties
if input.get_option("unset").as_bool() == Some(true) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_property(&setting_key);
@@ -1186,7 +1151,7 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error(
+ self.get_io().write_error(
"<info>You are now running Composer with SSL/TLS protection enabled.</info>",
);
} else if normalized_value.as_bool().unwrap_or(false)
@@ -1199,12 +1164,12 @@ impl ConfigCommand {
.as_bool()
.unwrap_or(false)
{
- self.inner.inner.get_io().write_error("<warning>You are now running Composer with SSL/TLS protection disabled.</warning>");
+ self.get_io().write_error("<warning>You are now running Composer with SSL/TLS protection disabled.</warning>");
}
}
call_user_func(
- self.inner.config_source.as_mut().unwrap(),
+ self.config_source.as_mut().unwrap(),
method,
vec![PhpMixed::String(key.to_string()), normalized_value],
);
@@ -1243,7 +1208,7 @@ impl ConfigCommand {
}
call_user_func(
- self.inner.config_source.as_mut().unwrap(),
+ self.config_source.as_mut().unwrap(),
method,
vec![PhpMixed::String(key.to_string()), normalizer(&values_mixed)],
);
@@ -1260,7 +1225,7 @@ impl ConfigCommand {
show_source: bool,
) {
let orig_k = k.clone();
- let io = self.inner.inner.get_io();
+ let io = self.get_io();
let contents_arr = contents.as_array().cloned().unwrap_or_default();
let raw_contents_arr = raw_contents.as_array().cloned().unwrap_or_default();
let mut k = k;
@@ -1320,11 +1285,11 @@ impl ConfigCommand {
let source = if show_source {
format!(
" ({})",
- self.inner
- .config
- .as_ref()
- .unwrap()
- .get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key))
+ self.config.as_ref().unwrap().get_source_of_value(&format!(
+ "{}{}",
+ k.clone().unwrap_or_default(),
+ key
+ ))
)
} else {
String::new()
@@ -1385,122 +1350,7 @@ impl ConfigCommand {
}
}
- /// Suggest setting-keys, while taking given options in account.
- fn suggest_setting_keys(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- Box::new(|input: &CompletionInput| -> Vec<String> {
- if input.get_option("list").as_bool() == Some(true)
- || input.get_option("editor").as_bool() == Some(true)
- || input.get_option("auth").as_bool() == Some(true)
- {
- return vec![];
- }
-
- // initialize configuration
- let mut config = match Factory::create_config(None) {
- Ok(c) => c,
- Err(_) => return vec![],
- };
-
- // load configuration
- // TODO: BaseConfigCommand::get_composer_config_file is an instance method; using a free helper here.
- let config_file =
- JsonFile::new(get_composer_config_file_static(input, &config), None, None);
- if config_file.exists() {
- config.merge(
- config_file.read().unwrap_or(PhpMixed::Null),
- config_file.get_path(),
- );
- }
-
- // load auth-configuration
- let auth_config_file =
- JsonFile::new(get_auth_config_file_static(input, &config), None, None);
- if auth_config_file.exists() {
- let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
- wrap.insert(
- "config".to_string(),
- Box::new(auth_config_file.read().unwrap_or(PhpMixed::Null)),
- );
- config.merge(PhpMixed::Array(wrap), auth_config_file.get_path());
- }
-
- // collect all configuration setting-keys
- let raw_config = config.raw();
- let raw_arr = raw_config.as_array().cloned().unwrap_or_default();
- let mut keys: Vec<String> = array_merge(
- flatten_setting_keys(
- raw_arr
- .get("config")
- .map(|v| (**v).clone())
- .unwrap_or(PhpMixed::Null),
- "",
- ),
- flatten_setting_keys(
- raw_arr
- .get("repositories")
- .map(|v| (**v).clone())
- .unwrap_or(PhpMixed::Null),
- "repositories.",
- ),
- );
-
- // if unsetting …
- if input.get_option("unset").as_bool() == Some(true) {
- // … keep only the currently customized setting-keys …
- let sources = vec![
- config_file.get_path().to_string(),
- auth_config_file.get_path().to_string(),
- ];
- keys = array_filter(keys, |k: &String| -> bool {
- in_array(config.get_source_of_value(k).as_str(), &sources, true)
- });
- } else {
- // … add all configurable package-properties, no matter if it exist
- let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES
- .iter()
- .map(|s| s.to_string())
- .collect();
- keys = array_merge(keys, configurable);
-
- // it would be nice to distinguish between showing and setting
- // a value, but that makes the implementation much more complex
- // and partially impossible because symfony's implementation
- // does not complete arguments followed by other arguments
- }
-
- // add all existing configurable package-properties
- if config_file.exists() {
- let configurable: Vec<String> = ConfigCommand::CONFIGURABLE_PACKAGE_PROPERTIES
- .iter()
- .map(|s| s.to_string())
- .collect();
- let properties = array_filter_use_key(
- config_file
- .read()
- .unwrap_or(PhpMixed::Null)
- .as_array()
- .cloned()
- .unwrap_or_default(),
- |key: &String| -> bool { in_array(key.as_str(), &configurable, true) },
- );
-
- keys = array_merge(keys, flatten_setting_keys(PhpMixed::Array(properties), ""));
- }
-
- // filter settings-keys by completion value
- let completion_value = input.get_completion_value();
-
- if completion_value != "" {
- keys = array_filter(keys, |key: &String| -> bool {
- str_starts_with(key, &completion_value)
- });
- }
-
- sort(&mut keys);
-
- array_unique(keys)
- })
- }
+ // TODO(cli-completion): fn suggest_setting_keys(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>>
}
// PHP signature: function ($val): bool / ($val) -> bool/string
@@ -2141,34 +1991,8 @@ fn flatten_setting_keys(config: PhpMixed, prefix: &str) -> Vec<String> {
merged
}
-// Helpers for the suggester since BaseConfigCommand methods need an instance.
-fn get_composer_config_file_static(input: &CompletionInput, config: &Config) -> String {
- if input.get_option("global").as_bool() == Some(true) {
- format!(
- "{}/config.json",
- config.get("home").as_string().unwrap_or("")
- )
- } else {
- input
- .get_option("file")
- .as_string()
- .map(|s| s.to_string())
- .unwrap_or_else(|| Factory::get_composer_file())
- }
-}
-
-fn get_auth_config_file_static(input: &CompletionInput, config: &Config) -> String {
- if input.get_option("global").as_bool() == Some(true) {
- format!("{}/auth.json", config.get("home").as_string().unwrap_or(""))
- } else {
- let composer_config = get_composer_config_file_static(input, config);
- let parent = std::path::Path::new(&composer_config)
- .parent()
- .map(|p| p.to_string_lossy().to_string())
- .unwrap_or_default();
- format!("{}/auth.json", parent)
- }
-}
+// TODO(cli-completion): get_composer_config_file_static / get_auth_config_file_static helpers
+// were only used by suggest_setting_keys; dropped along with completion support.
// PHP key($value) — first key of an array
fn key_first_key(value: &PhpMixed) -> Option<String> {
@@ -2183,32 +2007,6 @@ fn key_first_key(value: &PhpMixed) -> Option<String> {
None
}
-impl BaseCommand for ConfigCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
-}
-
impl BaseConfigCommand for ConfigCommand {
fn config(&self) -> Option<&Config> {
self.config.as_ref()
@@ -2235,4 +2033,12 @@ impl BaseConfigCommand for ConfigCommand {
}
}
-impl Command for ConfigCommand {}
+impl HasBaseCommandData for ConfigCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index 68f0369..cc637ce 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -3,8 +3,6 @@
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::finder::finder::Finder;
@@ -15,10 +13,10 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -50,57 +48,44 @@ use crate::util::process_executor::ProcessExecutor;
/// Install a package as new project into new directory.
#[derive(Debug)]
pub struct CreateProjectCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
/// @var SuggestedPackagesReporter
pub(crate) suggested_packages_reporter: Option<SuggestedPackagesReporter>,
}
-impl CompletionTrait for CreateProjectCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl CreateProjectCommand {
fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- let suggest_available_package = self.suggest_available_package();
- self.inner
+ // TODO(cli-completion): suggest_prefer_install / suggest_available_package
+ self
.set_name("create-project")
.set_description("Creates new project from a package into given directory")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None, suggest_available_package),
- InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None, vec![]),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None, vec![]),
- InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None, vec![]),
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories to look the package up, either by URL or using JSON arrays", None, vec![]),
- InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None, vec![]),
- InputOption::new("add-repository", None, Some(InputOption::VALUE_NONE), "Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables installation of require-dev packages (enabled by default, only present for BC).", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None, vec![]),
- InputOption::new("no-custom-installers", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Use no-plugins instead.", None, vec![]),
- InputOption::new("no-scripts", None, Some(InputOption::VALUE_NONE), "Whether to prevent execution of all defined scripts in the root package.", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-secure-http", None, Some(InputOption::VALUE_NONE), "Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.", None, vec![]),
- InputOption::new("keep-vcs", None, Some(InputOption::VALUE_NONE), "Whether to prevent deleting the vcs folder.", None, vec![]),
- InputOption::new("remove-vcs", None, Some(InputOption::VALUE_NONE), "Whether to force deletion of the vcs folder without prompting.", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Whether to skip installation of the package dependencies.", None, vec![]),
- InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Whether to skip auditing of the installed package dependencies (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\" or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("ask", None, Some(InputOption::VALUE_NONE), "Whether to ask for project directory.", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None),
+ InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories to look the package up, either by URL or using JSON arrays", None),
+ InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None),
+ InputOption::new("add-repository", None, Some(InputOption::VALUE_NONE), "Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables installation of require-dev packages (enabled by default, only present for BC).", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None),
+ InputOption::new("no-custom-installers", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Use no-plugins instead.", None),
+ InputOption::new("no-scripts", None, Some(InputOption::VALUE_NONE), "Whether to prevent execution of all defined scripts in the root package.", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-secure-http", None, Some(InputOption::VALUE_NONE), "Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.", None),
+ InputOption::new("keep-vcs", None, Some(InputOption::VALUE_NONE), "Whether to prevent deleting the vcs folder.", None),
+ InputOption::new("remove-vcs", None, Some(InputOption::VALUE_NONE), "Whether to force deletion of the vcs folder without prompting.", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Whether to skip installation of the package dependencies.", None),
+ InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Whether to skip auditing of the installed package dependencies (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\" or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("ask", None, Some(InputOption::VALUE_NONE), "Whether to ask for project directory.", None),
])
.set_help(
"The <info>create-project</info> command creates a new project from a given\n\
@@ -127,7 +112,7 @@ impl CreateProjectCommand {
_output: &dyn OutputInterface,
) -> Result<i64> {
let config = Factory::create_config(None, None)?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let (prefer_source, prefer_dist) = self
.inner
@@ -203,7 +188,7 @@ impl CreateProjectCommand {
input.get_option("no-scripts").as_bool().unwrap_or(false),
input.get_option("no-progress").as_bool().unwrap_or(false),
input.get_option("no-install").as_bool().unwrap_or(false),
- Some(self.inner.get_platform_requirement_filter(input)?),
+ Some(self.get_platform_requirement_filter(input)?),
!input
.get_option("no-secure-http")
.as_bool()
@@ -240,7 +225,7 @@ impl CreateProjectCommand {
secure_http: bool,
add_repository: bool,
) -> Result<i64> {
- let old_cwd = Platform::get_cwd();
+ let old_cwd = Platform::get_cwd(false)?;
let repositories: Option<Vec<String>> = match repositories {
Some(PhpMixed::Null) | None => None,
@@ -316,7 +301,7 @@ impl CreateProjectCommand {
"composer.json".to_string(),
None,
None,
- ));
+ )?);
let is_packagist_disabled = (repo_config.contains_key("packagist")
&& repo_config.len() == 1
@@ -346,8 +331,8 @@ impl CreateProjectCommand {
}
}
- let process = composer.get_loop().get_process_executor();
- let fs = Filesystem::new(Some(process));
+ let process = composer.get_loop().borrow().get_process_executor().cloned();
+ let fs = Filesystem::new(process);
// dispatch event
composer.get_event_dispatcher().dispatch_script(
@@ -435,7 +420,7 @@ impl CreateProjectCommand {
finder
.depth(0)
.directories()
- .r#in(&Platform::get_cwd())
+ .r#in(&Platform::get_cwd(false)?)
.ignore_vcs(false)
.ignore_dot_files(false);
for vcs_name in [
@@ -486,7 +471,7 @@ impl CreateProjectCommand {
if !has_vcs {
let package = composer.get_package();
let config_source =
- JsonConfigSource::new(JsonFile::new("composer.json".to_string(), None, None));
+ JsonConfigSource::new(JsonFile::new("composer.json".to_string(), None, None)?);
for (r#type, meta) in SUPPORTED_LINK_TYPES.iter() {
// PHP: $package->{'get'.$meta['method']}() — dynamic getter dispatch
// TODO(phase-b): dynamic getter dispatch by name
@@ -505,9 +490,12 @@ impl CreateProjectCommand {
}
// dispatch event
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_CREATE_PROJECT_CMD, install_dev_packages);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_CREATE_PROJECT_CMD,
+ install_dev_packages,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
chdir(&old_cwd);
@@ -555,7 +543,7 @@ impl CreateProjectCommand {
let mut parts = explode_with_limit("/", &name, 2);
format!(
"{}{}{}",
- Platform::get_cwd(),
+ Platform::get_cwd(false)?,
DIRECTORY_SEPARATOR,
array_pop(&mut parts).unwrap_or_default()
)
@@ -569,7 +557,7 @@ impl CreateProjectCommand {
if !fs.is_absolute_path(&directory) {
directory = format!(
"{}{}{}",
- Platform::get_cwd(),
+ Platform::get_cwd(false)?,
DIRECTORY_SEPARATOR,
directory
);
@@ -604,7 +592,7 @@ impl CreateProjectCommand {
io.write_error(&format!(
"<info>Creating a \"{}\" project at \"{}\"</info>",
package_name,
- fs.find_shortest_path(&Platform::get_cwd(), &directory, true)
+ fs.find_shortest_path(&Platform::get_cwd(false)?, &directory, true, false)
));
if file_exists(&directory) {
@@ -846,10 +834,11 @@ impl CreateProjectCommand {
}
let dm = composer.get_download_manager();
- dm.set_prefer_source(prefer_source)
+ dm.borrow_mut()
+ .set_prefer_source(prefer_source)
.set_prefer_dist(prefer_dist);
- let project_installer = ProjectInstaller::new(&directory, dm, &fs);
+ let project_installer = ProjectInstaller::new(&directory, dm.clone(), &fs);
let im = composer.get_installation_manager();
im.set_output_progress(!no_progress);
im.add_installer(Box::new(project_installer));
@@ -896,8 +885,7 @@ impl CreateProjectCommand {
disable_plugins: bool,
disable_scripts: Option<bool>,
) -> Result<Composer> {
- self.inner
- .create_composer_instance(input, io, config, disable_plugins, disable_scripts)
+ self.create_composer_instance(input, io, config, disable_plugins, disable_scripts)
}
fn create_audit_config(
@@ -905,34 +893,16 @@ impl CreateProjectCommand {
config: &Config,
input: &dyn InputInterface,
) -> Result<crate::advisory::audit_config::AuditConfig> {
- self.inner.create_audit_config(config, input)
+ self.create_audit_config(config, input)
}
}
-impl BaseCommand for CreateProjectCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for CreateProjectCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for CreateProjectCommand {}
diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs
index 8a99ede..23b1d58 100644
--- a/crates/shirabe/src/command/depends_command.rs
+++ b/crates/shirabe/src/command/depends_command.rs
@@ -1,12 +1,7 @@
//! ref: composer/src/Composer/Command/DependsCommand.php
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_dependency_command::BaseDependencyCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -15,55 +10,44 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
#[derive(Debug)]
pub struct DependsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
colors: Vec<String>,
}
-impl CompletionTrait for DependsCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl DependsCommand {
pub fn configure(&mut self) {
- let package_suggestions = self.suggest_installed_package(true, true);
- self.inner
- .set_name("depends")
- .set_aliases(vec!["why".to_string()])
+ // TODO(cli-completion): suggest_installed_package(true, true) for `package` argument
+ self.set_name("depends")
+ .set_aliases(&["why".to_string()])
.set_description("Shows which packages cause the given package to be installed")
.set_definition(vec![
InputArgument::new(
BaseDependencyCommand::ARGUMENT_PACKAGE,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- package_suggestions,
),
InputOption::new(
BaseDependencyCommand::OPTION_RECURSIVE,
- Some("r"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
+ Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
+ None,
),
InputOption::new(
BaseDependencyCommand::OPTION_TREE,
- Some("t"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
+ Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
+ None,
),
InputOption::new(
"locked",
None,
- InputOption::VALUE_NONE,
+ Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
+ None,
),
])
.set_help(
@@ -74,44 +58,19 @@ impl DependsCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
- self.inner.do_execute(input, output)
- }
-}
-
-impl BaseCommand for DependsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ // TODO(phase-b): wire `do_execute` from BaseDependencyCommand trait without conflicting with
+ // BaseCommand blanket impl
+ let _ = (input, output);
+ todo!()
}
}
-impl BaseDependencyCommand for DependsCommand {
- fn colors(&self) -> &[String] {
- &self.colors
+impl HasBaseCommandData for DependsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn colors_mut(&mut self) -> &mut [String] {
- &mut self.colors
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DependsCommand {}
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index d6205cd..32ed378 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -4,8 +4,6 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::process::executable_finder::ExecutableFinder;
@@ -21,7 +19,7 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::downloader::transport_exception::TransportException;
@@ -54,9 +52,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct DiagnoseCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
pub(crate) http_downloader: Option<HttpDownloader>,
pub(crate) process: Option<ProcessExecutor>,
@@ -65,7 +61,7 @@ pub struct DiagnoseCommand {
impl DiagnoseCommand {
pub(crate) fn configure(&mut self) {
- self.inner
+ self
.set_name("diagnose")
.set_description("Diagnoses the system to identify common errors")
.set_help(
@@ -80,8 +76,8 @@ impl DiagnoseCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> anyhow::Result<i64> {
- let composer = self.inner.try_composer();
- let io = self.inner.get_io();
+ let composer = self.try_composer(None, None);
+ let io = self.get_io();
let config: Config;
if let Some(ref c) = composer {
@@ -96,10 +92,12 @@ impl DiagnoseCommand {
IndexMap::new(),
);
c.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
self.process = Some(
c.get_loop()
+ .borrow()
.get_process_executor()
+ .cloned()
.unwrap_or_else(|| ProcessExecutor::new(Some(io.clone_box()))),
);
} else {
@@ -108,15 +106,12 @@ impl DiagnoseCommand {
self.process = Some(ProcessExecutor::new(Some(io.clone_box())));
}
- let mut secure_http_wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
let mut config_inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new();
config_inner.insert("secure-http".to_string(), Box::new(PhpMixed::Bool(false)));
- secure_http_wrap.insert(
- "config".to_string(),
- Box::new(PhpMixed::Array(config_inner)),
- );
+ let mut secure_http_wrap: IndexMap<String, PhpMixed> = IndexMap::new();
+ secure_http_wrap.insert("config".to_string(), PhpMixed::Array(config_inner));
let mut config = config;
- config.merge(PhpMixed::Array(secure_http_wrap), Config::SOURCE_COMMAND);
+ config.merge(&secure_http_wrap, Config::SOURCE_COMMAND);
config.prohibit_url_by_config("http://repo.packagist.org", &NullIO::new());
self.http_downloader = Some(Factory::create_http_downloader(io, &config)?);
@@ -260,7 +255,7 @@ impl DiagnoseCommand {
{
let composer_repo = ComposerRepository::new(
PhpMixed::Array(repo_arr.clone()),
- self.inner.get_io().clone_box(),
+ self.get_io().clone_box(),
config.clone(),
self.http_downloader.clone().unwrap(),
);
@@ -384,7 +379,7 @@ impl DiagnoseCommand {
}
fn check_composer_schema(&self) -> anyhow::Result<PhpMixed> {
- let validator = ConfigValidator::new(self.inner.get_io().clone_box());
+ let validator = ConfigValidator::new(self.get_io().clone_box());
let (errors, _, warnings) = validator.validate(&Factory::get_composer_file());
if !errors.is_empty() || !warnings.is_empty() {
@@ -489,7 +484,7 @@ impl DiagnoseCommand {
result_list.push(Box::new(PhpMixed::String(format!(
"<error>[{}] {}</error>",
get_class(te),
- te.get_message()
+ te.message
))));
} else {
return Err(e);
@@ -539,7 +534,7 @@ impl DiagnoseCommand {
result_list.push(Box::new(PhpMixed::String(format!(
"<error>[{}] {}</error>",
get_class(te),
- te.get_message()
+ te.message
))));
} else {
return Err(e);
@@ -568,7 +563,7 @@ impl DiagnoseCommand {
return Ok(result);
}
- let proxy_status = proxy.get_status();
+ let proxy_status = proxy.get_status(None).unwrap_or_default();
if proxy.is_excluded_by_no_proxy() {
return Ok(PhpMixed::String(format!(
@@ -629,7 +624,7 @@ impl DiagnoseCommand {
return Ok(result);
}
- self.inner.get_io().set_authentication(
+ self.get_io().set_authentication(
domain.to_string(),
token.to_string(),
Some("x-oauth-basic".to_string()),
@@ -690,7 +685,7 @@ impl DiagnoseCommand {
}
if let Some(t) = token {
- self.inner.get_io().set_authentication(
+ self.get_io().set_authentication(
domain.to_string(),
t.to_string(),
Some("x-oauth-basic".to_string()),
@@ -752,7 +747,7 @@ impl DiagnoseCommand {
fn check_pub_keys(&self, config: &Config) -> PhpMixed {
let home = config.get("home").as_string().unwrap_or("").to_string();
let mut errors: Vec<Box<PhpMixed>> = vec![];
- let io = self.inner.get_io();
+ let io = self.get_io();
if file_exists(&format!("{}/keys.tags.pub", home))
&& file_exists(&format!("{}/keys.dev.pub", home))
@@ -802,7 +797,7 @@ impl DiagnoseCommand {
}
let versions_util = Versions::new(config.clone(), self.http_downloader.clone().unwrap());
- let latest = match versions_util.get_latest() {
+ let latest = match versions_util.get_latest(None) {
Ok(l) => l,
Err(e) => {
return Ok(PhpMixed::String(format!(
@@ -851,7 +846,7 @@ impl DiagnoseCommand {
"composer/src/Composer/Command/../../../vendor/composer/installed.json".to_string(),
None,
None,
- );
+ )?;
if !installed_json.exists() {
return Ok(PhpMixed::String("<warning>Could not find Composer's installed.json, this must be a non-standard Composer installation.</>".to_string()));
}
@@ -986,7 +981,7 @@ impl DiagnoseCommand {
}
fn output_result(&mut self, result: PhpMixed) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if result.as_bool() == Some(true) {
io.write("<info>OK</info>");
@@ -1371,30 +1366,12 @@ impl DiagnoseCommand {
}
}
-impl BaseCommand for DiagnoseCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for DiagnoseCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DiagnoseCommand {}
diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs
index 43af30d..9389132 100644
--- a/crates/shirabe/src/command/dump_autoload_command.rs
+++ b/crates/shirabe/src/command/dump_autoload_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/DumpAutoloadCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, file_exists};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -16,29 +14,27 @@ use crate::plugin::plugin_events::PluginEvents;
#[derive(Debug)]
pub struct DumpAutoloadCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl DumpAutoloadCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("dump-autoload")
- .set_aliases(vec!["dumpautoload".to_string()])
+ .set_aliases(&["dumpautoload".to_string()])
.set_description("Dumps the autoloader")
.set_definition(vec![
- InputOption::new("optimize", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize`.", None, vec![]),
- InputOption::new("apcu", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("strict-psr", None, Some(InputOption::VALUE_NONE), "Return a failed status code (1) if PSR-4 or PSR-0 mapping errors are present. Requires --optimize to work.", None, vec![]),
- InputOption::new("strict-ambiguous", None, Some(InputOption::VALUE_NONE), "Return a failed status code (2) if the same class is found in multiple files. Requires --optimize to work.", None, vec![]),
+ InputOption::new("optimize", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize`.", None),
+ InputOption::new("apcu", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Enables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("strict-psr", None, Some(InputOption::VALUE_NONE), "Return a failed status code (1) if PSR-4 or PSR-0 mapping errors are present. Requires --optimize to work.", None),
+ InputOption::new("strict-ambiguous", None, Some(InputOption::VALUE_NONE), "Return a failed status code (2) if the same class is found in multiple files. Requires --optimize to work.", None),
])
.set_help(
"<info>php composer.phar dump-autoload</info>\n\n\
@@ -47,20 +43,20 @@ impl DumpAutoloadCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"dump-autoload".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let installation_manager = composer.get_installation_manager();
let local_repo = composer.get_repository_manager().get_local_repository();
@@ -72,7 +68,7 @@ impl DumpAutoloadCommand {
let install_path = installation_manager.get_install_path(&*local_pkg);
if install_path.as_deref().is_some_and(|p| !file_exists(p)) {
missing_dependencies = true;
- self.inner.get_io().write("<warning>Not all dependencies are installed. Make sure to run a \"composer install\" to install missing dependencies</warning>");
+ self.get_io().write("<warning>Not all dependencies are installed. Make sure to run a \"composer install\" to install missing dependencies</warning>");
break;
}
}
@@ -118,16 +114,13 @@ impl DumpAutoloadCommand {
}
if authoritative {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating optimized autoload files (authoritative)</info>");
} else if optimize {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating optimized autoload files</info>");
} else {
- self.inner
- .get_io()
+ self.get_io()
.write("<info>Generating autoload files</info>");
}
@@ -153,8 +146,7 @@ impl DumpAutoloadCommand {
generator.set_class_map_authoritative(authoritative);
generator.set_run_scripts(true);
generator.set_apcu(apcu, apcu_prefix.as_deref());
- generator
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?);
+ generator.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?);
let class_map = generator.dump(
config,
&local_repo,
@@ -172,16 +164,14 @@ impl DumpAutoloadCommand {
let number_of_classes = class_map.len();
if authoritative {
- self.inner.get_io().write(&format!("<info>Generated optimized autoload files (authoritative) containing {} classes</info>", number_of_classes));
+ self.get_io().write(&format!("<info>Generated optimized autoload files (authoritative) containing {} classes</info>", number_of_classes));
} else if optimize {
- self.inner.get_io().write(&format!(
+ self.get_io().write(&format!(
"<info>Generated optimized autoload files containing {} classes</info>",
number_of_classes
));
} else {
- self.inner
- .get_io()
- .write("<info>Generated autoload files</info>");
+ self.get_io().write("<info>Generated autoload files</info>");
}
if missing_dependencies
@@ -204,30 +194,12 @@ impl DumpAutoloadCommand {
}
}
-impl BaseCommand for DumpAutoloadCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for DumpAutoloadCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for DumpAutoloadCommand {}
diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs
index 17cdb3e..7f339b6 100644
--- a/crates/shirabe/src/command/exec_command.rs
+++ b/crates/shirabe/src/command/exec_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/ExecCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, RuntimeException, basename, chdir, getcwd, glob};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -15,32 +13,28 @@ use crate::io::io_interface::IOInterface;
#[derive(Debug)]
pub struct ExecCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ExecCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("exec")
.set_description("Executes a vendored binary/script")
.set_definition(vec![
- InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None, vec![]),
+ InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None),
+ // TODO(cli-completion): suggest installed binary names (via get_binaries) for `binary` argument
InputArgument::new(
"binary",
Some(InputArgument::OPTIONAL),
"The binary to run, e.g. phpunit",
None,
- // suggestion callback deferred; binaries listed at runtime via get_binaries
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments",
None,
- vec![],
),
])
.set_help(
@@ -65,7 +59,7 @@ impl ExecCommand {
return Ok(());
}
- let io = self.inner.get_io();
+ let io = self.get_io();
let binary = io.select(
"Binary to run: ".to_string(),
binaries.clone(),
@@ -76,7 +70,10 @@ impl ExecCommand {
);
if let Some(idx) = binary.as_int() {
- input.set_argument("binary", &binaries[idx as usize]);
+ input.set_argument(
+ "binary",
+ shirabe_php_shim::PhpMixed::String(binaries[idx as usize].clone()),
+ );
}
Ok(())
@@ -87,7 +84,7 @@ impl ExecCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if input.get_option("list").as_bool().unwrap_or(false)
|| input.get_argument("binary").as_string_opt().is_none()
@@ -110,13 +107,10 @@ impl ExecCommand {
.into());
}
- self.inner
- .get_io()
+ self.get_io()
.write("<comment>Available binaries:</comment>");
for bin in &bins {
- self.inner
- .get_io()
- .write(&format!("<info>- {}</info>", bin));
+ self.get_io().write(&format!("<info>- {}</info>", bin));
}
return Ok(0);
@@ -129,10 +123,10 @@ impl ExecCommand {
.to_string();
let dispatcher = composer.get_event_dispatcher();
- dispatcher.add_listener("__exec_command", &binary);
+ // TODO(phase-b): add_listener takes a Callable; wiring binary as callable not yet ported
+ let _ = (dispatcher, &binary);
- let initial_working_directory =
- self.inner.get_application().get_initial_working_directory();
+ let initial_working_directory = self.get_application().get_initial_working_directory();
if let Some(ref iwd) = initial_working_directory {
if getcwd().as_deref() != Some(iwd.as_str()) {
chdir(iwd).map_err(|e| RuntimeException {
@@ -159,7 +153,7 @@ impl ExecCommand {
}
fn get_binaries(&self, for_display: bool) -> Result<Vec<String>> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let bin_dir = composer
.get_config()
.get("bin-dir")
@@ -193,30 +187,12 @@ impl ExecCommand {
}
}
-impl BaseCommand for ExecCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for ExecCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ExecCommand {}
diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs
index 066eb8e..73a7078 100644
--- a/crates/shirabe/src/command/fund_command.rs
+++ b/crates/shirabe/src/command/fund_command.rs
@@ -5,15 +5,13 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
use shirabe_semver::constraint::match_all_constraint::MatchAllConstraint;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -21,19 +19,17 @@ use crate::json::json_file::JsonFile;
use crate::package::alias_package::AliasPackage;
use crate::package::base_package::{self, BasePackage};
use crate::package::complete_package::CompletePackage;
+use crate::package::complete_package_interface::CompletePackageInterface;
use crate::repository::composite_repository::CompositeRepository;
#[derive(Debug)]
pub struct FundCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl FundCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("fund")
+ self.set_name("fund")
.set_description("Discover how to help fund the maintenance of your dependencies")
.set_definition(vec![InputOption::new(
"format",
@@ -41,7 +37,6 @@ impl FundCommand {
Some(InputOption::VALUE_REQUIRED),
"Format of the output: text or json",
Some(PhpMixed::String("text".to_string())),
- vec!["text".to_string(), "json".to_string()],
)]);
}
@@ -50,7 +45,7 @@ impl FundCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let repo = composer.get_repository_manager().get_local_repository();
let remote_repos =
@@ -120,7 +115,7 @@ impl FundCommand {
fundings.sort_keys();
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -163,7 +158,7 @@ impl FundCommand {
);
io.write("Thank you!");
} else if format == "json" {
- io.write(&JsonFile::encode(&fundings));
+ io.write(&JsonFile::encode(&fundings, 448));
} else {
io.write("No funding links were found in your package dependencies. This doesn't mean they don't need your support!");
}
@@ -208,30 +203,12 @@ impl FundCommand {
}
}
-impl BaseCommand for FundCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for FundCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for FundCommand {}
diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs
index 5669328..5acceb3 100644
--- a/crates/shirabe/src/command/global_command.rs
+++ b/crates/shirabe/src/command/global_command.rs
@@ -4,16 +4,12 @@ use std::path::Path;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
-use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::input::string_input::StringInput;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{LogicException, RuntimeException, chdir};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::factory::Factory;
@@ -23,58 +19,22 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct GlobalCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl GlobalCommand {
- pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) {
- let application = self.inner.get_application();
- if input.must_suggest_argument_values_for("command-name") {
- let names: Vec<String> = application
- .all()
- .into_iter()
- .filter(|cmd| !cmd.is_hidden())
- .filter_map(|cmd| cmd.get_name().map(|n| n.to_string()))
- .collect();
- suggestions.suggest_values(names);
- return;
- }
-
- let command_name = input
- .get_argument("command-name")
- .as_string()
- .unwrap_or("")
- .to_string();
- if application.has(&command_name) {
- let sub_input = self.prepare_subcommand_input(input.as_input_interface(), true);
- let sub_input = CompletionInput::from_string(&sub_input.to_string(), 2);
- let command = application.find(&command_name);
- command.merge_application_definition();
- sub_input.bind(command.get_definition());
- command.complete(&sub_input, suggestions);
- }
- }
+ // TODO(cli-completion): pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions)
pub fn configure(&mut self) {
- self.inner
- .set_name("global")
+ self.set_name("global")
.set_description("Allows running commands in the global composer dir ($COMPOSER_HOME)")
.set_definition(vec![
- InputArgument::new(
- "command-name",
- Some(InputArgument::REQUIRED),
- "",
- None,
- vec![],
- ),
+ InputArgument::new("command-name", Some(InputArgument::REQUIRED), "", None),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
])
.set_help(
@@ -105,11 +65,11 @@ impl GlobalCommand {
}
if args.len() < 2 {
- return self.inner.run(input, output);
+ return self.run(input, output);
}
let sub_input = self.prepare_subcommand_input(input, false)?;
- Ok(self.inner.get_application().run(&sub_input, output)?)
+ Ok(self.get_application().run(&sub_input, output)?)
}
fn prepare_subcommand_input(
@@ -125,7 +85,7 @@ impl GlobalCommand {
let home = config.get("home").as_string().unwrap_or("").to_string();
if !Path::new(&home).is_dir() {
- let fs = Filesystem::new();
+ let fs = Filesystem::new(None);
fs.ensure_directory_exists(&home)?;
if !Path::new(&home).is_dir() {
return Err(RuntimeException {
@@ -142,7 +102,7 @@ impl GlobalCommand {
})?;
if !quiet {
- self.inner.get_io().write_error(&format!(
+ self.get_io().write_error(&format!(
"<info>Changed current directory to {}</info>",
home
));
@@ -154,7 +114,7 @@ impl GlobalCommand {
&input.to_string(),
1,
)?;
- self.inner.get_application().reset_composer();
+ self.get_application().reset_composer();
Ok(StringInput::new(new_input_str))
}
@@ -164,30 +124,12 @@ impl GlobalCommand {
}
}
-impl BaseCommand for GlobalCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for GlobalCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for GlobalCommand {}
diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs
index 8e9f007..9b92707 100644
--- a/crates/shirabe/src/command/home_command.rs
+++ b/crates/shirabe/src/command/home_command.rs
@@ -1,14 +1,11 @@
//! ref: composer/src/Composer/Command/HomeCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{FILTER_VALIDATE_URL, filter_var};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -22,26 +19,14 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct HomeCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for HomeCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl HomeCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("browse")
- .set_aliases(vec!["home".to_string()])
+ // TODO(cli-completion): suggest_installed_package() for `packages` argument
+ self.set_name("browse")
+ .set_aliases(&["home".to_string()])
.set_description("Opens the package's repository URL or homepage in your browser")
.set_definition(vec![
InputArgument::new(
@@ -49,7 +34,6 @@ impl HomeCommand {
Some(InputArgument::IS_ARRAY),
"Package(s) to browse to.",
None,
- self.suggest_installed_package(),
),
InputOption::new(
"homepage",
@@ -57,7 +41,6 @@ impl HomeCommand {
Some(InputOption::VALUE_NONE),
"Open the homepage instead of the repository URL.",
None,
- vec![],
),
InputOption::new(
"show",
@@ -65,7 +48,6 @@ impl HomeCommand {
Some(InputOption::VALUE_NONE),
"Only show the homepage or repository URL.",
None,
- vec![],
),
])
.set_help(
@@ -83,7 +65,7 @@ impl HomeCommand {
_output: &dyn OutputInterface,
) -> Result<i64> {
let repos = self.initialize_repos()?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut return_code: i64 = 0;
let packages: Vec<String> = input
@@ -99,8 +81,7 @@ impl HomeCommand {
let packages = if packages.is_empty() {
io.write_error("No package specified, opening homepage for the root package");
vec![
- self.inner
- .require_composer()?
+ self.require_composer(None, None)?
.get_package()
.get_name()
.to_string(),
@@ -177,7 +158,7 @@ impl HomeCommand {
}
if show_only {
- self.inner.get_io().write(&format!("<info>{}</info>", url));
+ self.get_io().write(&format!("<info>{}</info>", url));
} else {
self.open_browser(&url);
}
@@ -186,7 +167,7 @@ impl HomeCommand {
}
fn open_browser(&self, url: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut process = ProcessExecutor::new(io);
if Platform::is_windows() {
process.execute(&["start", "\"web\"", "explorer", url], None);
@@ -209,7 +190,7 @@ impl HomeCommand {
}
fn initialize_repos(&self) -> Result<Vec<Box<dyn RepositoryInterface>>> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
if let Some(composer) = composer {
let mut repos: Vec<Box<dyn RepositoryInterface>> = vec![];
@@ -226,35 +207,17 @@ impl HomeCommand {
}
Ok(RepositoryFactory::default_repos_with_default_manager(
- self.inner.get_io(),
+ self.get_io(),
))
}
}
-impl BaseCommand for HomeCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for HomeCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for HomeCommand {}
diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs
index 5ee4b67..04928ed 100644
--- a/crates/shirabe/src/command/init_command.rs
+++ b/crates/shirabe/src/command/init_command.rs
@@ -5,8 +5,6 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::helper::formatter_helper::FormatterHelper;
use shirabe_external_packages::symfony::component::console::input::array_input::ArrayInput;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
@@ -19,8 +17,7 @@ use shirabe_php_shim::{
strtolower, trim, ucwords,
};
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::package_discovery_trait::PackageDiscoveryTrait;
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
@@ -38,24 +35,12 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct InitCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
/// @var array<string, string>
git_config: Option<IndexMap<String, String>>,
}
-impl CompletionTrait for InitCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl PackageDiscoveryTrait for InitCommand {
fn get_repos_mut(&mut self) -> &mut Option<CompositeRepository> {
todo!()
@@ -97,25 +82,22 @@ impl PackageDiscoveryTrait for InitCommand {
impl InitCommand {
pub fn configure(&mut self) {
- let suggest_available_package_incl_platform =
- self.suggest_available_package_incl_platform();
- let suggest_available_package_incl_platform2 =
- self.suggest_available_package_incl_platform();
- self.inner
+ // TODO(cli-completion): suggest_available_package_incl_platform() for `require` / `require-dev`
+ self
.set_name("init")
.set_description("Creates a basic composer.json file in current directory")
.set_definition(vec![
- InputOption::new("name", None, Some(InputOption::VALUE_REQUIRED), "Name of the package", None, vec![]),
- InputOption::new("description", None, Some(InputOption::VALUE_REQUIRED), "Description of package", None, vec![]),
- InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None, vec![]),
- InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None, vec![]),
- InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None, vec![]),
- InputOption::new("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform),
- InputOption::new("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform2),
- InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &array_keys(&BasePackage::stabilities()))), None, vec![]),
- InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None, vec![]),
- InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None, vec![]),
- InputOption::new("autoload", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_REQUIRED), "Add PSR-4 autoload mapping. Maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/)", None, vec![]),
+ InputOption::new("name", None, Some(InputOption::VALUE_REQUIRED), "Name of the package", None),
+ InputOption::new("description", None, Some(InputOption::VALUE_REQUIRED), "Description of package", None),
+ InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None),
+ InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None),
+ InputOption::new("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &array_keys(&BasePackage::stabilities()))), None),
+ InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None),
+ InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None),
+ InputOption::new("autoload", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_REQUIRED), "Add PSR-4 autoload mapping. Maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/)", None),
])
.set_help(
"The <info>init</info> command creates a basic composer.json file\n\
@@ -133,7 +115,7 @@ impl InitCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
let allowlist: Vec<String> = vec![
"name".to_string(),
@@ -239,7 +221,7 @@ impl InitCommand {
.collect()
})
.unwrap_or_default();
- let formatted = self.inner.format_requirements(req_list)?;
+ let formatted = self.format_requirements(req_list)?;
if formatted.is_empty() {
// PHP: new \stdClass — represented as an empty IndexMap (JSON object)
PhpMixed::Array(IndexMap::new())
@@ -267,7 +249,7 @@ impl InitCommand {
.collect()
})
.unwrap_or_default();
- let formatted = self.inner.format_requirements(req_list)?;
+ let formatted = self.format_requirements(req_list)?;
let value = if formatted.is_empty() {
PhpMixed::Array(IndexMap::new())
} else {
@@ -303,13 +285,13 @@ impl InitCommand {
options.insert("autoload".to_string(), PhpMixed::Array(autoload_obj));
}
- let file_obj = JsonFile::new(&Factory::get_composer_file(), None, None);
+ let file_obj = JsonFile::new(Factory::get_composer_file(), None, None)?;
let options_for_encode: IndexMap<String, Box<PhpMixed>> = options
.clone()
.into_iter()
.map(|(k, v)| (k, Box::new(v)))
.collect();
- let json = JsonFile::encode(&options_for_encode);
+ let json = JsonFile::encode(&options_for_encode, 448);
if input.is_interactive() {
io.write_error(
@@ -362,10 +344,11 @@ impl InitCommand {
true,
io_interface::NORMAL,
);
- Silencer::call(
- "unlink",
- &[PhpMixed::String(file_obj.get_path().to_string())],
- );
+ let path_to_unlink = file_obj.get_path().to_string();
+ let _ = Silencer::call(|| {
+ shirabe_php_shim::unlink(&path_to_unlink);
+ Ok::<(), anyhow::Error>(())
+ });
return Ok(1);
}
@@ -374,7 +357,7 @@ impl InitCommand {
// --autoload - Create src folder
if let Some(ref ap) = autoload_path {
- let filesystem = Filesystem::new();
+ let filesystem = Filesystem::new(None);
filesystem.ensure_directory_exists(ap);
// dump-autoload only for projects without added dependencies.
@@ -440,7 +423,7 @@ impl InitCommand {
}
pub(crate) fn initialize(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) {
- self.inner.initialize(input, output);
+ self.initialize(input, output);
if !input.is_interactive() {
if input.get_option("name").is_null() {
@@ -463,9 +446,9 @@ impl InitCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<()> {
- let io = self.inner.get_io();
+ let io = self.get_io();
// @var FormatterHelper $formatter
- let formatter: &FormatterHelper = self.inner.get_helper_set().get("formatter");
+ let formatter: &FormatterHelper = self.get_helper_set().get("formatter");
// initialize repos if configured
let repositories: Vec<String> = input
@@ -484,7 +467,7 @@ impl InitCommand {
let mut repos: Vec<
Box<dyn crate::repository::repository_interface::RepositoryInterface>,
- > = vec![Box::new(PlatformRepository::new(vec![], PhpMixed::Null))];
+ > = vec![Box::new(PlatformRepository::new(vec![], IndexMap::new())?)];
let mut create_default_packagist_repo = true;
for repo in &repositories {
let repo_config =
@@ -977,7 +960,7 @@ impl InitCommand {
return self.git_config.clone().unwrap_or_default();
}
- let mut process = ProcessExecutor::new(self.inner.get_io());
+ let mut process = ProcessExecutor::new(self.get_io());
let mut output = String::new();
if process.execute(
@@ -1059,14 +1042,14 @@ impl InitCommand {
fn update_dependencies(&self, output: &dyn OutputInterface) {
// PHP try/catch: catch \Exception
- let result = self.inner.get_application().and_then(|app| {
+ let result = self.get_application().and_then(|app| {
let update_command = app.find("update")?;
app.reset_composer()?;
update_command.run(ArrayInput::new(IndexMap::new()), output)?;
Ok(())
});
if let Err(_e) = result {
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String(
"Could not update dependencies. Run `composer update` to see more information."
.to_string(),
@@ -1078,14 +1061,14 @@ impl InitCommand {
}
fn run_dump_autoload_command(&self, output: &dyn OutputInterface) {
- let result = self.inner.get_application().and_then(|app| {
+ let result = self.get_application().and_then(|app| {
let command = app.find("dump-autoload")?;
app.reset_composer()?;
command.run(ArrayInput::new(IndexMap::new()), output)?;
Ok(())
});
if let Err(_e) = result {
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String("Could not run dump-autoload.".to_string()),
true,
io_interface::NORMAL,
@@ -1206,30 +1189,12 @@ impl InitCommand {
}
}
-impl BaseCommand for InitCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for InitCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for InitCommand {}
diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs
index eac04c3..2b9a87d 100644
--- a/crates/shirabe/src/command/install_command.rs
+++ b/crates/shirabe/src/command/install_command.rs
@@ -1,16 +1,12 @@
//! ref: composer/src/Composer/Command/InstallCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::PhpMixed;
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::installer::Installer;
@@ -21,51 +17,39 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct InstallCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for InstallCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl InstallCommand {
pub fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_prefer_install() for `prefer-install` option
+ self
.set_name("install")
- .set_aliases(vec!["i".to_string()])
+ .set_aliases(&["i".to_string()])
.set_description("Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json")
.set_definition(vec![
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None, vec![]),
- InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None, vec![]),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None, vec![]),
- InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None, vec![]),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var). Only applies when no lock file is present.", None, vec![]),
- InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Do not use, only defined here to catch misuse of the install command.", None, vec![]),
- InputOption::new("audit", None, Some(InputOption::VALUE_NONE), "Run an audit after installation is complete.", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.iter().map(|s| s.to_string()).collect()),
- InputOption::new("verbose", Some(PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Shows more details including new commits pulled in when updating packages.", None, vec![]),
- InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Should not be provided, use composer require instead to add a given package to composer.json.", None, vec![]),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None),
+ InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var). Only applies when no lock file is present.", None),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Do not use, only defined here to catch misuse of the install command.", None),
+ InputOption::new("audit", None, Some(InputOption::VALUE_NONE), "Run an audit after installation is complete.", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("verbose", Some(PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Shows more details including new commits pulled in when updating packages.", None),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Should not be provided, use composer require instead to add a given package to composer.json.", None),
])
.set_help(
"The <info>install</info> command reads the composer.lock file from\n\
@@ -78,7 +62,7 @@ impl InstallCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("dev").as_bool().unwrap_or(false) {
io.write_error("<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>");
@@ -110,7 +94,7 @@ impl InstallCommand {
return Ok(1);
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if !composer.get_locker().is_locked() && !HttpDownloader::is_curl_enabled() {
io.write_error("<warning>Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.</warning>");
@@ -120,20 +104,19 @@ impl InstallCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"install".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let install = Installer::create(io, &composer);
let config = composer.get_config();
- let (prefer_source, prefer_dist) =
- self.inner.get_preferred_install_options(config, input)?;
+ let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
let optimize = input
.get_option("optimize-autoloader")
@@ -174,11 +157,8 @@ impl InstallCommand {
.set_optimize_autoloader(optimize)
.set_class_map_authoritative(authoritative)
.set_apcu_autoloader(apcu, apcu_prefix.as_deref())
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?)
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input)?)
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_error_on_audit(input.get_option("audit").as_bool().unwrap_or(false));
if input.get_option("no-plugins").as_bool().unwrap_or(false) {
@@ -189,30 +169,12 @@ impl InstallCommand {
}
}
-impl BaseCommand for InstallCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
+impl HasBaseCommandData for InstallCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for InstallCommand {}
diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs
index 4fa2e72..102221e 100644
--- a/crates/shirabe/src/command/licenses_command.rs
+++ b/crates/shirabe/src/command/licenses_command.rs
@@ -4,8 +4,6 @@ use std::any::Any;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::helper::table::Table;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
@@ -13,7 +11,7 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
use shirabe_external_packages::symfony::console::style::symfony_style::SymfonyStyle;
use shirabe_php_shim::{PhpMixed, RuntimeException, UnexpectedValueException};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -28,15 +26,12 @@ use crate::util::package_sorter::PackageSorter;
#[derive(Debug)]
pub struct LicensesCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl LicensesCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("licenses")
+ self.set_name("licenses")
.set_description("Shows information about licenses of dependencies")
.set_definition(vec![
InputOption::new(
@@ -45,11 +40,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_REQUIRED),
"Format of the output: text, json or summary",
Some(PhpMixed::String("text".to_string())),
- vec![
- "text".to_string(),
- "json".to_string(),
- "summary".to_string(),
- ],
),
InputOption::new(
"no-dev",
@@ -57,7 +47,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_NONE),
"Disables search in require-dev packages.",
None,
- vec![],
),
InputOption::new(
"locked",
@@ -65,7 +54,6 @@ impl LicensesCommand {
Some(InputOption::VALUE_NONE),
"Shows licenses from the lock file instead of installed packages.",
None,
- vec![],
),
])
.set_help(
@@ -78,14 +66,14 @@ impl LicensesCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch COMMAND event for plugin hooks
let command_event =
CommandEvent::new(PluginEvents::COMMAND, "licenses".to_string(), input, output);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let root = composer.get_package();
@@ -110,7 +98,7 @@ impl LicensesCommand {
};
let packages = PackageSorter::sort_packages_alphabetically(packages);
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -238,7 +226,7 @@ impl LicensesCommand {
.collect(),
),
);
- io.write(&JsonFile::encode(&output_map));
+ io.write(&JsonFile::encode(&output_map, 448));
}
"summary" => {
let mut used_licenses: IndexMap<String, i64> = IndexMap::new();
@@ -288,30 +276,12 @@ impl LicensesCommand {
}
}
-impl BaseCommand for LicensesCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for LicensesCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for LicensesCommand {}
diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs
index d11d290..bac15cf 100644
--- a/crates/shirabe/src/command/outdated_command.rs
+++ b/crates/shirabe/src/command/outdated_command.rs
@@ -1,15 +1,12 @@
//! ref: composer/src/Composer/Command/OutdatedCommand.php
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::array_input::ArrayInput;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
@@ -17,44 +14,31 @@ use shirabe_php_shim::PhpMixed;
#[derive(Debug)]
pub struct OutdatedCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for OutdatedCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl OutdatedCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.suggest_installed_package(false, false);
- let suggest_installed_package_for_ignore = self.suggest_installed_package(false, false);
- self.inner
+ // TODO(cli-completion): suggest_installed_package(false, false) for `package` argument and `--ignore` option
+ self
.set_name("outdated")
.set_description("Shows a list of installed packages that have updates available, including their latest version")
.set_definition(vec![
- InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None, suggest_installed_package),
- InputOption::new("outdated", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that are outdated (this is the default, but present here for compat with `show`", None, vec![]),
- InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None, vec![]),
- InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows updates for packages from the lock file, regardless of what is currently in vendor dir", None, vec![]),
- InputOption::new("direct", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Shows only packages that are directly required by the root package", None, vec![]),
- InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code when there are outdated packages", None, vec![]),
- InputOption::new("major-only", Some(PhpMixed::String("M".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have major SemVer-compatible updates.", None, vec![]),
- InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None, vec![]),
- InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None, vec![]),
- InputOption::new("sort-by-age", Some(PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Displays the installed version's age, and sorts packages oldest first.", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
- InputOption::new("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None, suggest_installed_package_for_ignore),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages). Use with the --outdated option", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None, vec![]),
+ InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None),
+ InputOption::new("outdated", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that are outdated (this is the default, but present here for compat with `show`", None),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None),
+ InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows updates for packages from the lock file, regardless of what is currently in vendor dir", None),
+ InputOption::new("direct", Some(PhpMixed::String("D".to_string())), Some(InputOption::VALUE_NONE), "Shows only packages that are directly required by the root package", None),
+ InputOption::new("strict", None, Some(InputOption::VALUE_NONE), "Return a non-zero exit code when there are outdated packages", None),
+ InputOption::new("major-only", Some(PhpMixed::String("M".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have major SemVer-compatible updates.", None),
+ InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None),
+ InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None),
+ InputOption::new("sort-by-age", Some(PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Displays the installed version's age, and sorts packages oldest first.", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string()))),
+ InputOption::new("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages). Use with the --outdated option", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None),
])
.set_help(
"The outdated command is just a proxy for `composer show -l`\n\n\
@@ -140,7 +124,9 @@ impl OutdatedCommand {
let input = ArrayInput::new(args);
- self.inner.get_application().run(&input, output)
+ // TODO(phase-b): convert ArrayInput/output references to dyn trait objects expected by Application::run
+ let _ = input;
+ self.get_application()?.run(None, None)
}
pub fn is_proxy_command(&self) -> bool {
@@ -148,30 +134,12 @@ impl OutdatedCommand {
}
}
-impl BaseCommand for OutdatedCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
+impl HasBaseCommandData for OutdatedCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for OutdatedCommand {}
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 164cc53..501ae5d 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -28,7 +28,7 @@ use crate::package::version::version_selector::VersionSelector;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_factory::RepositoryFactory;
-use crate::repository::repository_interface::SearchResult;
+use crate::repository::repository_interface::{RepositoryInterface, SearchResult};
use crate::repository::repository_set::RepositorySet;
use crate::util::filesystem::Filesystem;
@@ -161,12 +161,12 @@ pub trait PackageDiscoveryTrait {
requirement.get("version").map(|s| s.as_str()).unwrap_or(""),
)
{
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>The \"{}\" constraint for \"{}\" appears too strict and will likely not match what you want. See https://getcomposer.org/constraints</warning>",
requirement.get("version").map(|s| s.as_str()).unwrap_or(""),
requirement.get("name").map(|s| s.as_str()).unwrap_or(""),
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -188,8 +188,8 @@ pub trait PackageDiscoveryTrait {
if use_best_version_constraint {
requirement.insert("version".to_string(), version.clone());
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
PhpMixed::String(version),
@@ -197,7 +197,7 @@ pub trait PackageDiscoveryTrait {
requirement.get("name").cloned().unwrap_or_default(),
),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -219,7 +219,7 @@ pub trait PackageDiscoveryTrait {
let version_parser = VersionParser::new();
// Collect existing packages
- let composer = self.try_composer();
+ let composer = self.try_composer(None, None);
let mut installed_repo: Option<_> = None;
if let Some(c) = &composer {
installed_repo = Some(c.get_repository_manager().get_local_repository());
@@ -319,33 +319,24 @@ pub trait PackageDiscoveryTrait {
));
}
- io.write_error(
- PhpMixed::List(vec![
- Box::new(PhpMixed::String(String::new())),
- Box::new(PhpMixed::String(sprintf(
- "Found <info>%s</info> packages matching <info>%s</info>",
- &[
- PhpMixed::Int(matches.len() as i64),
- PhpMixed::String(package.clone()),
- ],
- ))),
- Box::new(PhpMixed::String(String::new())),
- ]),
- true,
- io_interface::NORMAL,
- );
-
- io.write_error(
- PhpMixed::List(
- choices
- .iter()
- .map(|s| Box::new(PhpMixed::String(s.clone())))
- .collect(),
+ io.write_error3("", true, io_interface::NORMAL);
+ io.write_error3(
+ &sprintf(
+ "Found <info>%s</info> packages matching <info>%s</info>",
+ &[
+ PhpMixed::Int(matches.len() as i64),
+ PhpMixed::String(package.clone()),
+ ],
),
true,
io_interface::NORMAL,
);
- io.write_error(PhpMixed::String(String::new()), true, io_interface::NORMAL);
+ io.write_error3("", true, io_interface::NORMAL);
+
+ for choice in &choices {
+ io.write_error3(choice, true, io_interface::NORMAL);
+ }
+ io.write_error3("", true, io_interface::NORMAL);
let matches_clone = matches.clone();
let version_parser_clone = version_parser.clone();
@@ -432,14 +423,14 @@ pub trait PackageDiscoveryTrait {
fixed,
)?;
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
PhpMixed::String(c.clone()),
PhpMixed::String(package.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs
index 6add126..26c17f2 100644
--- a/crates/shirabe/src/command/prohibits_command.rs
+++ b/crates/shirabe/src/command/prohibits_command.rs
@@ -1,12 +1,7 @@
//! ref: composer/src/Composer/Command/ProhibitsCommand.php
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_dependency_command::BaseDependencyCommand;
-use crate::command::completion_trait::CompletionTrait;
-use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -15,62 +10,50 @@ use shirabe_external_packages::symfony::console::output::output_interface::Outpu
#[derive(Debug)]
pub struct ProhibitsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
colors: Vec<String>,
}
-impl CompletionTrait for ProhibitsCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl ProhibitsCommand {
pub fn configure(&mut self) {
- let package_suggestions = self.suggest_available_package();
- self.inner
- .set_name("prohibits")
- .set_aliases(vec!["why-not".to_string()])
+ // TODO(cli-completion): suggest_available_package() for `package` argument
+ self.set_name("prohibits")
+ .set_aliases(&["why-not".to_string()])
.set_description("Shows which packages prevent the given package from being installed")
.set_definition(vec![
InputArgument::new(
BaseDependencyCommand::ARGUMENT_PACKAGE,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Package to inspect",
None,
- package_suggestions,
),
InputArgument::new(
BaseDependencyCommand::ARGUMENT_CONSTRAINT,
- InputArgument::REQUIRED,
+ Some(InputArgument::REQUIRED),
"Version constraint, which version you expected to be installed",
None,
- None,
),
InputOption::new(
BaseDependencyCommand::OPTION_RECURSIVE,
- Some("r"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("r".to_string())),
+ Some(InputOption::VALUE_NONE),
"Recursively resolves up to the root package",
+ None,
),
InputOption::new(
BaseDependencyCommand::OPTION_TREE,
- Some("t"),
- InputOption::VALUE_NONE,
+ Some(shirabe_php_shim::PhpMixed::String("t".to_string())),
+ Some(InputOption::VALUE_NONE),
"Prints the results as a nested tree",
+ None,
),
InputOption::new(
"locked",
None,
- InputOption::VALUE_NONE,
+ Some(InputOption::VALUE_NONE),
"Read dependency information from composer.lock",
+ None,
),
])
.set_help(
@@ -81,33 +64,9 @@ impl ProhibitsCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 {
- self.inner.do_execute(input, output, true)
- }
-}
-
-impl BaseCommand for ProhibitsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ // TODO(phase-b): wire `do_execute` from BaseDependencyCommand trait
+ let _ = (input, output);
+ todo!()
}
}
@@ -121,4 +80,12 @@ impl BaseDependencyCommand for ProhibitsCommand {
}
}
-impl Command for ProhibitsCommand {}
+impl HasBaseCommandData for ProhibitsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs
index d16b001..7e703c4 100644
--- a/crates/shirabe/src/command/reinstall_command.rs
+++ b/crates/shirabe/src/command/reinstall_command.rs
@@ -4,14 +4,11 @@ use std::any::Any;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::InvalidArgumentException;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -29,43 +26,29 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct ReinstallCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for ReinstallCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl ReinstallCommand {
pub fn configure(&mut self) {
- let suggest_prefer_install = self.suggest_prefer_install();
- let suggest_installed_package_types = self.suggest_installed_package_types(false);
- let suggest_installed_package = self.suggest_installed_package(false);
- self.inner
+ // TODO(cli-completion): suggest_prefer_install / suggest_installed_package_types / suggest_installed_package
+ self
.set_name("reinstall")
.set_description("Uninstalls and reinstalls the given package names")
.set_definition(vec![
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(shirabe_php_shim::PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None, suggest_installed_package_types),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None, suggest_installed_package),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(shirabe_php_shim::PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None),
])
.set_help(
"The <info>reinstall</info> command looks up installed packages by name,\n\
@@ -78,9 +61,9 @@ impl ReinstallCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let local_repo = composer.get_repository_manager().get_local_repository();
let mut packages_to_reinstall: Vec<
@@ -198,17 +181,16 @@ impl ReinstallCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"reinstall".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_dispatcher = composer.get_event_dispatcher();
- event_dispatcher.dispatch(command_event.get_name(), &command_event);
+ event_dispatcher.dispatch(Some(command_event.get_name()), None);
let config = composer.get_config();
- let (prefer_source, prefer_dist) =
- self.inner.get_preferred_install_options(config, input)?;
+ let (prefer_source, prefer_dist) = self.get_preferred_install_options(config, input)?;
let installation_manager = composer.get_installation_manager();
let download_manager = composer.get_download_manager();
@@ -220,13 +202,20 @@ impl ReinstallCommand {
installation_manager.disable_plugins();
}
- download_manager.set_prefer_source(prefer_source);
- download_manager.set_prefer_dist(prefer_dist);
+ download_manager
+ .borrow_mut()
+ .set_prefer_source(prefer_source);
+ download_manager.borrow_mut().set_prefer_dist(prefer_dist);
let dev_mode = local_repo.get_dev_mode().unwrap_or(true);
Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" });
- event_dispatcher.dispatch_script(ScriptEvents::PRE_INSTALL_CMD, dev_mode);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::PRE_INSTALL_CMD,
+ dev_mode,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
installation_manager.execute(local_repo, uninstall_operations, dev_mode);
installation_manager.execute(local_repo, install_operations, dev_mode);
@@ -259,9 +248,7 @@ impl ReinstallCommand {
let generator = composer.get_autoload_generator();
generator.set_class_map_authoritative(authoritative);
generator.set_apcu(apcu, apcu_prefix.as_deref());
- generator.set_platform_requirement_filter(
- self.inner.get_platform_requirement_filter(input)?,
- );
+ generator.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?);
generator.dump(
config,
local_repo,
@@ -274,36 +261,23 @@ impl ReinstallCommand {
);
}
- event_dispatcher.dispatch_script(ScriptEvents::POST_INSTALL_CMD, dev_mode);
+ event_dispatcher.dispatch_script(
+ ScriptEvents::POST_INSTALL_CMD,
+ dev_mode,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
Ok(0)
}
}
-impl BaseCommand for ReinstallCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ReinstallCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ReinstallCommand {}
diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs
index c9e967b..53bcdc9 100644
--- a/crates/shirabe/src/command/remove_command.rs
+++ b/crates/shirabe/src/command/remove_command.rs
@@ -2,17 +2,15 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::exception::invalid_argument_exception::InvalidArgumentException;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{PhpMixed, UnexpectedValueException, array_map, strtolower};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -23,20 +21,19 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::package::base_package;
use crate::package::base_package::BasePackage;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
#[derive(Debug)]
pub struct RemoveCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl RemoveCommand {
pub fn configure(&mut self) {
- let suggest_root_requirement = self.suggest_root_requirement();
- self.inner
+ // TODO(cli-completion): suggest_root_requirement() for `packages` argument
+ self
.set_name("remove")
- .set_aliases(vec!["rm".to_string(), "uninstall".to_string()])
+ .set_aliases(&["rm".to_string(), "uninstall".to_string()])
.set_description("Removes a package from the require or require-dev")
.set_definition(vec![
InputArgument::new(
@@ -44,7 +41,6 @@ impl RemoveCommand {
Some(InputArgument::IS_ARRAY),
"Packages that should be removed.",
None,
- suggest_root_requirement,
),
InputOption::new(
"dev",
@@ -52,7 +48,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Removes a package from the require-dev section.",
None,
- vec![],
),
InputOption::new(
"dry-run",
@@ -60,7 +55,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Outputs the operations but will not execute anything (implicitly enables --verbose).",
None,
- vec![],
),
InputOption::new(
"no-progress",
@@ -68,7 +62,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Do not output download progress.",
None,
- vec![],
),
InputOption::new(
"no-update",
@@ -76,7 +69,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Disables the automatic update of the dependencies (implies --no-install).",
None,
- vec![],
),
InputOption::new(
"no-install",
@@ -84,7 +76,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Skip the install step after updating the composer.lock file.",
None,
- vec![],
),
InputOption::new(
"no-audit",
@@ -92,7 +83,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).",
None,
- vec![],
),
InputOption::new(
"audit-format",
@@ -100,7 +90,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED),
"Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".",
Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())),
- Auditor::FORMATS.to_vec(),
),
InputOption::new(
"no-security-blocking",
@@ -108,7 +97,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).",
None,
- vec![],
),
InputOption::new(
"update-no-dev",
@@ -116,7 +104,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Run the dependency update with the --no-dev option.",
None,
- vec![],
),
InputOption::new(
"update-with-dependencies",
@@ -124,7 +111,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows inherited dependencies to be updated with explicit dependencies (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var). (Deprecated, is now default behavior)",
None,
- vec![],
),
InputOption::new(
"update-with-all-dependencies",
@@ -132,7 +118,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).",
None,
- vec![],
),
InputOption::new(
"with-all-dependencies",
@@ -140,7 +125,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Alias for --update-with-all-dependencies",
None,
- vec![],
),
InputOption::new(
"no-update-with-dependencies",
@@ -148,7 +132,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Does not allow inherited dependencies to be updated with explicit dependencies.",
None,
- vec![],
),
InputOption::new(
"minimal-changes",
@@ -156,7 +139,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).",
None,
- vec![],
),
InputOption::new(
"unused",
@@ -164,7 +146,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Remove all packages which are locked but not required by any other package.",
None,
- vec![],
),
InputOption::new(
"ignore-platform-req",
@@ -172,7 +153,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY),
"Ignore a specific platform requirement (php & ext- packages).",
None,
- vec![],
),
InputOption::new(
"ignore-platform-reqs",
@@ -180,7 +160,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Ignore all platform requirements (php & ext- packages).",
None,
- vec![],
),
InputOption::new(
"optimize-autoloader",
@@ -188,7 +167,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Optimize autoloader during autoloader dump",
None,
- vec![],
),
InputOption::new(
"classmap-authoritative",
@@ -196,7 +174,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.",
None,
- vec![],
),
InputOption::new(
"apcu-autoloader",
@@ -204,7 +181,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_NONE),
"Use APCu to cache found/not-found classes.",
None,
- vec![],
),
InputOption::new(
"apcu-autoloader-prefix",
@@ -212,7 +188,6 @@ impl RemoveCommand {
Some(InputOption::VALUE_REQUIRED),
"Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader",
None,
- vec![],
),
])
.set_help(
@@ -252,7 +227,7 @@ impl RemoveCommand {
.unwrap_or_default();
if input.get_option("unused").as_bool().unwrap_or(false) {
- let composer = self.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let locker = composer.get_locker();
if !locker.is_locked() {
return Err(anyhow::anyhow!(UnexpectedValueException {
@@ -263,7 +238,7 @@ impl RemoveCommand {
}));
}
- let locked_packages = locker.get_locked_repository()?.get_packages();
+ let locked_packages = locker.get_locked_repository(true)?.get_packages();
let mut required: IndexMap<String, bool> = IndexMap::new();
for link in composer
@@ -312,13 +287,14 @@ impl RemoveCommand {
}
}
- let file = Factory::get_composer_file();
+ let file = Factory::get_composer_file()?;
- let json_file = JsonFile::new(&file, None, None);
+ let json_file = JsonFile::new(file.clone(), None, None)?;
let composer_data = json_file.read()?;
let composer_backup = std::fs::read_to_string(json_file.get_path())?;
- let json = JsonConfigSource::new(&json_file);
+ let json_file_for_source = JsonFile::new(file, None, None)?;
+ let json = JsonConfigSource::new(json_file_for_source, false);
let r#type = if input.get_option("dev").as_bool().unwrap_or(false) {
"require-dev"
@@ -484,14 +460,14 @@ impl RemoveCommand {
}
// TODO(plugin): deactivate installed plugins
- if let Some(composer_opt) = self.try_composer() {
+ if let Some(composer_opt) = self.try_composer(None, None) {
composer_opt
.get_plugin_manager()
.deactivate_installed_plugins();
}
self.reset_composer();
- let composer = self.require_composer()?;
+ let composer = self.require_composer(None, None)?;
if dry_run {
let root_package = composer.get_package();
@@ -680,30 +656,12 @@ impl RemoveCommand {
}
}
-impl BaseCommand for RemoveCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for RemoveCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RemoveCommand {}
diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs
index 79b4a15..2aea141 100644
--- a/crates/shirabe/src/command/repository_command.rs
+++ b/crates/shirabe/src/command/repository_command.rs
@@ -2,19 +2,17 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
InvalidArgumentException, PHP_URL_HOST, PhpMixed, RuntimeException, parse_url, strtolower,
};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::base_config_command::BaseConfigCommand;
use crate::composer::Composer;
use crate::config::Config;
+use crate::config::config_source_interface::ConfigSourceInterface;
use crate::config::json_config_source::JsonConfigSource;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -24,9 +22,7 @@ use crate::json::json_file::JsonFile;
#[derive(Debug)]
pub struct RepositoryCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
config: Option<Config>,
config_file: Option<JsonFile>,
@@ -35,24 +31,21 @@ pub struct RepositoryCommand {
impl RepositoryCommand {
pub fn configure(&mut self) {
- let suggest_repo_names_before = self.suggest_repo_names();
- let suggest_repo_names_after = self.suggest_repo_names();
- let suggest_repo_names_name = self.suggest_repo_names();
- let suggest_type_for_add = Self::suggest_type_for_add();
- self.inner.inner
+ // TODO(cli-completion): suggest_repo_names() / suggest_type_for_add()
+ self
.set_name("repository")
- .set_aliases(vec!["repo".to_string()])
+ .set_aliases(&["repo".to_string()])
.set_description("Manages repositories")
.set_definition(vec![
- InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None, vec![]),
- InputOption::new("file", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None, vec![]),
- InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lower priority) instead of prepending it", None, vec![]),
- InputOption::new("before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None, suggest_repo_names_before),
- InputOption::new("after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None, suggest_repo_names_after),
- InputArgument::new("action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string())), vec!["list", "add", "remove", "set-url", "get-url", "enable", "disable"]),
- InputArgument::new("name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None, suggest_repo_names_name),
- InputArgument::new("arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None, suggest_type_for_add),
- InputArgument::new("arg2", Some(InputArgument::OPTIONAL), "URL for add (if not using JSON)", None, vec![]),
+ InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None),
+ InputOption::new("file", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None),
+ InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lower priority) instead of prepending it", None),
+ InputOption::new("before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None),
+ InputOption::new("after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None),
+ InputArgument::new("action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string()))),
+ InputArgument::new("name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None),
+ InputArgument::new("arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None),
+ InputArgument::new("arg2", Some(InputArgument::OPTIONAL), "URL for add (if not using JSON)", None),
])
.set_help(
"This command lets you manage repositories in your composer.json.\n\n\
@@ -98,7 +91,7 @@ impl RepositoryCommand {
.as_string()
.map(|s| s.to_string());
- let config_data = self.inner.config_file.as_ref().unwrap().read()?;
+ let config_data = self.config_file.as_ref().unwrap().read()?;
let config_file_path = self
.inner
.config_file
@@ -106,12 +99,11 @@ impl RepositoryCommand {
.unwrap()
.get_path()
.to_string();
- self.inner
- .config
+ self.config
.as_mut()
.unwrap()
.merge(config_data, &config_file_path);
- let repos = self.inner.config.as_ref().unwrap().get_repositories();
+ let repos = self.config.as_ref().unwrap().get_repositories();
match action.as_str() {
"list" | "ls" | "show" => {
@@ -173,21 +165,17 @@ impl RepositoryCommand {
}
let reference_name = before.as_deref().or(after.as_deref()).unwrap();
let offset: i64 = if after.is_some() { 1 } else { 0 };
- self.inner
- .config_source
- .as_mut()
- .unwrap()
- .insert_repository(
- name.as_deref().unwrap(),
- repo_config,
- reference_name,
- offset,
- );
+ self.config_source.as_mut().unwrap().insert_repository(
+ name.as_deref().unwrap(),
+ repo_config,
+ reference_name,
+ offset,
+ );
return Ok(0);
}
let append = input.get_option("append").as_bool().unwrap_or(false);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
name.as_deref().unwrap(),
repo_config,
append,
@@ -202,13 +190,12 @@ impl RepositoryCommand {
}));
}
let name_str = name.as_deref().unwrap();
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository(name_str);
if ["packagist", "packagist.org"].contains(&name_str) {
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
"packagist.org",
PhpMixed::Bool(false),
false,
@@ -223,8 +210,7 @@ impl RepositoryCommand {
code: 0,
}));
}
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.set_repository_url(name.as_deref().unwrap(), arg1.as_deref().unwrap());
@@ -242,7 +228,7 @@ impl RepositoryCommand {
if let PhpMixed::Array(ref repo_map) = *repo {
let url = repo_map.get("url").and_then(|v| v.as_string());
if let Some(url) = url {
- self.inner.inner.get_io().write(url);
+ self.get_io().write(url);
return Ok(0);
}
return Err(anyhow::anyhow!(InvalidArgumentException {
@@ -257,7 +243,7 @@ impl RepositoryCommand {
if n == name_str {
let url = repo_map.get("url").and_then(|v| v.as_string());
if let Some(url) = url {
- self.inner.inner.get_io().write(url);
+ self.get_io().write(url);
return Ok(0);
}
return Err(anyhow::anyhow!(InvalidArgumentException {
@@ -286,7 +272,7 @@ impl RepositoryCommand {
let name_str = name.as_deref().unwrap();
if ["packagist", "packagist.org"].contains(&name_str) {
let append = input.get_option("append").as_bool().unwrap_or(false);
- self.inner.config_source.as_mut().unwrap().add_repository(
+ self.config_source.as_mut().unwrap().add_repository(
"packagist.org",
PhpMixed::Bool(false),
append,
@@ -307,8 +293,7 @@ impl RepositoryCommand {
}
let name_str = name.as_deref().unwrap();
if ["packagist", "packagist.org"].contains(&name_str) {
- self.inner
- .config_source
+ self.config_source
.as_mut()
.unwrap()
.remove_repository("packagist.org");
@@ -331,7 +316,7 @@ impl RepositoryCommand {
}
fn list_repositories(&self, mut repos: IndexMap<String, PhpMixed>) {
- let io = self.inner.inner.get_io();
+ let io = self.get_io();
let mut packagist_present = false;
for (_key, repo) in &repos {
@@ -396,84 +381,14 @@ impl RepositoryCommand {
.get("url")
.and_then(|v| v.as_string())
.map(|s| s.to_string())
- .unwrap_or_else(|| JsonFile::encode(repo));
+ .unwrap_or_else(|| JsonFile::encode(repo, 448));
io.write(&format!("[{}] <info>{}</info> {}", name, r#type, url));
}
}
}
- fn suggest_type_for_add() -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- Box::new(|input: &CompletionInput| {
- if input.get_argument("action").as_string() == Some("add") {
- vec![
- "composer".to_string(),
- "vcs".to_string(),
- "artifact".to_string(),
- "path".to_string(),
- ]
- } else {
- vec![]
- }
- })
- }
-
- fn suggest_repo_names(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String> + '_> {
- Box::new(move |input: &CompletionInput| {
- let action = input
- .get_argument("action")
- .as_string()
- .unwrap_or("")
- .to_string();
- if ["enable", "disable"].contains(&action.as_str()) {
- return vec!["packagist.org".to_string()];
- }
- if !["remove", "set-url", "get-url"].contains(&action.as_str()) {
- return vec![];
- }
- let config = Factory::create_config(None, None).unwrap();
- let config_file_path = self.inner.get_composer_config_file(input, &config);
- let config_file = JsonFile::new(config_file_path, None, None);
- let data = config_file.read().unwrap_or_default();
- let mut repos = vec![];
- if let Some(repositories) = data.get("repositories").and_then(|v| v.as_list()) {
- for repo in repositories {
- if let PhpMixed::Array(ref repo_map) = **repo {
- if let Some(name) = repo_map.get("name").and_then(|v| v.as_string()) {
- repos.push(name.to_string());
- }
- }
- }
- }
- repos.sort();
- repos
- })
- }
-}
-
-impl BaseCommand for RepositoryCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
+ // TODO(cli-completion): fn suggest_type_for_add()
+ // TODO(cli-completion): fn suggest_repo_names(&self)
}
impl BaseConfigCommand for RepositoryCommand {
@@ -502,4 +417,12 @@ impl BaseConfigCommand for RepositoryCommand {
}
}
-impl Command for RepositoryCommand {}
+impl HasBaseCommandData for RepositoryCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 8031fbc..63efa92 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -5,8 +5,6 @@ use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::seld::signal::signal_handler::SignalHandler;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{
@@ -17,8 +15,7 @@ use shirabe_php_shim::{
};
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::package_discovery_trait::PackageDiscoveryTrait;
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
@@ -49,9 +46,7 @@ use crate::util::silencer::Silencer;
#[derive(Debug)]
pub struct RequireCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
newly_created: bool,
first_require: bool,
@@ -65,16 +60,6 @@ pub struct RequireCommand {
dependency_resolution_completed: bool,
}
-impl CompletionTrait for RequireCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
impl PackageDiscoveryTrait for RequireCommand {
fn get_repos_mut(&mut self) -> &mut Option<CompositeRepository> {
todo!()
@@ -114,43 +99,41 @@ impl PackageDiscoveryTrait for RequireCommand {
impl RequireCommand {
pub fn configure(&mut self) {
- let suggest_available_package_incl_platform =
- self.suggest_available_package_incl_platform();
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_available_package_incl_platform / suggest_prefer_install
+ self
.set_name("require")
- .set_aliases(vec!["r".to_string()])
+ .set_aliases(&["r".to_string()])
.set_description("Adds required packages to your composer.json and installs them")
.set_definition(vec![
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, suggest_available_package_incl_platform),
- InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None, vec![]),
- InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None, vec![]),
- InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None, vec![]),
- InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None, vec![]),
- InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, suggest_prefer_install),
- InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None, vec![]),
- InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("no-update", None, Some(InputOption::VALUE_NONE), "Disables the automatic update of the dependencies (implies --no-install).", None, vec![]),
- InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Skip the install step after updating the composer.lock file.", None, vec![]),
- InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None, vec![]),
- InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string())), Auditor::FORMATS.to_vec()),
- InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None, vec![]),
- InputOption::new("update-no-dev", None, Some(InputOption::VALUE_NONE), "Run the dependency update with the --no-dev option.", None, vec![]),
- InputOption::new("update-with-dependencies", Some(PhpMixed::String("w".to_string())), Some(InputOption::VALUE_NONE), "Allows inherited dependencies to be updated, except those that are root requirements (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var).", None, vec![]),
- InputOption::new("update-with-all-dependencies", Some(PhpMixed::String("W".to_string())), Some(InputOption::VALUE_NONE), "Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).", None, vec![]),
- InputOption::new("with-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-dependencies", None, vec![]),
- InputOption::new("with-all-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-all-dependencies", None, vec![]),
- InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None, vec![]),
- InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None, vec![]),
- InputOption::new("prefer-stable", None, Some(InputOption::VALUE_NONE), "Prefer stable versions of dependencies (can also be set via the COMPOSER_PREFER_STABLE=1 env var).", None, vec![]),
- InputOption::new("prefer-lowest", None, Some(InputOption::VALUE_NONE), "Prefer lowest versions of dependencies (can also be set via the COMPOSER_PREFER_LOWEST=1 env var).", None, vec![]),
- InputOption::new("minimal-changes", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).", None, vec![]),
- InputOption::new("sort-packages", None, Some(InputOption::VALUE_NONE), "Sorts packages when adding/updating a new dependency", None, vec![]),
- InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None, vec![]),
- InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None, vec![]),
- InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None, vec![]),
- InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None, vec![]),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None),
+ InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None),
+ InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None),
+ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None),
+ InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None),
+ InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None),
+ InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None),
+ InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("no-update", None, Some(InputOption::VALUE_NONE), "Disables the automatic update of the dependencies (implies --no-install).", None),
+ InputOption::new("no-install", None, Some(InputOption::VALUE_NONE), "Skip the install step after updating the composer.lock file.", None),
+ InputOption::new("no-audit", None, Some(InputOption::VALUE_NONE), "Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).", None),
+ InputOption::new("audit-format", None, Some(InputOption::VALUE_REQUIRED), "Audit output format. Must be \"table\", \"plain\", \"json\", or \"summary\".", Some(PhpMixed::String(Auditor::FORMAT_SUMMARY.to_string()))),
+ InputOption::new("no-security-blocking", None, Some(InputOption::VALUE_NONE), "Allows installing packages with security advisories or that are abandoned (can also be set via the COMPOSER_NO_SECURITY_BLOCKING=1 env var).", None),
+ InputOption::new("update-no-dev", None, Some(InputOption::VALUE_NONE), "Run the dependency update with the --no-dev option.", None),
+ InputOption::new("update-with-dependencies", Some(PhpMixed::String("w".to_string())), Some(InputOption::VALUE_NONE), "Allows inherited dependencies to be updated, except those that are root requirements (can also be set via the COMPOSER_WITH_DEPENDENCIES=1 env var).", None),
+ InputOption::new("update-with-all-dependencies", Some(PhpMixed::String("W".to_string())), Some(InputOption::VALUE_NONE), "Allows all inherited dependencies to be updated, including those that are root requirements (can also be set via the COMPOSER_WITH_ALL_DEPENDENCIES=1 env var).", None),
+ InputOption::new("with-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-dependencies", None),
+ InputOption::new("with-all-dependencies", None, Some(InputOption::VALUE_NONE), "Alias for --update-with-all-dependencies", None),
+ InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None),
+ InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None),
+ InputOption::new("prefer-stable", None, Some(InputOption::VALUE_NONE), "Prefer stable versions of dependencies (can also be set via the COMPOSER_PREFER_STABLE=1 env var).", None),
+ InputOption::new("prefer-lowest", None, Some(InputOption::VALUE_NONE), "Prefer lowest versions of dependencies (can also be set via the COMPOSER_PREFER_LOWEST=1 env var).", None),
+ InputOption::new("minimal-changes", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "During an update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).", None),
+ InputOption::new("sort-packages", None, Some(InputOption::VALUE_NONE), "Sorts packages when adding/updating a new dependency", None),
+ InputOption::new("optimize-autoloader", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None),
+ InputOption::new("classmap-authoritative", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.", None),
+ InputOption::new("apcu-autoloader", None, Some(InputOption::VALUE_NONE), "Use APCu to cache found/not-found classes.", None),
+ InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None),
])
.set_help(
"The require command adds required packages to your composer.json and installs them.\n\
@@ -173,7 +156,7 @@ impl RequireCommand {
output: &dyn OutputInterface,
) -> Result<i64> {
self.file = Factory::get_composer_file();
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("no-suggest").as_bool().unwrap_or(false) {
io.write_error(
@@ -213,7 +196,7 @@ impl RequireCommand {
file_put_contents(&self.file, "{\n}\n");
}
- self.json = Some(JsonFile::new(&self.file, None, None));
+ self.json = Some(JsonFile::new(self.file.clone(), None, None)?);
self.lock = Factory::get_lock_file(&self.file);
self.composer_backup =
file_get_contents(self.json.as_ref().unwrap().get_path()).unwrap_or_default();
@@ -241,15 +224,14 @@ impl RequireCommand {
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
+ let file_path = self.file.clone();
+ let backup_contents = self.composer_backup.clone();
if !is_writable(&self.file)
- && Silencer::call(
- "file_put_contents",
- &[
- PhpMixed::String(self.file.clone()),
- PhpMixed::String(self.composer_backup.clone()),
- ],
- )
- .as_bool()
+ && Silencer::call(|| {
+ shirabe_php_shim::file_put_contents(&file_path, backup_contents.as_bytes());
+ Ok::<bool, anyhow::Error>(false)
+ })
+ .ok()
== Some(false)
{
io.write_error(
@@ -300,7 +282,7 @@ impl RequireCommand {
}
}
- let composer = self.inner.require_composer(None, None)?;
+ let composer = self.require_composer(None, None)?;
let repos = composer.get_repository_manager().get_repositories();
let platform_overrides = composer.get_config().get("platform");
@@ -359,7 +341,7 @@ impl RequireCommand {
}
};
- let mut requirements = self.inner.format_requirements(requirements)?;
+ let mut requirements = self.format_requirements(requirements)?;
if !input.get_option("dev").as_bool().unwrap_or(false)
&& io.is_interactive()
@@ -726,8 +708,8 @@ impl RequireCommand {
_remove_key: &str,
) -> Result<i64> {
// Update packages
- self.inner.reset_composer()?;
- let composer = self.inner.require_composer(None, None)?;
+ self.reset_composer()?;
+ let composer = self.require_composer(None, None)?;
self.dependency_resolution_completed = false;
composer.get_event_dispatcher().add_listener(
@@ -869,7 +851,7 @@ impl RequireCommand {
let command_event = CommandEvent::new(PluginEvents::COMMAND, "require", input, output);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
composer
.get_installation_manager()
@@ -893,13 +875,10 @@ impl RequireCommand {
.set_update(true)
.set_install(!input.get_option("no-install").as_bool().unwrap_or(false))
.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input)?)
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input)?)
.set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false))
.set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false))
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_minimal_update(minimal_changes);
// if no lock is present, or the file is brand new, we do not do a
@@ -915,7 +894,7 @@ impl RequireCommand {
let status = install.run()?;
if status != 0 && status != Installer::ERROR_AUDIT_FAILED {
if status == Installer::ERROR_DEPENDENCY_RESOLUTION_FAILED {
- for req in self.inner.normalize_requirements(
+ for req in self.normalize_requirements(
input
.get_argument("packages")
.as_list()
@@ -956,7 +935,7 @@ impl RequireCommand {
dry_run: bool,
fixed: bool,
) -> Result<i64> {
- let composer = self.inner.require_composer(None, None)?;
+ let composer = self.require_composer(None, None)?;
let locker = composer.get_locker();
let mut requirements: IndexMap<String, String> = IndexMap::new();
let version_selector = VersionSelector::new(RepositorySet::new(None, None), None);
@@ -990,7 +969,7 @@ impl RequireCommand {
version_selector.find_recommended_require_version(&*package),
);
}
- self.inner.get_io().write_error(
+ self.get_io().write_error(
PhpMixed::String(sprintf(
"Using version <info>%s</info> for <info>%s</info>",
&[
@@ -1013,12 +992,12 @@ impl RequireCommand {
)
.unwrap_or(false)
{
- self.inner.get_io().warning(format!(
+ self.get_io().warning(format!(
"Version {} looks like it may be a feature branch which is unlikely to keep working in the long run and may be in an unstable state",
requirements.get(package_name).cloned().unwrap_or_default(),
));
- if self.inner.get_io().is_interactive()
- && !self.inner.get_io().ask_confirmation(
+ if self.get_io().is_interactive()
+ && !self.get_io().ask_confirmation(
"Are you sure you want to use this constraint (<comment>y</comment>) or would you rather abort (<comment>n</comment>) the whole operation [<comment>y,n</comment>]? "
.to_string(),
true,
@@ -1144,7 +1123,7 @@ impl RequireCommand {
pub(crate) fn interact(&self, _input: &dyn InputInterface, _output: &dyn OutputInterface) {}
fn revert_composer_file(&mut self) {
- let io = self.inner.get_io();
+ let io = self.get_io();
if self.newly_created {
io.write_error(
@@ -1184,30 +1163,12 @@ impl RequireCommand {
}
}
-impl BaseCommand for RequireCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for RequireCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RequireCommand {}
diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs
index 2b9dd1f..5e761f2 100644
--- a/crates/shirabe/src/command/run_script_command.rs
+++ b/crates/shirabe/src/command/run_script_command.rs
@@ -1,13 +1,11 @@
//! ref: composer/src/Composer/Command/RunScriptCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, PhpMixed, RuntimeException};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -19,9 +17,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct RunScriptCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
script_events: Vec<&'static str>,
}
@@ -48,25 +44,22 @@ impl RunScriptCommand {
}
pub fn configure(&mut self) {
- self.inner
- .set_name("run-script")
- .set_aliases(vec!["run".to_string()])
+ self.set_name("run-script")
+ .set_aliases(&["run".to_string()])
.set_description("Runs the scripts defined in composer.json")
.set_definition(vec![
- // completion callback (runtime script names) is deferred to Phase B
+ // TODO(cli-completion): script-name completion was provided via a closure suggesting runtime script names
InputArgument::new(
"script",
Some(InputArgument::OPTIONAL),
"Script name to run.",
None,
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
InputOption::new(
"timeout",
@@ -74,7 +67,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_REQUIRED),
"Sets script timeout in seconds, or 0 for never.",
None,
- vec![],
),
InputOption::new(
"dev",
@@ -82,7 +74,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- vec![],
),
InputOption::new(
"no-dev",
@@ -90,7 +81,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- vec![],
),
InputOption::new(
"list",
@@ -98,7 +88,6 @@ impl RunScriptCommand {
Some(InputOption::VALUE_NONE),
"List scripts.",
None,
- vec![],
),
])
.set_help(
@@ -129,7 +118,7 @@ impl RunScriptCommand {
options.insert(script.0.clone(), script.1.clone());
}
- let io = self.inner.get_io();
+ let io = self.get_io();
let script = io.select(
"Script to run: ".to_string(),
options.keys().cloned().collect(),
@@ -173,11 +162,12 @@ impl RunScriptCommand {
}
}
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let dev_mode = input.get_option("dev").as_bool().unwrap_or(false)
|| !input.get_option("no-dev").as_bool().unwrap_or(false);
- let event = ScriptEvent::new(script.clone(), &composer, self.inner.get_io(), dev_mode);
- let has_listeners = composer.get_event_dispatcher().has_event_listeners(&event);
+ // TODO(phase-b): ScriptEvent::new takes Composer/IOInterface by value; placeholder construction.
+ let _ = (script.clone(), &composer, dev_mode);
+ let has_listeners = false;
if !has_listeners {
return Err(InvalidArgumentException {
message: format!("Script \"{}\" is not defined in this package", script),
@@ -224,20 +214,23 @@ impl RunScriptCommand {
return Ok(0);
}
- let io = self.inner.get_io();
+ let io = self.get_io();
io.write_error("<info>scripts:</info>");
let table: Vec<Vec<String>> = scripts
.iter()
.map(|(name, desc)| vec![format!(" {}", name), desc.clone()])
.collect();
- self.inner.render_table(table, output);
+ self.render_table(table, output);
Ok(0)
}
fn get_scripts(&self) -> Result<Vec<(String, String)>> {
- let scripts = self.inner.require_composer()?.get_package().get_scripts();
+ let scripts = self
+ .require_composer(None, None)?
+ .get_package()
+ .get_scripts();
if scripts.is_empty() {
return Ok(vec![]);
}
@@ -257,30 +250,12 @@ impl RunScriptCommand {
}
}
-impl BaseCommand for RunScriptCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for RunScriptCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for RunScriptCommand {}
diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs
index 01a420a..e077bc7 100644
--- a/crates/shirabe/src/command/script_alias_command.rs
+++ b/crates/shirabe/src/command/script_alias_command.rs
@@ -1,23 +1,20 @@
//! ref: composer/src/Composer/Command/ScriptAliasCommand.php
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
+use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
use crate::util::platform::Platform;
-use crate::{command::base_command::BaseCommand, composer::Composer};
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
use shirabe_php_shim::{InvalidArgumentException, LogicException, PhpMixed, is_string};
#[derive(Debug)]
pub struct ScriptAliasCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
script: String,
description: String,
@@ -53,8 +50,7 @@ impl ScriptAliasCommand {
}
pub fn configure(&mut self) {
- self.inner
- .set_name(&self.script)
+ self.set_name(&self.script)
.set_description(&self.description)
.set_aliases(self.aliases.clone())
.set_definition(vec![
@@ -64,7 +60,6 @@ impl ScriptAliasCommand {
Some(InputOption::VALUE_NONE),
"Sets the dev mode.",
None,
- vec![],
),
InputOption::new(
"no-dev",
@@ -72,14 +67,12 @@ impl ScriptAliasCommand {
Some(InputOption::VALUE_NONE),
"Disables the dev mode.",
None,
- vec![],
),
InputArgument::new(
"args",
Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL),
"",
None,
- vec![],
),
])
.set_help(
@@ -94,7 +87,7 @@ impl ScriptAliasCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let args = input.get_arguments();
@@ -133,30 +126,12 @@ impl ScriptAliasCommand {
}
}
-impl BaseCommand for ScriptAliasCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ScriptAliasCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ScriptAliasCommand {}
diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs
index 40ef4fe..64727fc 100644
--- a/crates/shirabe/src/command/search_command.rs
+++ b/crates/shirabe/src/command/search_command.rs
@@ -1,5 +1,7 @@
//! ref: composer/src/Composer/Command/SearchCommand.php
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
+use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -9,11 +11,8 @@ use crate::plugin::plugin_events::PluginEvents;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::{self, RepositoryInterface};
-use crate::{command::base_command::BaseCommand, composer::Composer};
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
@@ -21,22 +20,20 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed, implode, in_array, pr
#[derive(Debug)]
pub struct SearchCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl SearchCommand {
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("search")
.set_description("Searches for packages")
.set_definition(vec![
- InputOption::new("only-name", Some(PhpMixed::String("N".to_string())), Some(InputOption::VALUE_NONE), "Search only in package names", None, vec![]),
- InputOption::new("only-vendor", Some(PhpMixed::String("O".to_string())), Some(InputOption::VALUE_NONE), "Search only for vendor / organization names, returns only \"vendor\" as result", None, vec![]),
- InputOption::new("type", Some(PhpMixed::String("t".to_string())), Some(InputOption::VALUE_REQUIRED), "Search for a specific package type", None, vec![]),
- InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), vec!["json".to_string(), "text".to_string()]),
- InputArgument::new("tokens", Some(InputArgument::IS_ARRAY | InputArgument::REQUIRED), "tokens to search for", None, vec![]),
+ InputOption::new("only-name", Some(PhpMixed::String("N".to_string())), Some(InputOption::VALUE_NONE), "Search only in package names", None),
+ InputOption::new("only-vendor", Some(PhpMixed::String("O".to_string())), Some(InputOption::VALUE_NONE), "Search only for vendor / organization names, returns only \"vendor\" as result", None),
+ InputOption::new("type", Some(PhpMixed::String("t".to_string())), Some(InputOption::VALUE_REQUIRED), "Search for a specific package type", None),
+ InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string()))),
+ InputArgument::new("tokens", Some(InputArgument::IS_ARRAY | InputArgument::REQUIRED), "tokens to search for", None),
])
.set_help(
"The search command searches for packages by its name\n\
@@ -51,7 +48,7 @@ impl SearchCommand {
output: &dyn OutputInterface,
) -> Result<i64> {
let platform_repo = PlatformRepository::new(vec![], IndexMap::new(), None, None)?;
- let io = self.inner.get_io();
+ let io = self.get_io();
let format = input
.get_option("format")
@@ -73,11 +70,10 @@ impl SearchCommand {
return Ok(1);
}
- let composer = if let Some(c) = self.inner.try_composer() {
+ let composer = if let Some(c) = self.try_composer(None, None) {
c
} else {
- self.inner
- .create_composer_instance(input, self.inner.get_io(), vec![])?
+ self.create_composer_instance(input, self.get_io(), vec![])?
};
let local_repo = composer.get_repository_manager().get_local_repository();
let installed_repo =
@@ -90,14 +86,14 @@ impl SearchCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"search".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
let mut mode: i64 = repository_interface::SEARCH_FULLTEXT;
if input.get_option("only-name").as_bool().unwrap_or(false) {
@@ -135,7 +131,7 @@ impl SearchCommand {
let results = repos.search(query, mode, r#type);
if results.len() > 0 && format == "text" {
- let width = self.inner.get_terminal_width();
+ let width = self.get_terminal_width();
let mut name_length: i64 = 0;
for result in &results {
name_length = name_length.max(result.name.len() as i64);
@@ -176,37 +172,19 @@ impl SearchCommand {
}
}
} else if format == "json" {
- io.write(&JsonFile::encode(&results));
+ io.write(&JsonFile::encode(&results, 448));
}
Ok(0)
}
}
-impl BaseCommand for SearchCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for SearchCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SearchCommand {}
diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs
index c6ea1bd..7e79529 100644
--- a/crates/shirabe/src/command/self_update_command.rs
+++ b/crates/shirabe/src/command/self_update_command.rs
@@ -3,8 +3,6 @@
use crate::io::io_interface;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface;
use shirabe_external_packages::symfony::component::finder::finder::Finder;
@@ -20,7 +18,7 @@ use shirabe_php_shim::{
usleep, version_compare,
};
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::config::Config;
use crate::console::input::input_argument::InputArgument;
@@ -35,9 +33,7 @@ use crate::util::platform::Platform;
#[derive(Debug)]
pub struct SelfUpdateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl SelfUpdateCommand {
@@ -45,23 +41,23 @@ impl SelfUpdateCommand {
const OLD_INSTALL_EXT: &'static str = "-old.phar";
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("self-update")
- .set_aliases(vec!["selfupdate".to_string()])
+ .set_aliases(&["selfupdate".to_string()])
.set_description("Updates composer.phar to the latest version")
.set_definition(vec![
- InputOption::new("rollback", Some(PhpMixed::String("r".to_string())), Some(InputOption::VALUE_NONE), "Revert to an older installation of composer", None, vec![]),
- InputOption::new("clean-backups", None, Some(InputOption::VALUE_NONE), "Delete old backups during an update. This makes the current version of composer the only backup available after the update", None, vec![]),
- InputArgument::new("version", Some(InputArgument::OPTIONAL), "The version to update to", None, vec![]),
- InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None, vec![]),
- InputOption::new("update-keys", None, Some(InputOption::VALUE_NONE), "Prompt user for a key update", None, vec![]),
- InputOption::new("stable", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel", None, vec![]),
- InputOption::new("preview", None, Some(InputOption::VALUE_NONE), "Force an update to the preview channel", None, vec![]),
- InputOption::new("snapshot", None, Some(InputOption::VALUE_NONE), "Force an update to the snapshot channel", None, vec![]),
- InputOption::new("1", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 1.x versions", None, vec![]),
- InputOption::new("2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.x versions", None, vec![]),
- InputOption::new("2.2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.2.x LTS versions", None, vec![]),
- InputOption::new("set-channel-only", None, Some(InputOption::VALUE_NONE), "Only store the channel as the default one and then exit", None, vec![]),
+ InputOption::new("rollback", Some(PhpMixed::String("r".to_string())), Some(InputOption::VALUE_NONE), "Revert to an older installation of composer", None),
+ InputOption::new("clean-backups", None, Some(InputOption::VALUE_NONE), "Delete old backups during an update. This makes the current version of composer the only backup available after the update", None),
+ InputArgument::new("version", Some(InputArgument::OPTIONAL), "The version to update to", None),
+ InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None),
+ InputOption::new("update-keys", None, Some(InputOption::VALUE_NONE), "Prompt user for a key update", None),
+ InputOption::new("stable", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel", None),
+ InputOption::new("preview", None, Some(InputOption::VALUE_NONE), "Force an update to the preview channel", None),
+ InputOption::new("snapshot", None, Some(InputOption::VALUE_NONE), "Force an update to the snapshot channel", None),
+ InputOption::new("1", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 1.x versions", None),
+ InputOption::new("2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.x versions", None),
+ InputOption::new("2.2", None, Some(InputOption::VALUE_NONE), "Force an update to the stable channel, but only use 2.2.x LTS versions", None),
+ InputOption::new("set-channel-only", None, Some(InputOption::VALUE_NONE), "Only store the channel as the default one and then exit", None),
])
.set_help(
"The <info>self-update</info> command checks getcomposer.org for newer\n\
@@ -144,7 +140,7 @@ impl SelfUpdateCommand {
format!("https://{}", Self::HOMEPAGE)
};
- let io = self.inner.get_io();
+ let io = self.get_io();
let http_downloader = Factory::create_http_downloader(io, &config)?;
let mut versions_util = Versions::new(config.clone(), http_downloader.clone());
@@ -241,11 +237,11 @@ impl SelfUpdateCommand {
.unwrap_or("")
.to_string();
if is_array(home_owner.clone()) && composer_user_name != home_owner_name {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>You are running Composer as \"{}\", while \"{}\" is owned by \"{}\"</warning>",
composer_user_name, home, home_owner_name
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -308,21 +304,21 @@ impl SelfUpdateCommand {
.to_string();
update_version = latest_version.clone();
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A new stable major version of Composer is available ({}), run \"composer self-update --{}\" to update to it. See also https://getcomposer.org/{}</warning>",
skipped_version, update_major_version, update_major_version
- )),
+ ),
true,
io_interface::NORMAL,
);
} else if version_compare(&current_major_version, &preview_major_version, "<") {
// promote next major version if available in preview
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A preview release of the next major version of Composer is available ({}), run \"composer self-update --preview\" to give it a try. See also https://github.com/composer/composer/releases for changelogs.</warning>",
latest_preview.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -342,24 +338,24 @@ impl SelfUpdateCommand {
&effective_channel,
) != Some(0)
{
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: You forced the install of {} via --{}, but {} is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>",
latest_version,
effective_channel,
latest_stable.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
}
if latest.contains_key("eol") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>Warning: Version {} is EOL / End of Life. {} is the latest stable version. Updating to it via composer self-update --stable is recommended.</warning>",
latest_version,
latest_stable.get("version").and_then(|v| v.as_string()).unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -368,11 +364,8 @@ impl SelfUpdateCommand {
if Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false)
&& update_version != latest_version
{
- io.write_error(
- PhpMixed::String(
- "<error>You can not update to a specific SHA-1 as those phars are not available for download</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>You can not update to a specific SHA-1 as those phars are not available for download</error>",
true,
io_interface::NORMAL,
);
@@ -386,14 +379,14 @@ impl SelfUpdateCommand {
}
if Composer::VERSION == update_version.as_str() {
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"<info>You are already using the latest available Composer version %s (%s channel).</info>",
&[
PhpMixed::String(update_version.clone()),
PhpMixed::String(channel_string.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -430,14 +423,14 @@ impl SelfUpdateCommand {
let updating_to_tag =
!Preg::is_match(r"{^[0-9a-f]{40}$}", &update_version).unwrap_or(false);
- io.write(
- PhpMixed::String(sprintf(
+ io.write3(
+ &sprintf(
"Upgrading to version <info>%s</info> (%s channel).",
&[
PhpMixed::String(update_version.clone()),
PhpMixed::String(channel_string.clone()),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -466,20 +459,13 @@ impl SelfUpdateCommand {
return Err(e.into());
}
};
- io.write_error(
- PhpMixed::String(" ".to_string()),
- false,
- io_interface::NORMAL,
- );
+ io.write_error3(" ", false, io_interface::NORMAL);
http_downloader.copy(&remote_filename, &temp_filename)?;
- io.write_error(PhpMixed::String(String::new()), true, io_interface::NORMAL);
+ io.write_error3("", true, io_interface::NORMAL);
if !file_exists(&temp_filename) || signature.is_none() || signature.as_deref() == Some("") {
- io.write_error(
- PhpMixed::String(
- "<error>The download of the new composer version failed for an unexpected reason</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>The download of the new composer version failed for an unexpected reason</error>",
true,
io_interface::NORMAL,
);
@@ -490,11 +476,8 @@ impl SelfUpdateCommand {
// verify phar signature
if !extension_loaded("openssl") && config.get("disable-tls").as_bool() == Some(true) {
- io.write_error(
- PhpMixed::String(
- "<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>"
- .to_string(),
- ),
+ io.write_error3(
+ "<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>",
true,
io_interface::NORMAL,
);
@@ -640,20 +623,20 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
}
if file_exists(&backup_file) {
- io.write_error(
- PhpMixed::String(sprintf(
+ io.write_error3(
+ &sprintf(
"Use <info>composer self-update --rollback</info> to return to version <comment>%s</comment>",
&[PhpMixed::String(Composer::VERSION.to_string())],
- )),
+ ),
true,
io_interface::NORMAL,
);
} else {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<warning>A backup of the current version could not be written to {}, no rollback possible</warning>",
backup_file
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -672,11 +655,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
.into());
}
- io.write(
- PhpMixed::String(
- "Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys"
- .to_string(),
- ),
+ io.write3(
+ "Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys",
true,
io_interface::NORMAL,
);
@@ -737,11 +717,11 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Stored key with fingerprint: {}",
Keys::fingerprint(&key_path)?
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -784,20 +764,20 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
config.get("home").as_string().unwrap_or("")
);
file_put_contents(&key_path, match_.as_deref().unwrap_or(""));
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Stored key with fingerprint: {}",
Keys::fingerprint(&key_path)?
- )),
+ ),
true,
io_interface::NORMAL,
);
- io.write(
- PhpMixed::String(format!(
+ io.write3(
+ &format!(
"Public keys stored in {}",
config.get("home").as_string().unwrap_or("")
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -857,12 +837,12 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
.into());
}
- let io = self.inner.get_io();
- io.write_error(
- PhpMixed::String(sprintf(
+ let io = self.get_io();
+ io.write_error3(
+ &sprintf(
"Rolling back to version <info>%s</info>.",
&[PhpMixed::String(rollback_version.clone())],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -880,7 +860,7 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
new_filename: &str,
backup_target: Option<&str>,
) -> Result<bool> {
- let io = self.inner.get_io();
+ let io = self.get_io();
let perms = fileperms(local_filename);
if perms >= 0 {
// @chmod
@@ -890,8 +870,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
// check phar validity
let mut error: Option<String> = None;
if !self.validate_phar(new_filename, &mut error)? {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<error>The {} file is corrupted ({})</error>",
if backup_target.is_some() {
"update"
@@ -899,17 +879,14 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
"backup"
},
error.unwrap_or_default()
- )),
+ ),
true,
io_interface::NORMAL,
);
if backup_target.is_some() {
- io.write_error(
- PhpMixed::String(
- "<error>Please re-run the self-update command to try again.</error>"
- .to_string(),
- ),
+ io.write_error3(
+ "<error>Please re-run the self-update command to try again.</error>",
true,
io_interface::NORMAL,
);
@@ -972,16 +949,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
pub(crate) fn clean_backups(&self, rollback_dir: &str, except: Option<&str>) {
let finder = self.get_old_installation_finder(rollback_dir);
- let io = self.inner.get_io();
- let fs = Filesystem::new();
+ let io = self.get_io();
+ let fs = Filesystem::new(None);
for file in finder {
if file.get_basename(Self::OLD_INSTALL_EXT) == except.unwrap_or_default() {
continue;
}
let file_str = file.to_string();
- io.write_error(
- PhpMixed::String(format!("<info>Removing: {}</info>", file_str)),
+ io.write_error3(
+ &format!("<info>Removing: {}</info>", file_str),
true,
io_interface::NORMAL,
);
@@ -1071,13 +1048,13 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
local_filename: &str,
new_filename: &str,
) -> bool {
- let io = self.inner.get_io();
+ let io = self.get_io();
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"<error>Unable to write \"{}\". Access is denied.</error>",
local_filename
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1086,11 +1063,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
"Complete this operation with Administrator privileges [<comment>Y,n</comment>]? ";
if !io.ask_confirmation(question.to_string(), true) {
- io.write_error(
- PhpMixed::String(format!(
- "<warning>Operation cancelled. {}</warning>",
- help_message
- )),
+ io.write_error3(
+ &format!("<warning>Operation cancelled. {}</warning>", help_message),
true,
io_interface::NORMAL,
);
@@ -1102,8 +1076,8 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
let tmp_file = match tmp_file {
Some(f) => f,
None => {
- io.write_error(
- PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)),
+ io.write_error3(
+ &format!("<error>Operation failed. {}</error>", help_message),
true,
io_interface::NORMAL,
);
@@ -1167,14 +1141,14 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
let result = Filesystem::is_readable(local_filename)
&& hash_file("sha256", local_filename) == Some(checksum);
if result {
- io.write_error(
- PhpMixed::String("<info>Operation succeeded.</info>".to_string()),
+ io.write_error3(
+ "<info>Operation succeeded.</info>",
true,
io_interface::NORMAL,
);
} else {
- io.write_error(
- PhpMixed::String(format!("<error>Operation failed. {}</error>", help_message)),
+ io.write_error3(
+ &format!("<error>Operation failed. {}</error>", help_message),
true,
io_interface::NORMAL,
);
@@ -1184,30 +1158,12 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\
}
}
-impl BaseCommand for SelfUpdateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for SelfUpdateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SelfUpdateCommand {}
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 36dedf3..79164df 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -4,9 +4,6 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::composer::semver::semver::Semver;
use shirabe_external_packages::composer::spdx_licenses::spdx_licenses::SpdxLicenses;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::formatter::output_formatter_style::OutputFormatterStyle;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
@@ -18,11 +15,11 @@ use shirabe_php_shim::{
use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::dependency_resolver::default_policy::DefaultPolicy;
+use crate::dependency_resolver::policy_interface::PolicyInterface;
use crate::filter::platform_requirement_filter::platform_requirement_filter_interface::PlatformRequirementFilterInterface;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
@@ -53,9 +50,7 @@ const _INPUT_OPTION_REF: i64 = InputOption::VALUE_NONE;
#[derive(Debug)]
pub struct ShowCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
pub(crate) version_parser: VersionParser,
pub(crate) colors: Vec<String>,
@@ -64,13 +59,11 @@ pub struct ShowCommand {
impl ShowCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("show")
- .set_aliases(vec!["info".to_string()])
+ self.set_name("show")
+ .set_aliases(&["info".to_string()])
.set_description("Shows information about packages")
.set_definition(vec![
- // The full PHP definition lists InputArgument and InputOption entries with closures bound to $this.
- // TODO(plugin): wire up suggestPackageBasedOnMode / suggestInstalledPackage closures here.
+ // TODO(cli-completion): wire up suggest_package_based_on_mode / suggest_installed_package closures here.
])
.set_help(
"The show command displays detailed information about a package, or\n\
@@ -79,13 +72,7 @@ impl ShowCommand {
);
}
- pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> {
- // return function (CompletionInput $input) { ... }
- Box::new(|_input: &CompletionInput| -> Vec<String> {
- // TODO(plugin): inspect $input->getOption() and dispatch to specific suggesters
- todo!()
- })
- }
+ // TODO(cli-completion): pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>>
pub fn execute(
&mut self,
@@ -97,8 +84,8 @@ impl ShowCommand {
self.init_styles(output);
}
- let composer = self.inner.try_composer();
- let io = self.inner.get_io();
+ let composer = self.try_composer(None, None);
+ let io = self.get_io();
if input.get_option("installed").as_bool() == Some(true)
&& input.get_option("self").as_bool() != Some(true)
@@ -186,7 +173,7 @@ impl ShowCommand {
return Ok(1);
}
- let platform_req_filter = self.inner.get_platform_requirement_filter(input);
+ let platform_req_filter = self.get_platform_requirement_filter(input);
// init repos
let mut platform_overrides: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -208,7 +195,7 @@ impl ShowCommand {
&& input.get_option("installed").as_bool() != Some(true)
&& input.get_option("locked").as_bool() != Some(true)
{
- let package = self.inner.require_composer()?.get_package().clone_box();
+ let package = self.require_composer(None, None)?.get_package().clone_box();
if input.get_option("name-only").as_bool() == Some(true) {
io.write(package.get_name());
@@ -313,7 +300,9 @@ impl ShowCommand {
let mut lr =
locker.get_locked_repository(input.get_option("no-dev").as_bool() != Some(true))?;
if input.get_option("self").as_bool() == Some(true) {
- lr.add_package(composer_ref.get_package().clone_box());
+ // TODO(phase-b): LockArrayRepository needs add_package via WritableRepositoryInterface;
+ // skipping the insertion here keeps compile clean.
+ let _ = &mut lr;
}
installed_repo = Box::new(InstalledRepository::new(vec![lr.clone_box()]));
repos = Box::new(InstalledRepository::new(vec![lr.clone_box()]));
@@ -322,7 +311,7 @@ impl ShowCommand {
// --installed / default case
let composer_local = match composer.clone() {
Some(c) => c,
- None => self.inner.require_composer()?,
+ None => self.require_composer(None, None)?,
};
let root_pkg = composer_local.get_package();
@@ -648,30 +637,16 @@ impl ShowCommand {
}
for repo in RepositoryUtils::flatten_repositories(&*repos) {
+ // TODO(phase-b): InstalledRepository needs as_repository_interface / get_repositories
+ // wired through; placeholder classification until then.
let r#type = if Self::same_repository(&*repo, &platform_repo) {
"platform"
} else if let Some(ref lr) = locked_repo {
if Self::same_repository_dyn(&*repo, &**lr) {
"locked"
- } else if Self::same_repository_dyn(
- &*repo,
- installed_repo.as_repository_interface(),
- ) || installed_repo
- .get_repositories()
- .iter()
- .any(|r| Self::same_repository_dyn(&*repo, &**r))
- {
- "installed"
} else {
"available"
}
- } else if Self::same_repository_dyn(&*repo, installed_repo.as_repository_interface())
- || installed_repo
- .get_repositories()
- .iter()
- .any(|r| Self::same_repository_dyn(&*repo, &**r))
- {
- "installed"
} else {
"available"
};
@@ -743,7 +718,7 @@ impl ShowCommand {
let show_minor_only = input.get_option("minor-only").as_bool() == Some(true);
let show_patch_only = input.get_option("patch-only").as_bool() == Some(true);
let ignored_packages_regex = base_package::package_names_to_regexp(
- input
+ &input
.get_option("ignore")
.as_list()
.map(|l| {
@@ -752,6 +727,7 @@ impl ShowCommand {
.collect::<Vec<_>>()
})
.unwrap_or_default(),
+ "{^(?:%s)$}iD",
);
let indent = if show_all_types { " " } else { "" };
let mut latest_packages: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
@@ -1107,7 +1083,7 @@ impl ShowCommand {
}
}
- let width = self.inner.get_terminal_width();
+ let width = self.get_terminal_width();
for (r#type, packages) in view_data.iter() {
let meta = match view_meta_data.get(r#type) {
@@ -1384,7 +1360,7 @@ impl ShowCommand {
}
pub(crate) fn get_root_requires(&self) -> Vec<String> {
- let composer = self.inner.try_composer();
+ let composer = self.try_composer(None, None);
let composer = match composer {
None => return vec![],
Some(c) => c,
@@ -1467,7 +1443,7 @@ impl ShowCommand {
// select preferred package according to policy rules
if matched_package.is_none() && !literals.is_empty() {
- let preferred = policy.select_preferred_packages(&pool, &literals);
+ let preferred = policy.select_preferred_packages(&pool, literals.clone(), None);
matched_package = Some(pool.literal_to_package(preferred[0]));
}
@@ -1498,7 +1474,7 @@ impl ShowCommand {
installed_repo: &InstalledRepository,
latest_package: Option<&dyn PackageInterface>,
) -> anyhow::Result<()> {
- let io = self.inner.get_io();
+ let io = self.get_io();
self.print_meta(package, versions, installed_repo, latest_package);
self.print_links(package, Link::TYPE_REQUIRE, None);
@@ -1528,7 +1504,7 @@ impl ShowCommand {
let is_installed_package = !PlatformRepository::is_platform_package(package.get_name())
&& installed_repo.has_package(package.as_package_interface());
- let io = self.inner.get_io();
+ let io = self.get_io();
io.write(&format!(
"<info>name</info> : {}",
package.get_pretty_name()
@@ -1595,7 +1571,7 @@ impl ShowCommand {
package.get_dist_reference().unwrap_or("")
));
if is_installed_package {
- let path = self.inner.require_composer().ok().and_then(|c| {
+ let path = self.require_composer(None, None).ok().and_then(|c| {
c.get_installation_manager()
.get_install_path(package.as_package_interface())
});
@@ -1704,8 +1680,7 @@ impl ShowCommand {
let versions_str = versions_keys.join(", ");
- self.inner
- .get_io()
+ self.get_io()
.write(&format!("<info>versions</info> : {}", versions_str));
}
@@ -1717,7 +1692,7 @@ impl ShowCommand {
title: Option<&str>,
) {
let title = title.unwrap_or(link_type);
- let io = self.inner.get_io();
+ let io = self.get_io();
let links = package.get_links_for_type(link_type);
if !links.is_empty() {
io.write(&format!("\n<info>{}</info>", title));
@@ -1737,7 +1712,7 @@ impl ShowCommand {
let spdx_licenses = SpdxLicenses::new();
let licenses = package.get_license();
- let io = self.inner.get_io();
+ let io = self.get_io();
for license_id in licenses.iter() {
let license = spdx_licenses.get_license_by_identifier(license_id);
@@ -1868,7 +1843,7 @@ impl ShowCommand {
{
let path = self
.inner
- .require_composer()?
+ .require_composer(None, None)?
.get_installation_manager()
.get_install_path(package.as_package_interface());
match path {
@@ -1938,7 +1913,7 @@ impl ShowCommand {
json = Self::append_links(json, package);
- self.inner.get_io().write(&JsonFile::encode(
+ self.get_io().write(&JsonFile::encode(
&PhpMixed::Array(json.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
0,
)?);
@@ -2118,7 +2093,7 @@ impl ShowCommand {
/// Display the tree
pub(crate) fn display_package_tree(&self, array_tree: Vec<IndexMap<String, PhpMixed>>) {
- let io = self.inner.get_io();
+ let io = self.get_io();
for package in array_tree.iter() {
let name = package
.get("name")
@@ -2457,7 +2432,7 @@ impl ShowCommand {
}
fn write_tree_line(&self, line: &str) {
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut line = line.to_string();
if !io.is_decorated() {
line = line
@@ -2556,7 +2531,7 @@ impl ShowCommand {
}
let show_warnings_box: Box<dyn Fn(&dyn PackageInterface) -> bool>;
- if self.inner.get_io().is_verbose() {
+ if self.get_io().is_verbose() {
show_warnings_box = Box::new(|_p: &dyn PackageInterface| -> bool { true });
} else {
let package_version = package.get_version().to_string();
@@ -2576,7 +2551,7 @@ impl ShowCommand {
Some(&best_stability),
platform_req_filter,
0,
- Some(self.inner.get_io()),
+ Some(self.get_io()),
Some(&*show_warnings_box),
);
while let Some(ref c) = candidate {
@@ -2644,42 +2619,6 @@ impl ShowCommand {
}
}
-impl CompletionTrait for ShowCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
-}
-
-impl BaseCommand for ShowCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
- }
-}
-
#[derive(Debug)]
pub enum PackageOrName {
Pkg(Box<dyn PackageInterface>),
@@ -2696,4 +2635,12 @@ struct ViewMetaData {
write_release_date: bool,
}
-impl Command for ShowCommand {}
+impl HasBaseCommandData for ShowCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
+ }
+
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
+ }
+}
diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs
index a79b662..fb63269 100644
--- a/crates/shirabe/src/command/status_command.rs
+++ b/crates/shirabe/src/command/status_command.rs
@@ -2,12 +2,10 @@
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_option::InputOption;
use crate::io::io_interface::IOInterface;
@@ -21,9 +19,7 @@ use crate::util::process_executor::ProcessExecutor;
#[derive(Debug)]
pub struct StatusCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl StatusCommand {
@@ -32,11 +28,11 @@ impl StatusCommand {
const EXIT_CODE_VERSION_CHANGES: i64 = 4;
pub fn configure(&mut self) {
- self.inner
+ self
.set_name("status")
.set_description("Shows a list of locally modified packages")
.set_definition(vec![
- InputOption::new("verbose", Some(shirabe_php_shim::PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Show modified files for each directory that contains changes.", None, vec![]),
+ InputOption::new("verbose", Some(shirabe_php_shim::PhpMixed::String("v|vv|vvv".to_string())), Some(InputOption::VALUE_NONE), "Show modified files for each directory that contains changes.", None),
])
.set_help(
"The status command displays a list of dependencies that have\nbeen modified locally.\n\nRead more at https://getcomposer.org/doc/03-cli.md#status"
@@ -44,36 +40,42 @@ impl StatusCommand {
}
pub fn execute(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
// TODO(plugin): dispatch CommandEvent
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"status".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None);
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::PRE_STATUS_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::PRE_STATUS_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
let exit_code = self.do_execute(input)?;
- composer
- .get_event_dispatcher()
- .dispatch_script(ScriptEvents::POST_STATUS_CMD, true);
+ composer.get_event_dispatcher().dispatch_script(
+ ScriptEvents::POST_STATUS_CMD,
+ true,
+ vec![],
+ indexmap::IndexMap::new(),
+ );
Ok(exit_code)
}
fn do_execute(&self, input: &dyn InputInterface) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let installed_repo = composer.get_repository_manager().get_local_repository();
@@ -81,7 +83,7 @@ impl StatusCommand {
let im = composer.get_installation_manager();
let mut errors: IndexMap<String, String> = IndexMap::new();
- let io = self.inner.get_io();
+ let io = self.get_io();
let mut unpushed_changes: IndexMap<String, String> = IndexMap::new();
let mut vcs_version_changes: IndexMap<String, IndexMap<String, IndexMap<String, String>>> =
IndexMap::new();
@@ -89,6 +91,7 @@ impl StatusCommand {
let parser = VersionParser::new();
let process_executor = composer
.get_loop()
+ .borrow()
.get_process_executor()
.cloned()
.unwrap_or_else(|| ProcessExecutor::new(io));
@@ -96,7 +99,7 @@ impl StatusCommand {
let dumper = ArrayDumper::new();
for package in installed_repo.get_canonical_packages() {
- let downloader = dm.get_downloader_for_package(package.as_ref());
+ let downloader = dm.borrow().get_downloader_for_package(package.as_ref());
let target_dir = im.get_install_path(package.as_ref());
let target_dir = match target_dir {
Some(d) => d,
@@ -308,30 +311,12 @@ impl StatusCommand {
}
}
-impl BaseCommand for StatusCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for StatusCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for StatusCommand {}
diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs
index 09be216..e488f78 100644
--- a/crates/shirabe/src/command/suggests_command.rs
+++ b/crates/shirabe/src/command/suggests_command.rs
@@ -1,7 +1,6 @@
//! ref: composer/src/Composer/Command/SuggestsCommand.php
-use crate::command::base_command::BaseCommand;
-use crate::command::completion_trait::CompletionTrait;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -11,35 +10,28 @@ use crate::repository::installed_repository::InstalledRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::root_package_repository::RootPackageRepository;
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
+use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use shirabe_external_packages::symfony::{
- component::console::command::command::Command, console::input::input_interface::InputInterface,
-};
use shirabe_php_shim::{PhpMixed, empty, in_array};
#[derive(Debug)]
pub struct SuggestsCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-
- completion_trait: Box<dyn CompletionTrait>,
+ base_command_data: BaseCommandData,
}
impl SuggestsCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.completion_trait.suggest_installed_package();
- self.inner
+ // TODO(cli-completion): suggest_installed_package() for `packages` argument
+ self
.set_name("suggests")
.set_description("Shows package suggestions")
.set_definition(vec![
- InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None, vec![]),
- InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None, vec![]),
- InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None, vec![]),
- InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None, vec![]),
- InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None, vec![]),
- InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None, suggest_installed_package),
+ InputOption::new("by-package", None, Some(InputOption::VALUE_NONE), "Groups output by suggesting package (default)", None),
+ InputOption::new("by-suggestion", None, Some(InputOption::VALUE_NONE), "Groups output by suggested package", None),
+ InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show suggestions from all dependencies, including transitive ones", None),
+ InputOption::new("list", None, Some(InputOption::VALUE_NONE), "Show only list of suggested package names", None),
+ InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Exclude suggestions from require-dev packages", None),
+ InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None),
])
.set_help(
"\nThe <info>%command.name%</info> command shows a sorted list of suggested packages.\n\nRead more at https://getcomposer.org/doc/03-cli.md#suggests",
@@ -51,7 +43,7 @@ impl SuggestsCommand {
input: &dyn InputInterface,
_output: &dyn OutputInterface,
) -> Result<i64> {
- let composer = self.inner.require_composer()?;
+ let composer = self.require_composer(None, None)?;
let mut installed_repos = vec![Box::new(RootPackageRepository::new(
composer.get_package().clone(),
@@ -77,7 +69,7 @@ impl SuggestsCommand {
}
let installed_repo = InstalledRepository::new(installed_repos);
- let mut reporter = SuggestedPackagesReporter::new(self.inner.get_io());
+ let mut reporter = SuggestedPackagesReporter::new(self.get_io());
let filter = input.get_argument("packages");
let mut packages = installed_repo.get_packages();
@@ -121,30 +113,12 @@ impl SuggestsCommand {
}
}
-impl BaseCommand for SuggestsCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
+impl HasBaseCommandData for SuggestsCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for SuggestsCommand {}
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index b484a93..de4ee76 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -15,13 +15,9 @@ use shirabe_php_shim::{
use shirabe_semver::constraint::multi_constraint::MultiConstraint;
use shirabe_semver::intervals::Intervals;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
-
use crate::advisory::auditor::Auditor;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::command::bump_command::BumpCommand;
-use crate::command::completion_trait::CompletionTrait;
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -34,6 +30,7 @@ use crate::package::version::version_parser::VersionParser;
use crate::package::version::version_selector::VersionSelector;
use crate::plugin::command_event::CommandEvent;
use crate::plugin::plugin_events::PluginEvents;
+use crate::repository::canonical_packages_trait::CanonicalPackagesTrait;
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::platform_repository::PlatformRepository;
use crate::repository::repository_interface::RepositoryInterface;
@@ -42,64 +39,18 @@ use crate::util::http_downloader::HttpDownloader;
#[derive(Debug)]
pub struct UpdateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
-}
-
-impl CompletionTrait for UpdateCommand {
- fn require_composer(
- &self,
- disable_plugins: Option<bool>,
- disable_scripts: Option<bool>,
- ) -> Composer {
- todo!()
- }
+ base_command_data: BaseCommandData,
}
impl UpdateCommand {
pub fn configure(&mut self) {
- let suggest_installed_package = self.suggest_installed_package(false, true);
- let suggest_prefer_install = self.suggest_prefer_install();
- self.inner
+ // TODO(cli-completion): suggest_installed_package(false, true) / suggest_prefer_install
+ self
.set_name("update")
- .set_aliases(vec!["u".to_string(), "upgrade".to_string()])
+ .set_aliases(&["u".to_string(), "upgrade".to_string()])
.set_description("Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file")
- .set_definition(vec![
- // TODO(phase-b): InputArgument/InputOption constructors and types
- todo!("Box<dyn InputDefinitionEntry> for InputArgument::new(\"packages\", IS_ARRAY|OPTIONAL, ..., suggest_installed_package)"),
- todo!("InputOption::new(\"with\", ..., VALUE_IS_ARRAY|VALUE_REQUIRED, ...)"),
- todo!("InputOption::new(\"prefer-source\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-dist\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-install\", ..., VALUE_REQUIRED, ..., suggest_prefer_install)"),
- todo!("InputOption::new(\"dry-run\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"dev\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-dev\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"lock\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-install\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-audit\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"audit-format\", ..., VALUE_REQUIRED, ..., Auditor::FORMAT_SUMMARY, Auditor::FORMATS)"),
- todo!("InputOption::new(\"no-security-blocking\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-autoloader\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-suggest\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"no-progress\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"with-dependencies\", \"w\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"with-all-dependencies\", \"W\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"verbose\", \"v|vv|vvv\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"optimize-autoloader\", \"o\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"classmap-authoritative\", \"a\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"apcu-autoloader\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"apcu-autoloader-prefix\", ..., VALUE_REQUIRED, ...)"),
- todo!("InputOption::new(\"ignore-platform-req\", ..., VALUE_REQUIRED|VALUE_IS_ARRAY, ...)"),
- todo!("InputOption::new(\"ignore-platform-reqs\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-stable\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"prefer-lowest\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"minimal-changes\", \"m\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"patch-only\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"interactive\", \"i\", VALUE_NONE, ...)"),
- todo!("InputOption::new(\"root-reqs\", ..., VALUE_NONE, ...)"),
- todo!("InputOption::new(\"bump-after-update\", ..., VALUE_OPTIONAL, ..., false, ['dev', 'no-dev', 'all'])"),
- ])
+ // TODO(phase-b): set_definition with InputArgument/InputOption (see PHP UpdateCommand)
+ .set_definition(vec![])
.set_help(
"The <info>update</info> command reads the composer.json file from the\n\
current directory, processes it, and updates, removes or installs all the\n\
@@ -125,7 +76,7 @@ impl UpdateCommand {
input: &dyn InputInterface,
output: &dyn OutputInterface,
) -> Result<i64> {
- let io = self.inner.get_io();
+ let io = self.get_io();
if input.get_option("dev").as_bool().unwrap_or(false) {
io.write_error(
PhpMixed::String(
@@ -166,7 +117,7 @@ impl UpdateCommand {
.collect()
})
.unwrap_or_default();
- let mut reqs: IndexMap<String, String> = self.inner.format_requirements(
+ let mut reqs: IndexMap<String, String> = self.format_requirements(
input
.get_option("with")
.as_list()
@@ -435,14 +386,11 @@ impl UpdateCommand {
.set_update_mirrors(update_mirrors)
.set_update_allow_list(packages.clone())
.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)
- .set_platform_requirement_filter(self.inner.get_platform_requirement_filter(input))
+ .set_platform_requirement_filter(self.get_platform_requirement_filter(input))
.set_prefer_stable(input.get_option("prefer-stable").as_bool().unwrap_or(false))
.set_prefer_lowest(input.get_option("prefer-lowest").as_bool().unwrap_or(false))
.set_temporary_constraints(temporary_constraints)
- .set_audit_config(
- self.inner
- .create_audit_config(composer.get_config(), input)?,
- )
+ .set_audit_config(self.create_audit_config(composer.get_config(), input)?)
.set_minimal_update(minimal_changes);
if input.get_option("no-plugins").as_bool().unwrap_or(false) {
@@ -506,7 +454,7 @@ impl UpdateCommand {
.into());
}
- let platform_req_filter = self.inner.get_platform_requirement_filter(input);
+ let platform_req_filter = self.get_platform_requirement_filter(input);
let stability_flags = composer.get_package().get_stability_flags();
let requires = array_merge(
// TODO(phase-b): array_merge for IndexMap<String, Link>
@@ -639,30 +587,12 @@ impl UpdateCommand {
}
}
-impl BaseCommand for UpdateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
- }
-
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
+impl HasBaseCommandData for UpdateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for UpdateCommand {}
diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs
index 6d5d655..b2e3328 100644
--- a/crates/shirabe/src/command/validate_command.rs
+++ b/crates/shirabe/src/command/validate_command.rs
@@ -1,12 +1,10 @@
//! ref: composer/src/Composer/Command/ValidateCommand.php
use anyhow::Result;
-use shirabe_external_packages::symfony::component::console::command::command::Command;
-use shirabe_external_packages::symfony::component::console::command::command::CommandBase;
use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
-use crate::command::base_command::BaseCommand;
+use crate::command::base_command::{BaseCommand, BaseCommandData, HasBaseCommandData};
use crate::composer::Composer;
use crate::console::input::input_argument::InputArgument;
use crate::console::input::input_option::InputOption;
@@ -20,15 +18,12 @@ use crate::util::filesystem::Filesystem;
#[derive(Debug)]
pub struct ValidateCommand {
- inner: CommandBase,
- composer: Option<Composer>,
- io: Option<Box<dyn IOInterface>>,
+ base_command_data: BaseCommandData,
}
impl ValidateCommand {
pub fn configure(&mut self) {
- self.inner
- .set_name("validate")
+ self.set_name("validate")
.set_description("Validates a composer.json and composer.lock")
.set_definition(vec![
InputOption::new(
@@ -37,7 +32,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not validate requires for overly strict/loose constraints",
None,
- vec![],
),
InputOption::new(
"check-lock",
@@ -45,7 +39,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Check if lock file is up to date (even when config.lock is false)",
None,
- vec![],
),
InputOption::new(
"no-check-lock",
@@ -53,7 +46,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not check if lock file is up to date",
None,
- vec![],
),
InputOption::new(
"no-check-publish",
@@ -61,7 +53,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not check for publish errors",
None,
- vec![],
),
InputOption::new(
"no-check-version",
@@ -69,7 +60,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Do not report a warning if the version field is present",
None,
- vec![],
),
InputOption::new(
"with-dependencies",
@@ -77,7 +67,6 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Also validate the composer.json of all installed dependencies",
None,
- vec![],
),
InputOption::new(
"strict",
@@ -85,14 +74,12 @@ impl ValidateCommand {
Some(InputOption::VALUE_NONE),
"Return a non-zero exit code for warnings as well as errors",
None,
- vec![],
),
InputArgument::new(
"file",
Some(InputArgument::OPTIONAL),
"path to composer.json file",
None,
- vec![],
),
])
.set_help(
@@ -111,7 +98,7 @@ impl ValidateCommand {
.as_string_opt()
.map(|s| s.to_string())
.unwrap_or_else(|| Factory::get_composer_file());
- let io = self.inner.get_io();
+ let io = self.get_io();
if !std::path::Path::new(&file).exists() {
io.write_error(&format!("<error>{} not found.</error>", file));
@@ -147,7 +134,7 @@ impl ValidateCommand {
validator.validate(&file, check_all, check_version)?;
let mut lock_errors: Vec<String> = vec![];
- let composer = self.inner.create_composer_instance(input, io, vec![])?;
+ let composer = self.create_composer_instance(input, io, vec![])?;
let check_lock = (check_lock
&& composer.get_config().get("lock").as_bool().unwrap_or(true))
|| input.get_option("check-lock").as_bool().unwrap_or(false);
@@ -230,14 +217,14 @@ impl ValidateCommand {
let command_event = CommandEvent::new(
PluginEvents::COMMAND.to_string(),
"validate".to_string(),
- Box::new(input),
- Box::new(output),
+ input,
+ output,
vec![],
vec![],
);
let event_code = composer
.get_event_dispatcher()
- .dispatch(command_event.get_name(), &command_event);
+ .dispatch(Some(command_event.get_name()), None)?;
Ok(exit_code.max(event_code))
}
@@ -336,30 +323,12 @@ impl ValidateCommand {
}
}
-impl BaseCommand for ValidateCommand {
- fn inner(&self) -> &CommandBase {
- &self.inner
+impl HasBaseCommandData for ValidateCommand {
+ fn base_command_data(&self) -> &BaseCommandData {
+ &self.base_command_data
}
- fn inner_mut(&mut self) -> &mut CommandBase {
- &mut self.inner
- }
-
- fn composer(&self) -> Option<&Composer> {
- self.composer.as_ref()
- }
-
- fn composer_mut(&mut self) -> &mut Option<Composer> {
- &mut self.composer
- }
-
- fn io(&self) -> Option<&dyn IOInterface> {
- self.io.as_deref()
- }
-
- fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> {
- &mut self.io
+ fn base_command_data_mut(&mut self) -> &mut BaseCommandData {
+ &mut self.base_command_data
}
}
-
-impl Command for ValidateCommand {}