aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command/base_command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/command/base_command.rs')
-rw-r--r--crates/shirabe/src/command/base_command.rs419
1 files changed, 274 insertions, 145 deletions
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.