diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-06 18:04:45 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-06 18:04:45 +0900 |
| commit | d5b313f388d424e60ae33ae58c1a65a65b24b8f1 (patch) | |
| tree | 849ada9b7319b8eb85aab28e02f614023fea2105 | |
| parent | 71b432db3f115d17be498f36cd3efd40b4650a0b (diff) | |
| download | php-shirabe-d5b313f388d424e60ae33ae58c1a65a65b24b8f1.tar.gz php-shirabe-d5b313f388d424e60ae33ae58c1a65a65b24b8f1.tar.zst php-shirabe-d5b313f388d424e60ae33ae58c1a65a65b24b8f1.zip | |
refactor(command): share Input/OutputInterface via Rc<RefCell>
Convert InputInterface and OutputInterface parameters from &dyn/&mut dyn
references to Rc<RefCell<dyn ...>> shared ownership across the command,
console, and IO layers, matching the Phase C shared-ownership approach
already used for IOInterface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
53 files changed, 1562 insertions, 631 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/component/console/application.rs b/crates/shirabe-external-packages/src/symfony/component/console/application.rs index 65abce4..becf1a5 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/application.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/application.rs @@ -12,8 +12,8 @@ impl Application { pub fn run( &mut self, - _input: Option<&mut dyn InputInterface>, - _output: Option<&mut dyn OutputInterface>, + _input: Option<std::rc::Rc<std::cell::RefCell<dyn InputInterface>>>, + _output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>, ) -> anyhow::Result<i64> { todo!() } @@ -90,7 +90,11 @@ impl Application { todo!() } - pub fn render_throwable(&self, _exception: &anyhow::Error, _output: &mut dyn OutputInterface) { + pub fn render_throwable( + &self, + _exception: &anyhow::Error, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { todo!() } diff --git a/crates/shirabe-external-packages/src/symfony/component/console/helper/progress_bar.rs b/crates/shirabe-external-packages/src/symfony/component/console/helper/progress_bar.rs index 1b896f9..77bc4fd 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/helper/progress_bar.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/helper/progress_bar.rs @@ -4,7 +4,7 @@ use crate::symfony::component::console::output::OutputInterface; pub struct ProgressBar; impl ProgressBar { - pub fn new(_output: &dyn OutputInterface, _max: i64) -> Self { + pub fn new(_output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, _max: i64) -> Self { todo!() } diff --git a/crates/shirabe-external-packages/src/symfony/component/console/helper/question_helper.rs b/crates/shirabe-external-packages/src/symfony/component/console/helper/question_helper.rs index 5c723c2..5d452c7 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/helper/question_helper.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/helper/question_helper.rs @@ -9,8 +9,8 @@ pub struct QuestionHelper; impl QuestionHelper { pub fn ask( &self, - _input: &mut dyn InputInterface, - _output: &mut dyn OutputInterface, + _input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, _question: &Question, ) -> Option<PhpMixed> { todo!() diff --git a/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs b/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs index ca8b292..f580965 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/helper/table.rs @@ -5,7 +5,7 @@ use shirabe_php_shim::PhpMixed; pub struct Table; impl Table { - pub fn new(_output: &dyn OutputInterface) -> Self { + pub fn new(_output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) -> Self { todo!() } diff --git a/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs b/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs index f38d524..a556198 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/input/input_interface.rs @@ -1,6 +1,6 @@ use shirabe_php_shim::PhpMixed; -pub trait InputInterface { +pub trait InputInterface: std::fmt::Debug { fn get_first_argument(&self) -> Option<String>; fn has_parameter_option(&self, values: &[&str], only_params: bool) -> bool; fn get_parameter_option( diff --git a/crates/shirabe-external-packages/src/symfony/component/console/output/console_output.rs b/crates/shirabe-external-packages/src/symfony/component/console/output/console_output.rs index 332cbd1..00b9324 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/output/console_output.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/output/console_output.rs @@ -16,11 +16,11 @@ impl ConsoleOutput { } impl ConsoleOutputInterface for ConsoleOutput { - fn get_error_output(&self) -> &dyn OutputInterface { + fn get_error_output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> { todo!() } - fn set_error_output(&mut self, _error: Box<dyn OutputInterface>) { + fn set_error_output(&mut self, _error: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) { todo!() } } diff --git a/crates/shirabe-external-packages/src/symfony/component/console/output/console_output_interface.rs b/crates/shirabe-external-packages/src/symfony/component/console/output/console_output_interface.rs index 8e68396..83ec1de 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/output/console_output_interface.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/output/console_output_interface.rs @@ -1,6 +1,6 @@ use crate::symfony::component::console::output::OutputInterface; pub trait ConsoleOutputInterface: OutputInterface { - fn get_error_output(&self) -> &dyn OutputInterface; - fn set_error_output(&mut self, error: Box<dyn OutputInterface>); + fn get_error_output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>; + fn set_error_output(&mut self, error: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>); } diff --git a/crates/shirabe-external-packages/src/symfony/component/console/output/output_interface.rs b/crates/shirabe-external-packages/src/symfony/component/console/output/output_interface.rs index 11fb82e..9e58a3e 100644 --- a/crates/shirabe-external-packages/src/symfony/component/console/output/output_interface.rs +++ b/crates/shirabe-external-packages/src/symfony/component/console/output/output_interface.rs @@ -1,7 +1,7 @@ use crate::symfony::component::console::formatter::OutputFormatter; use crate::symfony::component::console::output::ConsoleOutputInterface; -pub trait OutputInterface { +pub trait OutputInterface: std::fmt::Debug { // PHP class semantics: OutputInterface methods take &self with interior mutability, // because output objects are shared by reference across the PHP code. fn write(&self, messages: &str, newline: bool, r#type: i64); diff --git a/crates/shirabe-external-packages/src/symfony/console/helper/table.rs b/crates/shirabe-external-packages/src/symfony/console/helper/table.rs index df4978c..30c008c 100644 --- a/crates/shirabe-external-packages/src/symfony/console/helper/table.rs +++ b/crates/shirabe-external-packages/src/symfony/console/helper/table.rs @@ -5,7 +5,7 @@ use shirabe_php_shim::PhpMixed; pub struct Table; impl Table { - pub fn new(_output: &dyn OutputInterface) -> Self { + pub fn new(_output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) -> Self { todo!() } diff --git a/crates/shirabe-external-packages/src/symfony/console/style/symfony_style.rs b/crates/shirabe-external-packages/src/symfony/console/style/symfony_style.rs index 92e9e67..b5dfded 100644 --- a/crates/shirabe-external-packages/src/symfony/console/style/symfony_style.rs +++ b/crates/shirabe-external-packages/src/symfony/console/style/symfony_style.rs @@ -6,7 +6,10 @@ use shirabe_php_shim::PhpMixed; pub struct SymfonyStyle; impl SymfonyStyle { - pub fn new(_input: &dyn InputInterface, _output: &dyn OutputInterface) -> Self { + pub fn new( + _input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) -> Self { todo!() } diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs index 3e74d6e..e26e41e 100644 --- a/crates/shirabe/src/command/about_command.rs +++ b/crates/shirabe/src/command/about_command.rs @@ -22,7 +22,11 @@ impl AboutCommand { .set_help("<info>php composer.phar about</info>"); } - pub fn execute(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> i64 { + pub fn execute( + &mut self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) -> i64 { let composer_version = composer::get_version(); let _ = (input, output); diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index 5e60ce1..bf3a0c1 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -61,15 +61,16 @@ impl ArchiveCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.try_composer(None, None); let config = if let Some(ref composer) = composer { let config = composer.borrow_partial().get_config(); // TODO(plugin): dispatch CommandEvent - let command_event = CommandEvent::new(PluginEvents::COMMAND, "archive", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "archive", input.clone(), output); let event_dispatcher = composer.borrow_partial().get_event_dispatcher(); event_dispatcher .borrow_mut() @@ -86,6 +87,7 @@ impl ArchiveCommand { }; let format = input + .borrow() .get_option("format") .as_string() .map(|s| s.to_string()) @@ -99,6 +101,7 @@ impl ArchiveCommand { }); let dir = input + .borrow() .get_option("dir") .as_string() .map(|s| s.to_string()) @@ -117,17 +120,24 @@ impl ArchiveCommand { io_box.clone(), &config, input + .borrow() .get_argument("package") .as_string() .map(|s| s.to_string()), input + .borrow() .get_argument("version") .as_string() .map(|s| s.to_string()), &format, &dir, - input.get_option("file").as_string().map(|s| s.to_string()), input + .borrow() + .get_option("file") + .as_string() + .map(|s| s.to_string()), + input + .borrow() .get_option("ignore-filters") .as_bool() .unwrap_or(false), diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs index a28c344..f739029 100644 --- a/crates/shirabe/src/command/audit_command.rs +++ b/crates/shirabe/src/command/audit_command.rs @@ -48,11 +48,11 @@ impl AuditCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; - let packages = self.get_packages(&composer, input)?; + let packages = self.get_packages(&composer, input.clone())?; if packages.is_empty() { self.get_io().write_error("No packages - skipping audit."); @@ -84,6 +84,7 @@ impl AuditCommand { )?; let abandoned = input + .borrow() .get_option("abandoned") .as_string() .map(|s| s.to_string()); @@ -113,10 +114,11 @@ impl AuditCommand { let abandoned = abandoned.unwrap_or_else(|| audit_config.audit_abandoned.clone()); let ignore_severities = array_merge( - array_fill_keys(input.get_option("ignore-severity"), PhpMixed::Null), + array_fill_keys(input.borrow().get_option("ignore-severity"), PhpMixed::Null), PhpMixed::from(audit_config.ignore_severity_for_audit.clone()), ); let ignore_unreachable = input + .borrow() .get_option("ignore-unreachable") .as_bool() .unwrap_or(false) @@ -144,10 +146,15 @@ impl AuditCommand { fn get_packages( &self, composer: &PartialComposerHandle, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<Vec<crate::package::PackageInterfaceHandle>> { let mut composer = crate::command::composer_full_mut(composer); - if input.get_option("locked").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("locked") + .as_bool() + .unwrap_or(false) + { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); if !locker.is_locked() { @@ -156,8 +163,13 @@ impl AuditCommand { code: 0, }.into()); } - let locked_repo = locker - .get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))?; + let locked_repo = locker.get_locked_repository( + !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false), + )?; return locked_repo.borrow_mut().get_canonical_packages(); } diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 501915b..b6664e7 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -140,8 +140,8 @@ pub trait BaseCommand { fn run( &mut self, - _input: &mut dyn InputInterface, - _output: &mut dyn OutputInterface, + _input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { todo!() } @@ -196,14 +196,14 @@ pub trait BaseCommand { /// @inheritDoc fn initialize( &mut self, - input: &mut dyn InputInterface, - output: &mut dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<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, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<IndexMap<String, PhpMixed>>, disable_plugins: bool, @@ -214,13 +214,13 @@ pub trait BaseCommand { fn get_preferred_install_options( &self, config: &Config, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, keep_vcs_requires_prefer_source: bool, ) -> Result<(bool, bool)>; fn get_platform_requirement_filter( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<std::rc::Rc<dyn PlatformRequirementFilterInterface>>; /// @param array<string> $requirements @@ -237,20 +237,28 @@ pub trait BaseCommand { ) -> Result<Vec<IndexMap<String, String>>>; /// @param array<TableSeparator|mixed[]> $table - fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface); + fn render_table( + &self, + table: Vec<PhpMixed>, + output: std::rc::Rc<std::cell::RefCell<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>; + fn get_audit_format( + &self, + input: std::rc::Rc<std::cell::RefCell<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: &mut Config, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<AuditConfig>; } @@ -355,12 +363,16 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn initialize( &mut self, - input: &mut dyn InputInterface, - output: &mut dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<()> { // initialize a plugin-enabled Composer instance, either local or global - let mut disable_plugins = input.has_parameter_option(&["--no-plugins"], false); - let mut disable_scripts = input.has_parameter_option(&["--no-scripts"], false); + let mut disable_plugins = input + .borrow() + .has_parameter_option(&["--no-plugins"], false); + let mut disable_scripts = input + .borrow() + .has_parameter_option(&["--no-scripts"], false); // 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 @@ -392,8 +404,12 @@ impl<C: HasBaseCommandData> BaseCommand for C { let _ = pre_command_run_event.get_name(); } - if input.has_parameter_option(&["--no-ansi"], false) && input.has_option("no-progress") { - input.set_option("no-progress", PhpMixed::Bool(true)); + if input.borrow().has_parameter_option(&["--no-ansi"], false) + && input.borrow().has_option("no-progress") + { + input + .borrow_mut() + .set_option("no-progress", PhpMixed::Bool(true)); } let env_options: IndexMap<&str, Vec<&str>> = [ @@ -416,44 +432,55 @@ impl<C: HasBaseCommandData> BaseCommand for C { .collect(); for (env_name, option_names) in &env_options { for option_name in option_names { - if true == input.has_option(option_name) { - if false == input.get_option(option_name).as_bool().unwrap_or(false) + if true == input.borrow().has_option(option_name) { + if false + == input + .borrow() + .get_option(option_name) + .as_bool() + .unwrap_or(false) && Platform::get_env(env_name).map_or(false, |s| !s.is_empty() && s != "0") { - input.set_option(option_name, PhpMixed::Bool(true)); + input + .borrow_mut() + .set_option(option_name, PhpMixed::Bool(true)); } } } } - if true == input.has_option("ignore-platform-reqs") { + if true == input.borrow().has_option("ignore-platform-reqs") { if !input + .borrow() .get_option("ignore-platform-reqs") .as_bool() .unwrap_or(false) && Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQS") .map_or(false, |s| !s.is_empty() && s != "0") { - input.set_option("ignore-platform-reqs", PhpMixed::Bool(true)); + input + .borrow_mut() + .set_option("ignore-platform-reqs", PhpMixed::Bool(true)); io.write_error("<warning>COMPOSER_IGNORE_PLATFORM_REQS is set. You may experience unexpected errors.</warning>"); } } - if true == input.has_option("ignore-platform-req") - && (!input.has_option("ignore-platform-reqs") + if true == input.borrow().has_option("ignore-platform-req") + && (!input.borrow().has_option("ignore-platform-reqs") || !input + .borrow() .get_option("ignore-platform-reqs") .as_bool() .unwrap_or(false)) { let ignore_platform_req_env = Platform::get_env("COMPOSER_IGNORE_PLATFORM_REQ"); let ignore_str = ignore_platform_req_env.clone().unwrap_or_default(); - if 0 == count(&input.get_option("ignore-platform-req")) + if 0 == count(&input.borrow().get_option("ignore-platform-req")) && ignore_platform_req_env.is_some() && "" != ignore_str { - input.set_option( + input.borrow_mut().set_option( "ignore-platform-req", PhpMixed::List( explode(",", &ignore_str) @@ -476,16 +503,20 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn create_composer_instance( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<IndexMap<String, PhpMixed>>, disable_plugins: bool, disable_scripts: Option<bool>, ) -> Result<PartialComposerHandle> { - let disable_plugins = - disable_plugins || input.has_parameter_option(&["--no-plugins"], false); + let disable_plugins = disable_plugins + || input + .borrow() + .has_parameter_option(&["--no-plugins"], false); let disable_scripts = disable_scripts.unwrap_or(false) - || input.has_parameter_option(&["--no-scripts"], false); + || input + .borrow() + .has_parameter_option(&["--no-scripts"], false); // TODO(phase-b): requires inner Symfony Application access for disable_plugins_by_default / disable_scripts_by_default let disable_plugins_kind = if disable_plugins { @@ -501,7 +532,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn get_preferred_install_options( &self, config: &Config, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, keep_vcs_requires_prefer_source: bool, ) -> Result<(bool, bool)> { let mut prefer_source = false; @@ -519,12 +550,20 @@ impl<C: HasBaseCommandData> BaseCommand for C { } } - if !input.has_option("prefer-dist") || !input.has_option("prefer-source") { + if !input.borrow().has_option("prefer-dist") || !input.borrow().has_option("prefer-source") + { return Ok((prefer_source, prefer_dist)); } - if input.has_option("prefer-install") && is_string(&input.get_option("prefer-install")) { - if input.get_option("prefer-source").as_bool().unwrap_or(false) { + if input.borrow().has_option("prefer-install") + && is_string(&input.borrow().get_option("prefer-install")) + { + if input + .borrow() + .get_option("prefer-source") + .as_bool() + .unwrap_or(false) + { return Err(InvalidArgumentException { message: "--prefer-source can not be used together with --prefer-install" .to_string(), @@ -532,7 +571,12 @@ impl<C: HasBaseCommandData> BaseCommand for C { } .into()); } - if input.get_option("prefer-dist").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("prefer-dist") + .as_bool() + .unwrap_or(false) + { return Err(InvalidArgumentException { message: "--prefer-dist can not be used together with --prefer-install" .to_string(), @@ -540,7 +584,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { } .into()); } - let prefer_install = input.get_option("prefer-install"); + let prefer_install = input.borrow().get_option("prefer-install"); match prefer_install.as_string().unwrap_or("") { "dist" => { // TODO(phase-b): InputInterface set_option needs &mut self @@ -566,17 +610,41 @@ impl<C: HasBaseCommandData> BaseCommand for C { } } - if input.get_option("prefer-source").as_bool().unwrap_or(false) - || input.get_option("prefer-dist").as_bool().unwrap_or(false) + if input + .borrow() + .get_option("prefer-source") + .as_bool() + .unwrap_or(false) + || input + .borrow() + .get_option("prefer-dist") + .as_bool() + .unwrap_or(false) || (keep_vcs_requires_prefer_source - && input.has_option("keep-vcs") - && input.get_option("keep-vcs").as_bool().unwrap_or(false)) + && input.borrow().has_option("keep-vcs") + && input + .borrow() + .get_option("keep-vcs") + .as_bool() + .unwrap_or(false)) { - prefer_source = input.get_option("prefer-source").as_bool().unwrap_or(false) + prefer_source = input + .borrow() + .get_option("prefer-source") + .as_bool() + .unwrap_or(false) || (keep_vcs_requires_prefer_source - && input.has_option("keep-vcs") - && input.get_option("keep-vcs").as_bool().unwrap_or(false)); - prefer_dist = input.get_option("prefer-dist").as_bool().unwrap_or(false); + && input.borrow().has_option("keep-vcs") + && input + .borrow() + .get_option("keep-vcs") + .as_bool() + .unwrap_or(false)); + prefer_dist = input + .borrow() + .get_option("prefer-dist") + .as_bool() + .unwrap_or(false); } Ok((prefer_source, prefer_dist)) @@ -584,9 +652,11 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn get_platform_requirement_filter( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<std::rc::Rc<dyn PlatformRequirementFilterInterface>> { - if !input.has_option("ignore-platform-reqs") || !input.has_option("ignore-platform-req") { + if !input.borrow().has_option("ignore-platform-reqs") + || !input.borrow().has_option("ignore-platform-req") + { return Err(LogicException { message: "Calling getPlatformRequirementFilter from a command which does not define the --ignore-platform-req[s] flags is not permitted." @@ -598,6 +668,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { if true == input + .borrow() .get_option("ignore-platform-reqs") .as_bool() .unwrap_or(false) @@ -605,7 +676,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { return Ok(PlatformRequirementFilterFactory::ignore_all()); } - let ignores = input.get_option("ignore-platform-req"); + let ignores = input.borrow().get_option("ignore-platform-req"); if count(&ignores) > 0 { return Ok(PlatformRequirementFilterFactory::from_bool_or_list( ignores, @@ -648,7 +719,11 @@ impl<C: HasBaseCommandData> BaseCommand for C { parser.parse_name_version_pairs(requirements) } - fn render_table(&self, table: Vec<PhpMixed>, output: &dyn OutputInterface) { + fn render_table( + &self, + table: Vec<PhpMixed>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { let mut renderer = Table::new(output); renderer.set_style("compact"); renderer.set_rows(table).render(); @@ -668,8 +743,12 @@ impl<C: HasBaseCommandData> BaseCommand for C { width } - fn get_audit_format(&self, input: &dyn InputInterface, opt_name: &str) -> Result<String> { - if !input.has_option(opt_name) { + fn get_audit_format( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + opt_name: &str, + ) -> Result<String> { + if !input.borrow().has_option(opt_name) { return Err(LogicException { message: format!( "This should not be called on a Command which has no {} option defined.", @@ -680,7 +759,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { .into()); } - let val = input.get_option(opt_name); + let val = input.borrow().get_option(opt_name); let formats: Vec<Box<PhpMixed>> = Auditor::FORMATS .iter() .map(|s| Box::new(PhpMixed::String(s.to_string()))) @@ -703,17 +782,25 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn create_audit_config( &self, config: &mut Config, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<AuditConfig> { // Handle both --audit and --no-audit flags - let audit = if input.has_option("audit") { - input.get_option("audit").as_bool().unwrap_or(false) + let audit = if input.borrow().has_option("audit") { + input + .borrow() + .get_option("audit") + .as_bool() + .unwrap_or(false) } else { - !(input.has_option("no-audit") - && input.get_option("no-audit").as_bool().unwrap_or(false)) + !(input.borrow().has_option("no-audit") + && input + .borrow() + .get_option("no-audit") + .as_bool() + .unwrap_or(false)) }; - let audit_format = if input.has_option("audit-format") { - self.get_audit_format(input, "audit-format")? + let audit_format = if input.borrow().has_option("audit-format") { + self.get_audit_format(input.clone(), "audit-format")? } else { Auditor::FORMAT_SUMMARY.to_string() }; @@ -722,8 +809,9 @@ impl<C: HasBaseCommandData> BaseCommand for C { if Platform::get_env("COMPOSER_NO_SECURITY_BLOCKING") .map_or(false, |s| !s.is_empty() && s != "0") - || (input.has_option("no-security-blocking") + || (input.borrow().has_option("no-security-blocking") && input + .borrow() .get_option("no-security-blocking") .as_bool() .unwrap_or(false)) diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs index 1b7294e..3aed839 100644 --- a/crates/shirabe/src/command/base_config_command.rs +++ b/crates/shirabe/src/command/base_config_command.rs @@ -24,14 +24,18 @@ pub trait BaseConfigCommand: BaseCommand { fn initialize( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<()> { // TODO(phase-b): BaseCommand::initialize chained via Self::initialize would recurse; // omitted until trait disambiguation is sorted. - if input.get_option("global").as_bool().unwrap_or(false) - && !input.get_option("file").is_null() + if input + .borrow() + .get_option("global") + .as_bool() + .unwrap_or(false) + && !input.borrow().get_option("file").is_null() { return Err(anyhow::anyhow!("--file and --global can not be combined")); } @@ -44,12 +48,17 @@ pub trait BaseConfigCommand: BaseCommand { let config_rc = self.config().unwrap().clone(); // When using --global flag, set baseDir to home directory for correct absolute path resolution - if input.get_option("global").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("global") + .as_bool() + .unwrap_or(false) + { let home = config_rc.borrow_mut().get("home").to_string(); config_rc.borrow_mut().set_base_dir(Some(home)); } - let config_file = self.get_composer_config_file(input, &*config_rc.borrow()); + let config_file = self.get_composer_config_file(input.clone(), &*config_rc.borrow()); // Create global composer.json if invoked using `composer global [config-cmd]` if (config_file == "composer.json" || config_file == "./composer.json") @@ -69,7 +78,11 @@ pub trait BaseConfigCommand: BaseCommand { self.set_config_source(None); // Initialize the global file if it's not there, ignoring any warnings or notices - if input.get_option("global").as_bool().unwrap_or(false) + if input + .borrow() + .get_option("global") + .as_bool() + .unwrap_or(false) && !self.config_file().as_ref().unwrap().exists() { let path = self.config_file().as_ref().unwrap().get_path().to_string(); @@ -102,11 +115,21 @@ pub trait BaseConfigCommand: BaseCommand { } /// Get the local composer.json, global config.json, or the file passed by the user - fn get_composer_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { - if input.get_option("global").as_bool().unwrap_or(false) { + fn get_composer_config_file( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + config: &Config, + ) -> String { + if input + .borrow() + .get_option("global") + .as_bool() + .unwrap_or(false) + { format!("{}/config.json", config.get("home")) } else { input + .borrow() .get_option("file") .as_string() .map(|s| s.to_string()) @@ -116,8 +139,17 @@ pub trait BaseConfigCommand: BaseCommand { /// Get the local auth.json or global auth.json, or if the user passed in a file to use, /// the corresponding auth.json - fn get_auth_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { - if input.get_option("global").as_bool().unwrap_or(false) { + fn get_auth_config_file( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + config: &Config, + ) -> String { + if input + .borrow() + .get_option("global") + .as_bool() + .unwrap_or(false) + { format!("{}/auth.json", config.get("home")) } else { let composer_config = self.get_composer_config_file(input, config); diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 4f929c8..4a5b854 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -45,8 +45,8 @@ pub trait BaseDependencyCommand: BaseCommand { fn do_execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, inverted: bool, ) -> anyhow::Result<i64> { let composer = self.require_composer(None, None)?; @@ -60,7 +60,12 @@ pub trait BaseDependencyCommand: BaseCommand { )), )]; - if input.get_option("locked").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("locked") + .as_bool() + .unwrap_or(false) + { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); @@ -91,7 +96,7 @@ pub trait BaseDependencyCommand: BaseCommand { if local_repo.get_packages()?.len() == 0 && (root_pkg.get_requires().len() > 0 || root_pkg.get_dev_requires().len() > 0) { - output.writeln( + output.borrow().writeln( "<warning>No dependencies installed. Try running composer install or update, or use --locked.</warning>", shirabe_external_packages::symfony::console::output::OUTPUT_NORMAL, ); @@ -118,12 +123,14 @@ pub trait BaseDependencyCommand: BaseCommand { let mut installed_repo = InstalledRepository::new(repos); let needle = input + .borrow() .get_argument(Self::ARGUMENT_PACKAGE) .as_string() .unwrap_or_default() .to_string(); - let text_constraint: String = if input.has_argument(Self::ARGUMENT_CONSTRAINT) { + let text_constraint: String = if input.borrow().has_argument(Self::ARGUMENT_CONSTRAINT) { input + .borrow() .get_argument(Self::ARGUMENT_CONSTRAINT) .as_string() .unwrap_or("*") @@ -242,11 +249,13 @@ pub trait BaseDependencyCommand: BaseCommand { }; let render_tree = input + .borrow() .get_option(Self::OPTION_TREE) .as_bool() .unwrap_or(false); let recursive = render_tree || input + .borrow() .get_option(Self::OPTION_RECURSIVE) .as_bool() .unwrap_or(false); @@ -294,7 +303,7 @@ pub trait BaseDependencyCommand: BaseCommand { } if inverted - && input.has_argument(Self::ARGUMENT_CONSTRAINT) + && input.borrow().has_argument(Self::ARGUMENT_CONSTRAINT) && !PlatformRepository::is_platform_package(&needle) { let mut composer_command = "update"; @@ -322,7 +331,11 @@ pub trait BaseDependencyCommand: BaseCommand { Ok(r#return) } - fn print_table(&self, output: &dyn OutputInterface, results: Vec<DependentsEntry>) { + fn print_table( + &self, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + results: Vec<DependentsEntry>, + ) { let mut table: Vec<Vec<String>> = vec![]; let mut doubles: IndexMap<String, bool> = IndexMap::new(); let mut results = results; @@ -383,7 +396,7 @@ pub trait BaseDependencyCommand: BaseCommand { self.render_table(table_as_mixed, output); } - fn init_styles(&mut self, output: &dyn OutputInterface) { + fn init_styles(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) { *self.colors_mut() = vec![ "green".to_string(), "yellow".to_string(), @@ -395,7 +408,7 @@ pub trait BaseDependencyCommand: BaseCommand { // TODO(phase-b): output.get_formatter() returns &OutputFormatter; set_style needs // &mut. Need interior mutability or `get_formatter_mut`. let _ = OutputFormatterStyle::new(Some(color), None, None); - let _ = output.get_formatter(); + let _ = output.borrow().get_formatter(); } } diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs index d477747..ac207bc 100644 --- a/crates/shirabe/src/command/bump_command.rs +++ b/crates/shirabe/src/command/bump_command.rs @@ -57,10 +57,11 @@ impl BumpCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let packages_filter: Vec<String> = input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -70,9 +71,21 @@ impl BumpCommand { }) .unwrap_or_default(); - let dev_only = input.get_option("dev-only").as_bool().unwrap_or(false); - let no_dev_only = input.get_option("no-dev-only").as_bool().unwrap_or(false); - let dry_run = input.get_option("dry-run").as_bool().unwrap_or(false); + let dev_only = input + .borrow() + .get_option("dev-only") + .as_bool() + .unwrap_or(false); + let no_dev_only = input + .borrow() + .get_option("no-dev-only") + .as_bool() + .unwrap_or(false); + let dry_run = input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false); let io = self.get_io().clone(); self.do_bump( io, diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index 05f7e9b..7b50d25 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -51,19 +51,24 @@ impl CheckPlatformReqsCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); let io = self.get_io(); - let no_dev = input.get_option("no-dev").as_bool().unwrap_or(false); + let no_dev = input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false); let mut requires: IndexMap<String, Vec<Link>> = IndexMap::new(); let mut remove_packages: Vec<String> = vec![]; let installed_repo_base: crate::repository::RepositoryInterfaceHandle = if input + .borrow() .get_option("lock") .as_bool() .unwrap_or(false) @@ -242,6 +247,7 @@ impl CheckPlatformReqsCommand { } let format = input + .borrow() .get_option("format") .as_string() .unwrap_or("text") @@ -251,7 +257,12 @@ impl CheckPlatformReqsCommand { Ok(exit_code) } - fn print_table(&mut self, output: &dyn OutputInterface, results: &[CheckResult], format: &str) { + fn print_table( + &mut self, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + results: &[CheckResult], + format: &str, + ) { let io = self.get_io(); if format == "json" { diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs index 4859a46..1ceca69 100644 --- a/crates/shirabe/src/command/clear_cache_command.rs +++ b/crates/shirabe/src/command/clear_cache_command.rs @@ -29,8 +29,8 @@ impl ClearCacheCommand { pub fn execute( &mut self, - _input: &dyn InputInterface, - _output: &dyn OutputInterface, + _input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { // TODO(phase-b): port full execute logic once Config sharing model is settled let _ = composer::VERSION; diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 40a12e1..38b8a8d 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -120,13 +120,13 @@ impl ConfigCommand { pub(crate) fn initialize( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<()> { - self.initialize(input, output)?; + self.initialize(input.clone(), output)?; let auth_config_file = - self.get_auth_config_file(input, &*self.config.as_ref().unwrap().borrow()); + self.get_auth_config_file(input.clone(), &*self.config.as_ref().unwrap().borrow()); self.auth_config_file = Some(JsonFile::new( auth_config_file, @@ -138,7 +138,7 @@ impl ConfigCommand { self.auth_config_source = None; // Initialize the global file if it's not there, ignoring any warnings or notices - if input.get_option("global").as_bool() == Some(true) + if input.borrow().get_option("global").as_bool() == Some(true) && !self.auth_config_file.as_ref().unwrap().exists() { touch(self.auth_config_file.as_ref().unwrap().get_path()); @@ -177,11 +177,11 @@ impl ConfigCommand { pub(crate) fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { // Open file in editor - if input.get_option("editor").as_bool() == Some(true) { + if input.borrow().get_option("editor").as_bool() == Some(true) { let mut editor = Platform::get_env("EDITOR"); if editor.is_none() || editor.as_deref() == Some("") { if Platform::is_windows() { @@ -201,7 +201,7 @@ impl ConfigCommand { editor = Some(escapeshellcmd(&editor.unwrap())); } - let file = if input.get_option("auth").as_bool() == Some(true) { + let file = if input.borrow().get_option("auth").as_bool() == Some(true) { self.auth_config_file .as_ref() .unwrap() @@ -227,7 +227,7 @@ impl ConfigCommand { return Ok(0); } - if input.get_option("global").as_bool() != Some(true) { + if input.borrow().get_option("global").as_bool() != Some(true) { let config_read = self.config_file.as_mut().unwrap().read()?; let config_map = match config_read { PhpMixed::Array(m) => m @@ -263,7 +263,7 @@ impl ConfigCommand { } // List the configuration of the file settings - if input.get_option("list").as_bool() == Some(true) { + if input.borrow().get_option("list").as_bool() == Some(true) { let all_map = self.config.as_ref().unwrap().borrow_mut().all(0)?; let raw_map = self.config.as_ref().unwrap().borrow().raw(); let to_mixed = |m: IndexMap<String, PhpMixed>| -> PhpMixed { @@ -274,20 +274,20 @@ impl ConfigCommand { to_mixed(raw_map), output, None, - input.get_option("source").as_bool() == Some(true), + input.borrow().get_option("source").as_bool() == Some(true), ); return Ok(0); } - let setting_key_arg = input.get_argument("setting-key"); + let setting_key_arg = input.borrow().get_argument("setting-key"); let setting_key = match setting_key_arg.as_string() { Some(s) => s.to_string(), None => return Ok(0), }; // If the user enters in a config variable, parse it and save to file - let setting_values_raw = input.get_argument("setting-value"); + let setting_values_raw = input.borrow().get_argument("setting-value"); let setting_values: Vec<String> = setting_values_raw .as_list() .map(|l| { @@ -296,7 +296,8 @@ impl ConfigCommand { .collect() }) .unwrap_or_default(); - if !setting_values.is_empty() && input.get_option("unset").as_bool() == Some(true) { + if !setting_values.is_empty() && input.borrow().get_option("unset").as_bool() == Some(true) + { return Err(RuntimeException { message: "You can not combine a setting value with --unset".to_string(), code: 0, @@ -305,7 +306,7 @@ impl ConfigCommand { } // show the value if no value is provided - if setting_values.is_empty() && input.get_option("unset").as_bool() != Some(true) { + if setting_values.is_empty() && input.borrow().get_option("unset").as_bool() != Some(true) { let properties: Vec<&'static str> = Self::CONFIGURABLE_PACKAGE_PROPERTIES.to_vec(); let mut properties_defaults: IndexMap<String, PhpMixed> = IndexMap::new(); properties_defaults.insert("type".to_string(), PhpMixed::String("library".to_string())); @@ -411,7 +412,7 @@ impl ConfigCommand { { value = self.config.as_ref().unwrap().borrow_mut().get_with_flags( &setting_key, - if input.get_option("absolute").as_bool() == Some(true) { + if input.borrow().get_option("absolute").as_bool() == Some(true) { 0 } else { Config::RELATIVE_PATHS @@ -488,7 +489,7 @@ impl ConfigCommand { }; let mut source_of_config_value = String::new(); - if input.get_option("source").as_bool() == Some(true) { + if input.borrow().get_option("source").as_bool() == Some(true) { source_of_config_value = format!(" ({})", source); } @@ -526,7 +527,7 @@ impl ConfigCommand { let multi_config_values = build_multi_config_values(); // allow unsetting audit config entirely - if input.get_option("unset").as_bool() == Some(true) && setting_key == "audit" { + if input.borrow().get_option("unset").as_bool() == Some(true) && setting_key == "audit" { self.config_source .as_mut() .unwrap() @@ -535,7 +536,7 @@ impl ConfigCommand { return Ok(0); } - if input.get_option("unset").as_bool() == Some(true) + if input.borrow().get_option("unset").as_bool() == Some(true) && (unique_config_values.contains_key(&setting_key) || multi_config_values.contains_key(&setting_key)) { @@ -580,7 +581,7 @@ impl ConfigCommand { ) .unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -621,7 +622,7 @@ impl ConfigCommand { ) .unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -652,7 +653,7 @@ impl ConfigCommand { let unique_props = build_unique_props(); let multi_props = build_multi_props(); - if input.get_option("global").as_bool() == Some(true) + if input.borrow().get_option("global").as_bool() == Some(true) && (unique_props.contains_key(&setting_key) || multi_props.contains_key(&setting_key) || strpos(&setting_key, "extra.") == Some(0)) @@ -663,7 +664,7 @@ impl ConfigCommand { } .into()); } - if input.get_option("unset").as_bool() == Some(true) + if input.borrow().get_option("unset").as_bool() == Some(true) && (unique_props.contains_key(&setting_key) || multi_props.contains_key(&setting_key)) { self.config_source @@ -693,7 +694,7 @@ impl ConfigCommand { ) .unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -715,7 +716,7 @@ impl ConfigCommand { self.config_source.as_mut().unwrap().add_repository( &matches[1], PhpMixed::Array(repo), - input.get_option("append").as_bool() == Some(true), + input.borrow().get_option("append").as_bool() == Some(true), ); return Ok(0); @@ -731,7 +732,7 @@ impl ConfigCommand { self.config_source.as_mut().unwrap().add_repository( &matches[1], PhpMixed::Bool(false), - input.get_option("append").as_bool() == Some(true), + input.borrow().get_option("append").as_bool() == Some(true), ); return Ok(0); @@ -741,7 +742,7 @@ impl ConfigCommand { self.config_source.as_mut().unwrap().add_repository( &matches[1], value, - input.get_option("append").as_bool() == Some(true), + input.borrow().get_option("append").as_bool() == Some(true), ); return Ok(0); @@ -758,7 +759,7 @@ impl ConfigCommand { // handle extra let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::is_match3("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -768,9 +769,9 @@ impl ConfigCommand { } let mut value = PhpMixed::String(values[0].clone()); - if input.get_option("json").as_bool() == Some(true) { + if input.borrow().get_option("json").as_bool() == Some(true) { value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; - if input.get_option("merge").as_bool() == Some(true) { + if input.borrow().get_option("merge").as_bool() == Some(true) { let current_value_outer = self.config_file.as_mut().unwrap().read()?; let bits = explode(".", &setting_key); let mut current_value: PhpMixed = current_value_outer; @@ -816,7 +817,7 @@ impl ConfigCommand { // handle suggest let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::is_match3("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -838,7 +839,7 @@ impl ConfigCommand { setting_key.as_str().into(), &vec!["suggest".to_string(), "extra".to_string()].into(), true, - ) && input.get_option("unset").as_bool() == Some(true) + ) && input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() @@ -852,7 +853,7 @@ impl ConfigCommand { let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::is_match3("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -875,7 +876,7 @@ impl ConfigCommand { } // handle unsetting platform - if setting_key == "platform" && input.get_option("unset").as_bool() == Some(true) { + if setting_key == "platform" && input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -894,7 +895,7 @@ impl ConfigCommand { .into(), true, ) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -909,7 +910,7 @@ impl ConfigCommand { .map(|s| Box::new(PhpMixed::String(s.clone()))) .collect(), ); - if input.get_option("json").as_bool() == Some(true) { + if input.borrow().get_option("json").as_bool() == Some(true) { value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; if !is_array(&value) { return Err(RuntimeException { @@ -920,7 +921,7 @@ impl ConfigCommand { } } - if input.get_option("merge").as_bool() == Some(true) { + if input.borrow().get_option("merge").as_bool() == Some(true) { let current_config = self.config_file.as_mut().unwrap().read()?; let key_suffix = str_replace("audit.", "", &setting_key); let current_value = current_config @@ -973,7 +974,7 @@ impl ConfigCommand { // handle auth let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::is_match3("/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|custom-headers|bearer|forgejo-token)\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.auth_config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2])); self.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2])); @@ -1079,7 +1080,7 @@ impl ConfigCommand { // handle script let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::is_match3("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -1107,7 +1108,7 @@ impl ConfigCommand { } // handle unsetting other top level properties - if input.get_option("unset").as_bool() == Some(true) { + if input.borrow().get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() .unwrap() @@ -1243,7 +1244,7 @@ impl ConfigCommand { &mut self, contents: PhpMixed, raw_contents: PhpMixed, - output: &dyn OutputInterface, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, k: Option<String>, show_source: bool, ) { @@ -1278,7 +1279,13 @@ impl ConfigCommand { &Preg::replace("{^config\\.}", "", &format!("{}.", key)).unwrap_or_default(), ); k = Some(new_k); - self.list_configuration(value_inner, raw_val, output, k.clone(), show_source); + self.list_configuration( + value_inner, + raw_val, + output.clone(), + k.clone(), + show_source, + ); k = orig_k.clone(); continue; diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index b13a3d0..d05f27d 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -109,30 +109,35 @@ impl CreateProjectCommand { fn execute( &mut self, - input: &mut dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)); // TODO(phase-b): get_io returns &mut Self-borrow; clone_box for an owned Box to dodge. let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io().clone(); let (prefer_source, prefer_dist) = - self.get_preferred_install_options(&config.borrow(), input, true)?; + self.get_preferred_install_options(&config.borrow(), input.clone(), true)?; - if input.get_option("dev").as_bool().unwrap_or(false) { + if input.borrow().get_option("dev").as_bool().unwrap_or(false) { io.write_error("<warning>You are using the deprecated option \"dev\". Dev packages are installed by default now.</warning>"); } if input + .borrow() .get_option("no-custom-installers") .as_bool() .unwrap_or(false) { io.write_error("<warning>You are using the deprecated option \"no-custom-installers\". Use \"no-plugins\" instead.</warning>"); - input.set_option("no-plugins", PhpMixed::Bool(true)); + input + .borrow_mut() + .set_option("no-plugins", PhpMixed::Bool(true)); } - if input.is_interactive() && input.get_option("ask").as_bool().unwrap_or(false) { - let package = input.get_argument("package"); + if input.borrow().is_interactive() + && input.borrow().get_option("ask").as_bool().unwrap_or(false) + { + let package = input.borrow().get_argument("package"); if package.is_null() { return Err(RuntimeException { message: "Not enough arguments (missing: \"package\").".to_string(), @@ -146,11 +151,13 @@ impl CreateProjectCommand { "New project directory [<comment>{}</comment>]: ", array_pop(&mut parts).unwrap_or_default() ); - input.set_argument("directory", io.ask(prompt, PhpMixed::Null)); + input + .borrow_mut() + .set_argument("directory", io.ask(prompt, PhpMixed::Null)); } - let repository_opt = input.get_option("repository"); - let repository_url_opt = input.get_option("repository-url"); + let repository_opt = input.borrow().get_option("repository"); + let repository_url_opt = input.borrow().get_option("repository-url"); let repositories = if repository_opt .as_list() .map(|l| l.len() > 0) @@ -164,37 +171,63 @@ impl CreateProjectCommand { self.install_project( io, config, - input, + input.clone(), input + .borrow() .get_argument("package") .as_string() .map(|s| s.to_string()), input + .borrow() .get_argument("directory") .as_string() .map(|s| s.to_string()), input + .borrow() .get_argument("version") .as_string() .map(|s| s.to_string()), input + .borrow() .get_option("stability") .as_string() .map(|s| s.to_string()), prefer_source, prefer_dist, - !input.get_option("no-dev").as_bool().unwrap_or(false), + !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false), repositories, - input.get_option("no-plugins").as_bool().unwrap_or(false), - 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.get_platform_requirement_filter(input)?), + input + .borrow() + .get_option("no-plugins") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("no-scripts") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("no-install") + .as_bool() + .unwrap_or(false), + Some(self.get_platform_requirement_filter(input.clone())?), !input + .borrow() .get_option("no-secure-http") .as_bool() .unwrap_or(false), input + .borrow() .get_option("add-repository") .as_bool() .unwrap_or(false), @@ -209,7 +242,7 @@ impl CreateProjectCommand { &mut self, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, package_name: Option<String>, directory: Option<String>, package_version: Option<String>, @@ -254,7 +287,7 @@ impl CreateProjectCommand { let installed_from_vcs = if let Some(package_name) = package_name.as_ref() { self.install_root_package( - input, + input.clone(), io.clone(), &config, package_name, @@ -280,7 +313,7 @@ impl CreateProjectCommand { } let mut composer_handle = self.create_composer_instance( - input, + input.clone(), io.clone(), None, disable_plugins, @@ -339,7 +372,7 @@ impl CreateProjectCommand { } composer_handle = self.create_composer_instance( - input, + input.clone(), io.clone(), None, disable_plugins, @@ -371,7 +404,8 @@ impl CreateProjectCommand { // use the new config including the newly installed project let config = composer.get_config(); - let (ps, pd) = self.get_preferred_install_options(&*config.borrow(), input, false)?; + let (ps, pd) = + self.get_preferred_install_options(&*config.borrow(), input.clone(), false)?; prefer_source = ps; prefer_dist = pd; @@ -413,7 +447,9 @@ impl CreateProjectCommand { .unwrap_or(false), None, ) - .set_audit_config(self.create_audit_config(&mut *config.borrow_mut(), input)?); + .set_audit_config( + self.create_audit_config(&mut *config.borrow_mut(), input.clone())?, + ); if !composer.get_locker().borrow_mut().is_locked() { installer.set_update(true); @@ -444,9 +480,9 @@ impl CreateProjectCommand { } let mut has_vcs = installed_from_vcs; - let remove_vcs = !input.get_option("keep-vcs").as_bool().unwrap_or(false) + let remove_vcs = !input.borrow().get_option("keep-vcs").as_bool().unwrap_or(false) && installed_from_vcs - && (input.get_option("remove-vcs").as_bool().unwrap_or(false) + && (input.borrow().get_option("remove-vcs").as_bool().unwrap_or(false) || !io.is_interactive() || io.ask_confirmation( "<info>Do you want to remove the existing VCS (.git, .svn..) history?</info> [<comment>y,n</comment>]? ".to_string(), @@ -550,7 +586,7 @@ impl CreateProjectCommand { #[allow(clippy::too_many_arguments)] fn install_root_package( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, package_name: &str, @@ -957,7 +993,7 @@ impl CreateProjectCommand { // helpers reachable via $this in PHP, defined on BaseCommand here fn create_composer_instance( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<indexmap::IndexMap<String, PhpMixed>>, disable_plugins: bool, @@ -969,7 +1005,7 @@ impl CreateProjectCommand { fn create_audit_config( &self, config: &Config, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> Result<crate::advisory::AuditConfig> { self.create_audit_config(config, input) } diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs index 78b3dbb..a53aab7 100644 --- a/crates/shirabe/src/command/depends_command.rs +++ b/crates/shirabe/src/command/depends_command.rs @@ -67,8 +67,8 @@ impl DependsCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { self.do_execute(input, output, false) } diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index d9ab5e2..59df4f9 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -75,8 +75,8 @@ impl DiagnoseCommand { pub(crate) fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { let mut composer = self.try_composer(None, None); let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io().clone(); diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index 6f361da..0f229d0 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -44,15 +44,19 @@ impl DumpAutoloadCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); // TODO(plugin): dispatch CommandEvent - let command_event = - CommandEvent::new(PluginEvents::COMMAND, "dump-autoload", input, output); + let command_event = CommandEvent::new( + PluginEvents::COMMAND, + "dump-autoload", + input.clone(), + output, + ); composer .get_event_dispatcher() .borrow_mut() @@ -78,13 +82,18 @@ impl DumpAutoloadCommand { } } - let optimize = input.get_option("optimize").as_bool().unwrap_or(false) + let optimize = input + .borrow() + .get_option("optimize") + .as_bool() + .unwrap_or(false) || config .borrow_mut() .get("optimize-autoloader") .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -94,18 +103,25 @@ impl DumpAutoloadCommand { .as_bool() .unwrap_or(false); let apcu_prefix = input + .borrow() .get_option("apcu-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() - || input.get_option("apcu").as_bool().unwrap_or(false) + || input.borrow().get_option("apcu").as_bool().unwrap_or(false) || config .borrow_mut() .get("apcu-autoloader") .as_bool() .unwrap_or(false); - if input.get_option("strict-psr").as_bool().unwrap_or(false) && !optimize && !authoritative + if input + .borrow() + .get_option("strict-psr") + .as_bool() + .unwrap_or(false) + && !optimize + && !authoritative { return Err(InvalidArgumentException { message: "--strict-psr mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string(), @@ -114,6 +130,7 @@ impl DumpAutoloadCommand { .into()); } if input + .borrow() .get_option("strict-ambiguous") .as_bool() .unwrap_or(false) @@ -138,21 +155,36 @@ impl DumpAutoloadCommand { .write("<info>Generating autoload files</info>"); } - let platform_requirement_filter = self.get_platform_requirement_filter(input)?; - if input.get_option("dry-run").as_bool().unwrap_or(false) { + let platform_requirement_filter = self.get_platform_requirement_filter(input.clone())?; + if input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false) + { composer .get_autoload_generator() .borrow_mut() .set_dry_run(true); } - if input.get_option("no-dev").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false) + { composer .get_autoload_generator() .borrow_mut() .set_dev_mode(false); } - if input.get_option("dev").as_bool().unwrap_or(false) { - if input.get_option("no-dev").as_bool().unwrap_or(false) { + if input.borrow().get_option("dev").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false) + { return Err(InvalidArgumentException { message: "You can not use both --no-dev and --dev as they conflict with each other." @@ -199,13 +231,18 @@ impl DumpAutoloadCommand { } if missing_dependencies - || (input.get_option("strict-psr").as_bool().unwrap_or(false) + || (input + .borrow() + .get_option("strict-psr") + .as_bool() + .unwrap_or(false) && !class_map.get_psr_violations().is_empty()) { return Ok(1); } if input + .borrow() .get_option("strict-ambiguous") .as_bool() .unwrap_or(false) diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs index cfc0e89..b086937 100644 --- a/crates/shirabe/src/command/exec_command.rs +++ b/crates/shirabe/src/command/exec_command.rs @@ -41,16 +41,16 @@ impl ExecCommand { pub fn interact( &mut self, - input: &mut dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<()> { let binaries = self.get_binaries(false)?; if binaries.is_empty() { return Ok(()); } - if input.get_argument("binary").as_string().is_some() - || input.get_option("list").as_bool().unwrap_or(false) + if input.borrow().get_argument("binary").as_string().is_some() + || input.borrow().get_option("list").as_bool().unwrap_or(false) { return Ok(()); } @@ -66,7 +66,7 @@ impl ExecCommand { ); if let Some(idx) = binary.as_int() { - input.set_argument( + input.borrow_mut().set_argument( "binary", shirabe_php_shim::PhpMixed::String(binaries[idx as usize].clone()), ); @@ -77,13 +77,13 @@ impl ExecCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; - if input.get_option("list").as_bool().unwrap_or(false) - || input.get_argument("binary").as_string().is_none() + if input.borrow().get_option("list").as_bool().unwrap_or(false) + || input.borrow().get_argument("binary").as_string().is_none() { let bins = self.get_binaries(true)?; if bins.is_empty() { @@ -114,6 +114,7 @@ impl ExecCommand { } let binary = input + .borrow() .get_argument("binary") .as_string() .unwrap_or("") @@ -139,6 +140,7 @@ impl ExecCommand { } let args = input + .borrow() .get_argument("args") .as_list() .map(|l| { diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs index 0fab473..cb60eb0 100644 --- a/crates/shirabe/src/command/fund_command.rs +++ b/crates/shirabe/src/command/fund_command.rs @@ -47,8 +47,8 @@ impl FundCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let composer = crate::command::composer_full(&composer); @@ -121,6 +121,7 @@ impl FundCommand { let io = self.get_io(); let format = input + .borrow() .get_option("format") .as_string() .unwrap_or("text") diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs index a3d5a78..e5d06b7 100644 --- a/crates/shirabe/src/command/global_command.rs +++ b/crates/shirabe/src/command/global_command.rs @@ -56,8 +56,12 @@ impl GlobalCommand { ); } - pub fn run(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> { - let tokens = Preg::split(r"{\s+}", &input.to_input_string())?; + pub fn run( + &mut self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) -> Result<i64> { + let tokens = Preg::split(r"{\s+}", &input.borrow().to_input_string())?; let mut args: Vec<String> = vec![]; for token in &tokens { if !token.is_empty() && !token.starts_with('-') { @@ -76,12 +80,15 @@ impl GlobalCommand { let mut sub_input = self.prepare_subcommand_input(input, false)?; let mut app = self.get_application()?; let _ = output; - Ok(app.run(Some(&mut sub_input), None)?) + Ok(app.run( + Some(std::rc::Rc::new(std::cell::RefCell::new(sub_input))), + None, + )?) } fn prepare_subcommand_input( &mut self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, quiet: bool, ) -> Result<StringInput> { if Platform::get_env("COMPOSER").is_some() { @@ -118,7 +125,7 @@ impl GlobalCommand { let new_input_str = Preg::replace4( r"{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}", "", - &input.to_input_string(), + &input.borrow().to_input_string(), 1, )?; self.get_application()?.reset_composer(); diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs index db6f644..86909f5 100644 --- a/crates/shirabe/src/command/home_command.rs +++ b/crates/shirabe/src/command/home_command.rs @@ -69,14 +69,15 @@ impl HomeCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let repos = self.initialize_repos()?; let io = self.get_io().clone(); let mut return_code: i64 = 0; let packages: Vec<String> = input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -95,8 +96,12 @@ impl HomeCommand { packages }; - let show_homepage = input.get_option("homepage").as_bool().unwrap_or(false); - let show_only = input.get_option("show").as_bool().unwrap_or(false); + let show_homepage = input + .borrow() + .get_option("homepage") + .as_bool() + .unwrap_or(false); + let show_only = input.borrow().get_option("show").as_bool().unwrap_or(false); for package_name in &packages { let mut handled = false; diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index a46b6ca..eabe6e2 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -72,7 +72,7 @@ impl PackageDiscoveryTrait for InitCommand { fn get_platform_requirement_filter( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> std::rc::Rc< dyn crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface, > { @@ -116,8 +116,8 @@ impl InitCommand { /// @throws \Seld\JsonLint\ParsingException pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let io = PackageDiscoveryTrait::get_io(self); @@ -134,11 +134,13 @@ impl InitCommand { "autoload".to_string(), ]; // TODO(phase-b): adapt PhpMixed<->Box<PhpMixed> for array_filter_map - let filtered_input: IndexMap<String, Box<PhpMixed>> = - array_intersect_key(&input.get_options(), &array_flip_strings(&allowlist)) - .into_iter() - .map(|(k, v)| (k, Box::new(v))) - .collect(); + let filtered_input: IndexMap<String, Box<PhpMixed>> = array_intersect_key( + &input.borrow().get_options(), + &array_flip_strings(&allowlist), + ) + .into_iter() + .map(|(k, v)| (k, Box::new(v))) + .collect(); let mut options = shirabe_php_shim::array_filter_map(&filtered_input, |val: &PhpMixed| { !matches!(val, PhpMixed::Null) && !matches!(val, PhpMixed::List(l) if l.is_empty()) }); @@ -186,6 +188,7 @@ impl InitCommand { } let repositories: Vec<String> = input + .borrow() .get_option("repository") .as_list() .map(|l| { @@ -283,6 +286,7 @@ impl InitCommand { .to_string(); autoload_path = Some(ap.clone()); let name = input + .borrow() .get_option("name") .as_string() .unwrap_or("") @@ -303,7 +307,7 @@ impl InitCommand { .collect(); let json = JsonFile::encode(&PhpMixed::Array(options_for_encode.clone())); - if input.is_interactive() { + if input.borrow().is_interactive() { io.write_error3(&format!("\n{}\n", json), true, io_interface::NORMAL); if !io.ask_confirmation( "Do you confirm generation [<comment>yes</comment>]? ".to_string(), @@ -358,11 +362,11 @@ impl InitCommand { // dump-autoload only for projects without added dependencies. if !self.has_dependencies(&options) { - self.run_dump_autoload_command(output); + self.run_dump_autoload_command(output.clone()); } } - if input.is_interactive() && is_dir(".git") { + if input.borrow().is_interactive() && is_dir(".git") { let mut ignore_file = realpath(".gitignore").unwrap_or_default(); if ignore_file.is_empty() { @@ -380,7 +384,7 @@ impl InitCommand { let question = "Would you like to install dependencies now [<comment>yes</comment>]? ".to_string(); - if input.is_interactive() + if input.borrow().is_interactive() && self.has_dependencies(&options) && io.ask_confirmation(question, true) { @@ -390,6 +394,7 @@ impl InitCommand { // --autoload - Show post-install configuration info if autoload_path.is_some() { let name = input + .borrow() .get_option("name") .as_string() .unwrap_or("") @@ -411,15 +416,19 @@ impl InitCommand { Ok(0) } - pub(crate) fn initialize(&mut self, input: &dyn InputInterface, output: &dyn OutputInterface) { - self.initialize(input, output); + pub(crate) fn initialize( + &mut self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { + self.initialize(input.clone(), output); - if !input.is_interactive() { - if input.get_option("name").is_null() { + if !input.borrow().is_interactive() { + if input.borrow().get_option("name").is_null() { // TODO(phase-b): input.set_option requires &mut; signature passes &dyn here } - if input.get_option("author").is_null() { + if input.borrow().get_option("author").is_null() { // TODO(phase-b): input.set_option requires &mut; signature passes &dyn here } } @@ -427,8 +436,8 @@ impl InitCommand { pub(crate) fn interact( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<()> { let io = self.get_io(); // @var FormatterHelper $formatter @@ -439,6 +448,7 @@ impl InitCommand { // initialize repos if configured let repositories: Vec<String> = input + .borrow() .get_option("repository") .as_list() .map(|l| { @@ -527,6 +537,7 @@ impl InitCommand { ); let mut name = input + .borrow() .get_option("name") .as_string() .map(|s| s.to_string()) @@ -569,9 +580,12 @@ impl InitCommand { .as_string() .unwrap_or("") .to_string(); - input.set_option("name", PhpMixed::String(name)); + input + .borrow_mut() + .set_option("name", PhpMixed::String(name)); let description = input + .borrow() .get_option("description") .as_string() .map(|s| s.to_string()); @@ -585,9 +599,10 @@ impl InitCommand { .map(PhpMixed::String) .unwrap_or(PhpMixed::Null), ); - input.set_option("description", description); + input.borrow_mut().set_option("description", description); let author = input + .borrow() .get_option("author") .as_string() .map(|s| s.to_string()) @@ -623,9 +638,10 @@ impl InitCommand { None, PhpMixed::String(author_default), )?; - input.set_option("author", author_value); + input.borrow_mut().set_option("author", author_value); let minimum_stability = input + .borrow() .get_option("stability") .as_string() .map(|s| s.to_string()); @@ -669,9 +685,11 @@ impl InitCommand { .map(PhpMixed::String) .unwrap_or(PhpMixed::Null), )?; - input.set_option("stability", minimum_stability_value); + input + .borrow_mut() + .set_option("stability", minimum_stability_value); - let type_val = input.get_option("type"); + let type_val = input.borrow().get_option("type"); let type_str = type_val.as_string().unwrap_or("").to_string(); let mut type_value = io.ask( format!( @@ -683,9 +701,10 @@ impl InitCommand { if type_value.as_string() == Some("") || matches!(type_value, PhpMixed::Bool(false)) { type_value = PhpMixed::Null; } - input.set_option("type", type_value); + input.borrow_mut().set_option("type", type_value); let mut license = input + .borrow() .get_option("license") .as_string() .map(|s| s.to_string()); @@ -722,7 +741,7 @@ impl InitCommand { } .into()); } - input.set_option("license", license); + input.borrow_mut().set_option("license", license); io.write_error3("\nDefine your dependencies.\n", true, io_interface::NORMAL); @@ -741,6 +760,7 @@ impl InitCommand { let question = "Would you like to define your dependencies (require) interactively [<comment>yes</comment>]? ".to_string(); let require: Vec<String> = input + .borrow() .get_option("require") .as_list() .map(|l| { @@ -762,7 +782,7 @@ impl InitCommand { } else { vec![] }; - input.set_option( + input.borrow_mut().set_option( "require", PhpMixed::List( requirements @@ -774,6 +794,7 @@ impl InitCommand { let question = "Would you like to define your dev dependencies (require-dev) interactively [<comment>yes</comment>]? ".to_string(); let require_dev: Vec<String> = input + .borrow() .get_option("require-dev") .as_list() .map(|l| { @@ -796,7 +817,7 @@ impl InitCommand { } else { vec![] }; - input.set_option( + input.borrow_mut().set_option( "require-dev", PhpMixed::List( dev_requirements @@ -808,12 +829,14 @@ impl InitCommand { // --autoload - input and validation let mut autoload = input + .borrow() .get_option("autoload") .as_string() .map(|s| s.to_string()) .filter(|s| !s.is_empty()) .unwrap_or_else(|| "src/".to_string()); let name_str = input + .borrow() .get_option("name") .as_string() .unwrap_or("") @@ -861,7 +884,7 @@ impl InitCommand { None, PhpMixed::String(autoload_default), )?; - input.set_option("autoload", autoload_value); + input.borrow_mut().set_option("autoload", autoload_value); Ok(()) } @@ -1035,7 +1058,7 @@ impl InitCommand { shirabe_php_shim::filter_var(email, FILTER_VALIDATE_EMAIL) } - fn update_dependencies(&self, output: &dyn OutputInterface) { + fn update_dependencies(&self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) { // PHP try/catch: catch \Exception let result = self.get_application().and_then(|mut app| { let _update_command = app.find("update")?; @@ -1054,7 +1077,10 @@ impl InitCommand { } } - fn run_dump_autoload_command(&self, output: &dyn OutputInterface) { + fn run_dump_autoload_command( + &self, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { let result = self.get_application().and_then(|mut app| { let _command = app.find("dump-autoload")?; app.reset_composer(); diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs index 768f75f..de68f87 100644 --- a/crates/shirabe/src/command/install_command.rs +++ b/crates/shirabe/src/command/install_command.rs @@ -64,19 +64,24 @@ impl InstallCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let io = self.get_io().clone(); - if input.get_option("dev").as_bool().unwrap_or(false) { + if input.borrow().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>"); } - if input.get_option("no-suggest").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-suggest") + .as_bool() + .unwrap_or(false) + { io.write_error("<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>"); } - let args = input.get_argument("packages"); + let args = input.borrow().get_argument("packages"); let args_vec: Vec<String> = args .as_list() .map(|l| { @@ -94,7 +99,12 @@ impl InstallCommand { return Ok(1); } - if input.get_option("no-install").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-install") + .as_bool() + .unwrap_or(false) + { io.write_error("<error>Invalid option \"--no-install\". Use \"composer update --no-install\" instead if you are trying to update the composer.lock file.</error>"); return Ok(1); } @@ -107,7 +117,8 @@ impl InstallCommand { } // TODO(plugin): dispatch CommandEvent - let command_event = CommandEvent::new(PluginEvents::COMMAND, "install", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "install", input.clone(), output); composer .get_event_dispatcher() .borrow_mut() @@ -117,9 +128,10 @@ impl InstallCommand { let config = composer.get_config(); let (prefer_source, prefer_dist) = - self.get_preferred_install_options(&*config.borrow(), input, false)?; + self.get_preferred_install_options(&*config.borrow(), input.clone(), false)?; let optimize = input + .borrow() .get_option("optimize-autoloader") .as_bool() .unwrap_or(false) @@ -129,6 +141,7 @@ impl InstallCommand { .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -138,11 +151,13 @@ impl InstallCommand { .as_bool() .unwrap_or(false); let apcu_prefix = input + .borrow() .get_option("apcu-autoloader-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input + .borrow() .get_option("apcu-autoloader") .as_bool() .unwrap_or(false) @@ -155,26 +170,73 @@ impl InstallCommand { composer .get_installation_manager() .borrow_mut() - .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + .set_output_progress( + !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + ); install - .set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false)) - .set_download_only(input.get_option("download-only").as_bool().unwrap_or(false)) - .set_verbose(input.get_option("verbose").as_bool().unwrap_or(false)) + .set_dry_run( + input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false), + ) + .set_download_only( + input + .borrow() + .get_option("download-only") + .as_bool() + .unwrap_or(false), + ) + .set_verbose( + input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false), + ) .set_prefer_source(prefer_source) .set_prefer_dist(prefer_dist) - .set_dev_mode(!input.get_option("no-dev").as_bool().unwrap_or(false)) - .set_dump_autoloader(!input.get_option("no-autoloader").as_bool().unwrap_or(false)) + .set_dev_mode( + !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false), + ) + .set_dump_autoloader( + !input + .borrow() + .get_option("no-autoloader") + .as_bool() + .unwrap_or(false), + ) .set_optimize_autoloader(optimize) .set_class_map_authoritative(authoritative) .set_apcu_autoloader(apcu, apcu_prefix.clone()) - .set_platform_requirement_filter(self.get_platform_requirement_filter(input)?) + .set_platform_requirement_filter(self.get_platform_requirement_filter(input.clone())?) .set_audit_config( - self.create_audit_config(&mut *composer.get_config().borrow_mut(), input)?, + self.create_audit_config(&mut *composer.get_config().borrow_mut(), input.clone())?, ) - .set_error_on_audit(input.get_option("audit").as_bool().unwrap_or(false)); + .set_error_on_audit( + input + .borrow() + .get_option("audit") + .as_bool() + .unwrap_or(false), + ); - if input.get_option("no-plugins").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-plugins") + .as_bool() + .unwrap_or(false) + { install.disable_plugins(); } diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs index d870acb..79c86d1 100644 --- a/crates/shirabe/src/command/licenses_command.rs +++ b/crates/shirabe/src/command/licenses_command.rs @@ -76,14 +76,19 @@ impl LicensesCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); // TODO(plugin): dispatch COMMAND event for plugin hooks - let command_event = CommandEvent::new(PluginEvents::COMMAND, "licenses", input, output); + let command_event = CommandEvent::new( + PluginEvents::COMMAND, + "licenses", + input.clone(), + output.clone(), + ); composer .get_event_dispatcher() .borrow_mut() @@ -91,7 +96,12 @@ impl LicensesCommand { let root = composer.get_package(); - let packages = if input.get_option("locked").as_bool().unwrap_or(false) { + let packages = if input + .borrow() + .get_option("locked") + .as_bool() + .unwrap_or(false) + { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); if !locker.is_locked() { @@ -100,7 +110,11 @@ impl LicensesCommand { code: 0, }.into()); } - let no_dev = input.get_option("no-dev").as_bool().unwrap_or(false); + let no_dev = input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false); let repo = locker.get_locked_repository(!no_dev)?; repo.borrow_mut().get_packages()? } else { @@ -108,7 +122,12 @@ impl LicensesCommand { let repository_manager = repository_manager.borrow(); let repo = repository_manager.get_local_repository(); - if input.get_option("no-dev").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false) + { RepositoryUtils::filter_required_packages( &repo.get_packages()?, composer.get_package().clone().into(), @@ -126,6 +145,7 @@ impl LicensesCommand { let io = self.get_io(); let format = input + .borrow() .get_option("format") .as_string() .unwrap_or("text") diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs index 89edb44..f14ebe3 100644 --- a/crates/shirabe/src/command/outdated_command.rs +++ b/crates/shirabe/src/command/outdated_command.rs @@ -53,73 +53,130 @@ impl OutdatedCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let mut args: IndexMap<String, PhpMixed> = IndexMap::new(); args.insert("command".to_string(), PhpMixed::String("show".to_string())); args.insert("--latest".to_string(), PhpMixed::Bool(true)); if input + .borrow() .get_option("no-interaction") .as_bool() .unwrap_or(false) { args.insert("--no-interaction".to_string(), PhpMixed::Bool(true)); } - if input.get_option("no-plugins").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-plugins") + .as_bool() + .unwrap_or(false) + { args.insert("--no-plugins".to_string(), PhpMixed::Bool(true)); } - if input.get_option("no-scripts").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-scripts") + .as_bool() + .unwrap_or(false) + { args.insert("--no-scripts".to_string(), PhpMixed::Bool(true)); } - if input.get_option("no-cache").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-cache") + .as_bool() + .unwrap_or(false) + { args.insert("--no-cache".to_string(), PhpMixed::Bool(true)); } - if !input.get_option("all").as_bool().unwrap_or(false) { + if !input.borrow().get_option("all").as_bool().unwrap_or(false) { args.insert("--outdated".to_string(), PhpMixed::Bool(true)); } - if input.get_option("direct").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("direct") + .as_bool() + .unwrap_or(false) + { args.insert("--direct".to_string(), PhpMixed::Bool(true)); } - let package_arg = input.get_argument("package"); + let package_arg = input.borrow().get_argument("package"); if !matches!(package_arg, PhpMixed::Null) { args.insert("package".to_string(), package_arg); } - if input.get_option("strict").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("strict") + .as_bool() + .unwrap_or(false) + { args.insert("--strict".to_string(), PhpMixed::Bool(true)); } - if input.get_option("major-only").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("major-only") + .as_bool() + .unwrap_or(false) + { args.insert("--major-only".to_string(), PhpMixed::Bool(true)); } - if input.get_option("minor-only").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("minor-only") + .as_bool() + .unwrap_or(false) + { args.insert("--minor-only".to_string(), PhpMixed::Bool(true)); } - if input.get_option("patch-only").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("patch-only") + .as_bool() + .unwrap_or(false) + { args.insert("--patch-only".to_string(), PhpMixed::Bool(true)); } - if input.get_option("locked").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("locked") + .as_bool() + .unwrap_or(false) + { args.insert("--locked".to_string(), PhpMixed::Bool(true)); } - if input.get_option("no-dev").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false) + { args.insert("--no-dev".to_string(), PhpMixed::Bool(true)); } - if input.get_option("sort-by-age").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("sort-by-age") + .as_bool() + .unwrap_or(false) + { args.insert("--sort-by-age".to_string(), PhpMixed::Bool(true)); } args.insert( "--ignore-platform-req".to_string(), - input.get_option("ignore-platform-req"), + input.borrow().get_option("ignore-platform-req"), ); if input + .borrow() .get_option("ignore-platform-reqs") .as_bool() .unwrap_or(false) { args.insert("--ignore-platform-reqs".to_string(), PhpMixed::Bool(true)); } - args.insert("--format".to_string(), input.get_option("format")); - args.insert("--ignore".to_string(), input.get_option("ignore")); + args.insert("--format".to_string(), input.borrow().get_option("format")); + args.insert("--ignore".to_string(), input.borrow().get_option("ignore")); let input = ArrayInput::new(args); diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index 72b635f..9bd49c2 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -49,7 +49,7 @@ pub trait PackageDiscoveryTrait { ) -> PartialComposerHandle; fn get_platform_requirement_filter( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> std::rc::Rc< dyn crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface, >; @@ -81,7 +81,7 @@ pub trait PackageDiscoveryTrait { /// @param key-of<BasePackage::STABILITIES>|null $minimumStability fn get_repository_set( &mut self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, minimum_stability: Option<&str>, ) -> &RepositorySet { let key = minimum_stability.unwrap_or("default").to_string(); @@ -109,11 +109,15 @@ pub trait PackageDiscoveryTrait { } /// @return key-of<BasePackage::STABILITIES> - fn get_minimum_stability(&self, input: &dyn InputInterface) -> String { - if input.has_option("stability") { + fn get_minimum_stability( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + ) -> String { + if input.borrow().has_option("stability") { // @phpstan-ignore-line as InitCommand does have this option but not all classes using this trait do return VersionParser::normalize_stability( &input + .borrow() .get_option("stability") .as_string() .map(|s| s.to_string()) @@ -147,8 +151,8 @@ pub trait PackageDiscoveryTrait { /// @throws \Exception fn determine_requirements( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, mut requires: Vec<String>, platform_repo: Option<&PlatformRepositoryHandle>, preferred_stability: &str, @@ -184,7 +188,7 @@ pub trait PackageDiscoveryTrait { let (name, version): (String, String) = self .find_best_version_and_name_for_package( io.clone(), - input, + input.clone(), requirement.get("name").map(|s| s.as_str()).unwrap_or(""), platform_repo, preferred_stability, @@ -492,27 +496,27 @@ pub trait PackageDiscoveryTrait { fn find_best_version_and_name_for_package( &mut self, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, name: &str, platform_repo: Option<&PlatformRepositoryHandle>, preferred_stability: &str, fixed: bool, ) -> Result<(String, String)> { // handle ignore-platform-reqs flag if present - let platform_requirement_filter = if input.has_option("ignore-platform-reqs") - && input.has_option("ignore-platform-req") + let platform_requirement_filter = if input.borrow().has_option("ignore-platform-reqs") + && input.borrow().has_option("ignore-platform-req") { - self.get_platform_requirement_filter(input) + self.get_platform_requirement_filter(input.clone()) } else { PlatformRequirementFilterFactory::ignore_nothing() }; // find the latest version allowed in this repo set - let repo_set = self.get_repository_set(input, None); + let repo_set = self.get_repository_set(input.clone(), None); // TODO(phase-b): VersionSelector::new takes owned RepositorySet; we have a shared reference let mut version_selector: VersionSelector = todo!("VersionSelector::new with owned repo_set"); - let effective_minimum_stability = self.get_minimum_stability(input); + let effective_minimum_stability = self.get_minimum_stability(input.clone()); let package = version_selector.find_best_candidate( name, @@ -539,7 +543,7 @@ pub trait PackageDiscoveryTrait { )) > 0 { let mut constraint = "*".to_string(); - if input.is_interactive() { + if input.borrow().is_interactive() { let providers_count = providers.len(); let name_owned = name.to_string(); let validator: Box<dyn Fn(PhpMixed) -> anyhow::Result<PhpMixed>> = @@ -733,7 +737,7 @@ pub trait PackageDiscoveryTrait { .into()); } - if input.is_interactive() { + if input.borrow().is_interactive() { let result_mixed = io.select( format!( "<error>Could not find package {}.</error>\nPick one of these or leave empty to abort:", diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs index ff9328c..6fca061 100644 --- a/crates/shirabe/src/command/prohibits_command.rs +++ b/crates/shirabe/src/command/prohibits_command.rs @@ -75,8 +75,8 @@ impl ProhibitsCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { self.do_execute(input, output, true) } diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index d183c00..6e4cd7b 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -61,8 +61,8 @@ impl ReinstallCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let composer = crate::command::composer_full(&composer); @@ -74,9 +74,9 @@ impl ReinstallCommand { let mut packages_to_reinstall: Vec<crate::package::PackageInterfaceHandle> = vec![]; let mut package_names_to_reinstall: Vec<String> = vec![]; - let type_option = input.get_option("type"); + let type_option = input.borrow().get_option("type"); let type_count = type_option.as_list().map_or(0, |l| l.len()); - let packages_arg = input.get_argument("packages"); + let packages_arg = input.borrow().get_argument("packages"); let packages_count = packages_arg.as_list().map_or(0, |l| l.len()); if type_count > 0 { @@ -180,7 +180,8 @@ impl ReinstallCommand { }); // TODO(plugin): dispatch CommandEvent - let command_event = CommandEvent::new(PluginEvents::COMMAND, "reinstall", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "reinstall", input.clone(), output); let event_dispatcher = composer.get_event_dispatcher(); event_dispatcher .borrow_mut() @@ -188,15 +189,23 @@ impl ReinstallCommand { let config = composer.get_config(); let (prefer_source, prefer_dist) = - self.get_preferred_install_options(&*config.borrow(), input, false)?; + self.get_preferred_install_options(&*config.borrow(), input.clone(), false)?; let installation_manager = composer.get_installation_manager().clone(); let download_manager = composer.get_download_manager(); let package = composer.get_package(); // TODO(phase-b): InstallationManager setters need &mut self; conflicts with the &installation_manager / &local_repo / &package borrows held below; needs shared-ownership refactor - let _no_progress = !input.get_option("no-progress").as_bool().unwrap_or(false); - let _no_plugins = input.get_option("no-plugins").as_bool().unwrap_or(false); + let _no_progress = !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false); + let _no_plugins = input + .borrow() + .get_option("no-plugins") + .as_bool() + .unwrap_or(false); download_manager .borrow_mut() @@ -225,8 +234,14 @@ impl ReinstallCommand { // installation_manager.execute(local_repo_mut, uninstall_ops_boxed, dev_mode, true, false); // installation_manager.execute(local_repo_mut, install_ops_boxed, dev_mode, true, false); - if !input.get_option("no-autoloader").as_bool().unwrap_or(false) { + if !input + .borrow() + .get_option("no-autoloader") + .as_bool() + .unwrap_or(false) + { let optimize = input + .borrow() .get_option("optimize-autoloader") .as_bool() .unwrap_or(false) @@ -236,6 +251,7 @@ impl ReinstallCommand { .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -245,11 +261,13 @@ impl ReinstallCommand { .as_bool() .unwrap_or(false); let apcu_prefix = input + .borrow() .get_option("apcu-autoloader-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input + .borrow() .get_option("apcu-autoloader") .as_bool() .unwrap_or(false) diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index 225714e..2cf71e7 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -157,15 +157,20 @@ impl RemoveCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { if input + .borrow() .get_argument("packages") .as_list() .map(|l| l.is_empty()) .unwrap_or(true) - && !input.get_option("unused").as_bool().unwrap_or(false) + && !input + .borrow() + .get_option("unused") + .as_bool() + .unwrap_or(false) { return Err(anyhow::anyhow!(InvalidArgumentException { message: "Not enough arguments (missing: \"packages\").".to_string(), @@ -174,6 +179,7 @@ impl RemoveCommand { } let mut packages: Vec<String> = input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -183,7 +189,12 @@ impl RemoveCommand { }) .unwrap_or_default(); - if input.get_option("unused").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("unused") + .as_bool() + .unwrap_or(false) + { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); { @@ -261,12 +272,12 @@ impl RemoveCommand { let json_file_for_source = JsonFile::new(file.clone(), None, None)?; let mut json = JsonConfigSource::new(json_file_for_source, false); - let r#type = if input.get_option("dev").as_bool().unwrap_or(false) { + let r#type = if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "require-dev" } else { "require" }; - let alt_type = if !input.get_option("dev").as_bool().unwrap_or(false) { + let alt_type = if !input.borrow().get_option("dev").as_bool().unwrap_or(false) { "require-dev" } else { "require" @@ -274,6 +285,7 @@ impl RemoveCommand { let io = self.get_io(); if input + .borrow() .get_option("update-with-dependencies") .as_bool() .unwrap_or(false) @@ -299,7 +311,11 @@ impl RemoveCommand { } } - let dry_run = input.get_option("dry-run").as_bool().unwrap_or(false); + let dry_run = input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false); let mut to_remove: IndexMap<String, Vec<String>> = IndexMap::new(); for package in &packages { let in_type = composer_data @@ -425,7 +441,12 @@ impl RemoveCommand { io.write_error(&format!("<info>{} has been updated</info>", file)); - if input.get_option("no-update").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-update") + .as_bool() + .unwrap_or(false) + { return Ok(0); } @@ -468,7 +489,7 @@ impl RemoveCommand { let command_event = crate::plugin::CommandEvent::new( crate::plugin::PluginEvents::COMMAND, "remove", - input, + input.clone(), output, ); composer @@ -510,12 +531,23 @@ impl RemoveCommand { composer .get_installation_manager() .borrow_mut() - .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + .set_output_progress( + !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + ); let mut install = Installer::create(self.get_io().clone(), &composer_handle); - let update_dev_mode = !input.get_option("update-no-dev").as_bool().unwrap_or(false); + let update_dev_mode = !input + .borrow() + .get_option("update-no-dev") + .as_bool() + .unwrap_or(false); let optimize = input + .borrow() .get_option("optimize-autoloader") .as_bool() .unwrap_or(false) @@ -526,6 +558,7 @@ impl RemoveCommand { .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -536,11 +569,13 @@ impl RemoveCommand { .as_bool() .unwrap_or(false); let apcu_prefix = input + .borrow() .get_option("apcu-autoloader-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input + .borrow() .get_option("apcu-autoloader") .as_bool() .unwrap_or(false) @@ -551,6 +586,7 @@ impl RemoveCommand { .as_bool() .unwrap_or(false); let minimal_changes = input + .borrow() .get_option("minimal-changes") .as_bool() .unwrap_or(false) @@ -565,10 +601,12 @@ impl RemoveCommand { Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; let mut flags = String::new(); if input + .borrow() .get_option("update-with-all-dependencies") .as_bool() .unwrap_or(false) || input + .borrow() .get_option("with-all-dependencies") .as_bool() .unwrap_or(false) @@ -576,6 +614,7 @@ impl RemoveCommand { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; flags += " --with-all-dependencies"; } else if input + .borrow() .get_option("no-update-with-dependencies") .as_bool() .unwrap_or(false) @@ -590,15 +629,28 @@ impl RemoveCommand { flags )); - install.set_verbose(input.get_option("verbose").as_bool().unwrap_or(false)); + install.set_verbose( + input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false), + ); install.set_dev_mode(update_dev_mode); install.set_optimize_autoloader(optimize); install.set_class_map_authoritative(authoritative); install.set_apcu_autoloader(apcu, apcu_prefix); install.set_update(true); - install.set_install(!input.get_option("no-install").as_bool().unwrap_or(false)); + install.set_install( + !input + .borrow() + .get_option("no-install") + .as_bool() + .unwrap_or(false), + ); install.set_update_allow_transitive_dependencies(update_allow_transitive_dependencies); - install.set_platform_requirement_filter(self.get_platform_requirement_filter(input)?); + install + .set_platform_requirement_filter(self.get_platform_requirement_filter(input.clone())?); install.set_dry_run(dry_run); install.set_audit_config( self.create_audit_config(&mut *composer.get_config().borrow_mut(), input)?, diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index d57a98d..5ced013 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -68,25 +68,29 @@ impl RepositoryCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { let action = strtolower( &input + .borrow() .get_argument("action") .as_string() .unwrap_or("") .to_string(), ); let name = input + .borrow() .get_argument("name") .as_string() .map(|s| s.to_string()); let arg1 = input + .borrow() .get_argument("arg1") .as_string() .map(|s| s.to_string()); let arg2 = input + .borrow() .get_argument("arg2") .as_string() .map(|s| s.to_string()); @@ -143,10 +147,15 @@ impl RepositoryCommand { }; let before = input + .borrow() .get_option("before") .as_string() .map(|s| s.to_string()); - let after = input.get_option("after").as_string().map(|s| s.to_string()); + let after = input + .borrow() + .get_option("after") + .as_string() + .map(|s| s.to_string()); if before.is_some() && after.is_some() { return Err(anyhow::anyhow!(RuntimeException { message: "You can not combine --before and --after".to_string(), @@ -173,7 +182,11 @@ impl RepositoryCommand { return Ok(0); } - let append = input.get_option("append").as_bool().unwrap_or(false); + let append = input + .borrow() + .get_option("append") + .as_bool() + .unwrap_or(false); self.config_source.as_mut().unwrap().add_repository( name.as_deref().unwrap(), repo_config.clone(), @@ -270,7 +283,11 @@ 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); + let append = input + .borrow() + .get_option("append") + .as_bool() + .unwrap_or(false); self.config_source.as_mut().unwrap().add_repository( "packagist.org", PhpMixed::Bool(false), diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index e5ddbe7..d1143fe 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -87,7 +87,7 @@ impl PackageDiscoveryTrait for RequireCommand { fn get_platform_requirement_filter( &self, - input: &dyn InputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, ) -> std::rc::Rc< dyn crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface, > { @@ -154,12 +154,17 @@ impl RequireCommand { /// @throws \Seld\JsonLint\ParsingException pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { self.file = Factory::get_composer_file()?; - if input.get_option("no-suggest").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-suggest") + .as_bool() + .unwrap_or(false) + { self.get_io().write_error3("<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>", true, io_interface::NORMAL); } @@ -225,7 +230,7 @@ impl RequireCommand { return Ok(1); } - if input.get_option("fixed").as_bool() == Some(true) { + if input.borrow().get_option("fixed").as_bool() == Some(true) { let config = self.json.as_mut().unwrap().read()?; let package_type = if empty(&config.get("type").cloned().unwrap_or(PhpMixed::Null)) { @@ -239,7 +244,9 @@ impl RequireCommand { }; /// @see https://github.com/composer/composer/pull/8313#issuecomment-532637955 - if package_type != "project" && !input.get_option("dev").as_bool().unwrap_or(false) { + if package_type != "project" + && !input.borrow().get_option("dev").as_bool().unwrap_or(false) + { self.get_io().write_error3("<error>The \"--fixed\" option is only allowed for packages with a \"project\" type or for dev dependencies to prevent possible misuses.</error>", true, io_interface::NORMAL); if config.get("type").is_none() { @@ -278,9 +285,10 @@ impl RequireCommand { }; let requirements_result = self.determine_requirements( - input, - output, + input.clone(), + output.clone(), input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -292,8 +300,16 @@ impl RequireCommand { Some(&platform_repo), &preferred_stability, // if there is no update, we need to use the best possible version constraint directly as we cannot rely on the solver to guess the best constraint - input.get_option("no-update").as_bool().unwrap_or(false), - input.get_option("fixed").as_bool().unwrap_or(false), + input + .borrow() + .get_option("no-update") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("fixed") + .as_bool() + .unwrap_or(false), ); let requirements = match requirements_result { @@ -318,7 +334,7 @@ impl RequireCommand { let mut requirements = self.format_requirements(requirements)?; - if !input.get_option("dev").as_bool().unwrap_or(false) + if !input.borrow().get_option("dev").as_bool().unwrap_or(false) && self.get_io().is_interactive() && !composer.is_global() { @@ -398,12 +414,12 @@ impl RequireCommand { // unset($devPackages, $pkgDevTags); } - let mut require_key = if input.get_option("dev").as_bool().unwrap_or(false) { + let mut require_key = if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "require-dev" } else { "require" }; - let mut remove_key = if input.get_option("dev").as_bool().unwrap_or(false) { + let mut remove_key = if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "require" } else { "require-dev" @@ -446,7 +462,7 @@ impl RequireCommand { PhpMixed::String(package.clone()), PhpMixed::String(remove_key.to_string()), PhpMixed::String( - if input.get_option("dev").as_bool().unwrap_or(false) { + if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "with" } else { "without" @@ -475,7 +491,7 @@ impl RequireCommand { let q2 = sprintf( "<info>Do you want to re-run the command %s --dev?</info> [<comment>yes</comment>]? ", &[PhpMixed::String( - if input.get_option("dev").as_bool().unwrap_or(false) { + if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "without" } else { "with" @@ -497,7 +513,11 @@ impl RequireCommand { } } - let sort_packages = input.get_option("sort-packages").as_bool().unwrap_or(false) + let sort_packages = input + .borrow() + .get_option("sort-packages") + .as_bool() + .unwrap_or(false) || composer .get_config() .borrow() @@ -523,7 +543,12 @@ impl RequireCommand { } } - if !input.get_option("dry-run").as_bool().unwrap_or(false) { + if !input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false) + { // TODO(phase-b): update_file takes &mut self, but the json argument must be borrowed // from self.json, producing an overlapping borrow of self. Commented out until JsonFile // is shared (Rc<RefCell> / interior mutability) so it can be passed without holding a @@ -550,7 +575,12 @@ impl RequireCommand { self.get_io() .write_error3(&updated_msg, true, io_interface::NORMAL); - if input.get_option("no-update").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-update") + .as_bool() + .unwrap_or(false) + { return Ok(0); } @@ -560,9 +590,19 @@ impl RequireCommand { .deactivate_installed_plugins()?; let io = self.get_io().clone(); - let do_update_result = - self.do_update(input, output, io, &requirements, require_key, remove_key); - let dry_run = input.get_option("dry-run").as_bool().unwrap_or(false); + let do_update_result = self.do_update( + input.clone(), + output, + io, + &requirements, + require_key, + remove_key, + ); + let dry_run = input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false); let result = match do_update_result { Ok(result) => { @@ -573,7 +613,11 @@ impl RequireCommand { remove_key, sort_packages, dry_run, - input.get_option("fixed").as_bool().unwrap_or(false), + input + .borrow() + .get_option("fixed") + .as_bool() + .unwrap_or(false), )? } else { result @@ -678,8 +722,8 @@ impl RequireCommand { /// @throws \Exception fn do_update( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, requirements: &IndexMap<String, String>, require_key: &str, @@ -699,7 +743,12 @@ impl RequireCommand { 10000, ); - if input.get_option("dry-run").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false) + { let root_package = composer.get_package(); let mut links: IndexMap<String, IndexMap<String, crate::package::Link>> = IndexMap::new(); @@ -746,8 +795,13 @@ impl RequireCommand { // unset($stabilityFlags, $references); } - let update_dev_mode = !input.get_option("update-no-dev").as_bool().unwrap_or(false); + let update_dev_mode = !input + .borrow() + .get_option("update-no-dev") + .as_bool() + .unwrap_or(false); let optimize = input + .borrow() .get_option("optimize-autoloader") .as_bool() .unwrap_or(false) @@ -758,6 +812,7 @@ impl RequireCommand { .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -768,11 +823,13 @@ impl RequireCommand { .as_bool() .unwrap_or(false); let apcu_prefix = input + .borrow() .get_option("apcu-autoloader-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input + .borrow() .get_option("apcu-autoloader") .as_bool() .unwrap_or(false) @@ -783,6 +840,7 @@ impl RequireCommand { .as_bool() .unwrap_or(false); let minimal_changes = input + .borrow() .get_option("minimal-changes") .as_bool() .unwrap_or(false) @@ -796,10 +854,12 @@ impl RequireCommand { let mut update_allow_transitive_dependencies = Request::UPDATE_ONLY_LISTED; let mut flags = String::new(); if input + .borrow() .get_option("update-with-all-dependencies") .as_bool() .unwrap_or(false) || input + .borrow() .get_option("with-all-dependencies") .as_bool() .unwrap_or(false) @@ -807,10 +867,12 @@ impl RequireCommand { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; flags += " --with-all-dependencies"; } else if input + .borrow() .get_option("update-with-dependencies") .as_bool() .unwrap_or(false) || input + .borrow() .get_option("with-dependencies") .as_bool() .unwrap_or(false) @@ -835,7 +897,8 @@ impl RequireCommand { io_interface::NORMAL, ); - let command_event = CommandEvent::new(PluginEvents::COMMAND, "require", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "require", input.clone(), output); composer .get_event_dispatcher() .borrow_mut() @@ -844,16 +907,37 @@ impl RequireCommand { composer .get_installation_manager() .borrow_mut() - .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + .set_output_progress( + !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + ); let mut install = Installer::create(io.clone(), &composer_handle); - let (prefer_source, prefer_dist) = - self.get_preferred_install_options(&*composer.get_config().borrow(), input, false)?; + let (prefer_source, prefer_dist) = self.get_preferred_install_options( + &*composer.get_config().borrow(), + input.clone(), + false, + )?; install - .set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false)) - .set_verbose(input.get_option("verbose").as_bool().unwrap_or(false)) + .set_dry_run( + input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false), + ) + .set_verbose( + input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false), + ) .set_prefer_source(prefer_source) .set_prefer_dist(prefer_dist) .set_dev_mode(update_dev_mode) @@ -861,15 +945,34 @@ impl RequireCommand { .set_class_map_authoritative(authoritative) .set_apcu_autoloader(apcu, apcu_prefix.clone()) .set_update(true) - .set_install(!input.get_option("no-install").as_bool().unwrap_or(false)) + .set_install( + !input + .borrow() + .get_option("no-install") + .as_bool() + .unwrap_or(false), + ) .set_update_allow_transitive_dependencies(update_allow_transitive_dependencies)? .set_platform_requirement_filter(BaseCommand::get_platform_requirement_filter( - self, input, + self, + input.clone(), )?) - .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_prefer_stable( + input + .borrow() + .get_option("prefer-stable") + .as_bool() + .unwrap_or(false), + ) + .set_prefer_lowest( + input + .borrow() + .get_option("prefer-lowest") + .as_bool() + .unwrap_or(false), + ) .set_audit_config( - self.create_audit_config(&mut *composer.get_config().borrow_mut(), input)?, + self.create_audit_config(&mut *composer.get_config().borrow_mut(), input.clone())?, ) .set_minimal_update(minimal_changes); @@ -889,6 +992,7 @@ impl RequireCommand { for req in BaseCommand::normalize_requirements( self, input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -1138,7 +1242,12 @@ impl RequireCommand { true } - pub(crate) fn interact(&self, _input: &dyn InputInterface, _output: &dyn OutputInterface) {} + pub(crate) fn interact( + &self, + _input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { + } fn revert_composer_file(&mut self) { if self.newly_created { diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs index 9118463..cf8b8f1 100644 --- a/crates/shirabe/src/command/run_script_command.rs +++ b/crates/shirabe/src/command/run_script_command.rs @@ -115,16 +115,16 @@ impl RunScriptCommand { pub fn interact( &mut self, - input: &mut dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<()> { let scripts = self.get_scripts()?; if scripts.is_empty() { return Ok(()); } - if input.get_argument("script").as_string().is_some() - || input.get_option("list").as_bool().unwrap_or(false) + if input.borrow().get_argument("script").as_string().is_some() + || input.borrow().get_option("list").as_bool().unwrap_or(false) { return Ok(()); } @@ -145,7 +145,7 @@ impl RunScriptCommand { ); if let Some(selected) = script.as_string() { - // TODO(phase-b): input is &dyn InputInterface but set_argument needs &mut. + // TODO(phase-b): input is std::rc::Rc<std::cell::RefCell<dyn InputInterface>> but set_argument needs &mut. let _ = selected; } @@ -154,14 +154,14 @@ impl RunScriptCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { - if input.get_option("list").as_bool().unwrap_or(false) { + if input.borrow().get_option("list").as_bool().unwrap_or(false) { return self.list_scripts(output); } - let script = match input.get_argument("script").as_string() { + let script = match input.borrow().get_argument("script").as_string() { None => { return Err(RuntimeException { message: "Missing required argument \"script\"".to_string(), @@ -187,8 +187,12 @@ impl RunScriptCommand { let dispatcher = crate::command::composer_full(&composer) .get_event_dispatcher() .clone(); - let dev_mode = input.get_option("dev").as_bool().unwrap_or(false) - || !input.get_option("no-dev").as_bool().unwrap_or(false); + let dev_mode = input.borrow().get_option("dev").as_bool().unwrap_or(false) + || !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false); // TODO(phase-b): ScriptEvent::new takes Composer/IOInterface by value; placeholder construction. let _ = (script.clone(), &composer, dev_mode); let has_listeners = false; @@ -201,6 +205,7 @@ impl RunScriptCommand { } let args: Vec<String> = input + .borrow() .get_argument("args") .as_list() .map(|l| { @@ -210,7 +215,7 @@ impl RunScriptCommand { }) .unwrap_or_default(); - if let Some(timeout_val) = input.get_option("timeout").as_string() { + if let Some(timeout_val) = input.borrow().get_option("timeout").as_string() { let timeout_str = timeout_val.to_string(); if !timeout_str.chars().all(|c| c.is_ascii_digit()) { return Err(RuntimeException { @@ -232,7 +237,10 @@ impl RunScriptCommand { .dispatch_script(&script, dev_mode, args, IndexMap::new())?) } - fn list_scripts(&mut self, output: &dyn OutputInterface) -> Result<i64> { + fn list_scripts( + &mut self, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) -> Result<i64> { let scripts = self.get_scripts()?; if scripts.is_empty() { return Ok(0); diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs index 6499002..7c6e9fb 100644 --- a/crates/shirabe/src/command/script_alias_command.rs +++ b/crates/shirabe/src/command/script_alias_command.rs @@ -93,15 +93,15 @@ impl ScriptAliasCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let dispatcher = crate::command::composer_full(&composer) .get_event_dispatcher() .clone(); - let args = input.get_arguments(); + let args = input.borrow().get_arguments(); // TODO(phase-b): InputInterface has_to_string/get_class_name not modeled in Rust // TODO remove for Symfony 6+ as it is then in the interface @@ -113,8 +113,12 @@ impl ScriptAliasCommand { .into()); } - let dev_mode = input.get_option("dev").as_bool().unwrap_or(false) - || !input.get_option("no-dev").as_bool().unwrap_or(false); + let dev_mode = input.borrow().get_option("dev").as_bool().unwrap_or(false) + || !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false); Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" }); diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs index 593b927..7149db7 100644 --- a/crates/shirabe/src/command/search_command.rs +++ b/crates/shirabe/src/command/search_command.rs @@ -45,13 +45,14 @@ impl SearchCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let platform_repo = PlatformRepository::new4(vec![], IndexMap::new(), None, None)?; let io = self.get_io(); let format = input + .borrow() .get_option("format") .as_string() .map(|s| s.to_string()) @@ -76,7 +77,7 @@ impl SearchCommand { } else { // TODO(phase-b): clone_box to release self borrow held by get_io. let io_box = self.get_io().clone(); - self.create_composer_instance(input, io_box, None, false, None)? + self.create_composer_instance(input.clone(), io_box, None, false, None)? }; let composer_ref = crate::command::composer_full(&composer); let local_repo = composer_ref @@ -99,7 +100,8 @@ impl SearchCommand { let mut repos = CompositeRepository::new(all_repos); // TODO(plugin): dispatch CommandEvent for search command - let command_event = CommandEvent::new(PluginEvents::COMMAND, "search", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "search", input.clone(), output); let dispatcher = composer_ref.get_event_dispatcher().clone(); drop(composer_ref); dispatcher @@ -107,8 +109,18 @@ impl SearchCommand { .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) { - if input.get_option("only-vendor").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("only-name") + .as_bool() + .unwrap_or(false) + { + if input + .borrow() + .get_option("only-vendor") + .as_bool() + .unwrap_or(false) + { return Err(InvalidArgumentException { message: "--only-name and --only-vendor cannot be used together".to_string(), code: 0, @@ -116,13 +128,22 @@ impl SearchCommand { .into()); } mode = repository_interface::SEARCH_NAME; - } else if input.get_option("only-vendor").as_bool().unwrap_or(false) { + } else if input + .borrow() + .get_option("only-vendor") + .as_bool() + .unwrap_or(false) + { mode = repository_interface::SEARCH_VENDOR; } - let r#type = input.get_option("type").as_string().map(|s| s.to_string()); + let r#type = input + .borrow() + .get_option("type") + .as_string() + .map(|s| s.to_string()); - let tokens_arg = input.get_argument("tokens"); + let tokens_arg = input.borrow().get_argument("tokens"); let token_strings: Vec<String> = tokens_arg .as_array() .map(|arr| { diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs index 1dc8d87..2353d13 100644 --- a/crates/shirabe/src/command/self_update_command.rs +++ b/crates/shirabe/src/command/self_update_command.rs @@ -75,8 +75,8 @@ impl SelfUpdateCommand { /// @throws FilesystemException pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { // TODO(phase-b): __FILE__ / __DIR__ have no direct Rust equivalent let file_path: &str = ""; @@ -85,27 +85,27 @@ impl SelfUpdateCommand { if strpos(file_path, "phar:") != Some(0) { if str_contains(&strtr(dir_path, "\\", "/"), "vendor/composer/composer") { let proj_dir = shirabe_php_shim::dirname_levels(dir_path, 6); - output.writeln( + output.borrow().writeln( "<error>This instance of Composer does not have the self-update command.</error>", io_interface::NORMAL, ); - output.writeln( + output.borrow().writeln( &format!( "<comment>You are running Composer installed as a package in your current project (\"{}\").</comment>", proj_dir ), io_interface::NORMAL, ); - output.writeln( + output.borrow().writeln( "<comment>To update Composer, download a composer.phar from https://getcomposer.org and then run `composer.phar update composer/composer` in your project.</comment>", io_interface::NORMAL, ); } else { - output.writeln( + output.borrow().writeln( "<error>This instance of Composer does not have the self-update command.</error>", io_interface::NORMAL, ); - output.writeln( + output.borrow().writeln( "<comment>This could be due to a number of reasons, such as Composer being installed as a system package on your OS, or Composer being installed as a package in the current project.</comment>", io_interface::NORMAL, ); @@ -141,7 +141,12 @@ impl SelfUpdateCommand { // switch channel if requested let mut requested_channel: Option<String> = None; for channel in Versions::CHANNELS { - if input.get_option(channel).as_bool().unwrap_or(false) { + if input + .borrow() + .get_option(channel) + .as_bool() + .unwrap_or(false) + { requested_channel = Some(channel.to_string()); versions_util.set_channel(channel.to_string(), Some(io.clone()))??; break; @@ -149,6 +154,7 @@ impl SelfUpdateCommand { } if input + .borrow() .get_option("set-channel-only") .as_bool() .unwrap_or(false) @@ -183,7 +189,12 @@ impl SelfUpdateCommand { .into()); } - if input.get_option("update-keys").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("update-keys") + .as_bool() + .unwrap_or(false) + { self.fetch_keys(io.clone(), &*config.borrow())?; return Ok(0); } @@ -252,12 +263,17 @@ impl SelfUpdateCommand { } } - if input.get_option("rollback").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("rollback") + .as_bool() + .unwrap_or(false) + { return self.rollback(output, &rollback_dir, &local_filename); } - if input.get_argument("command").as_string() == Some("self") - && input.get_argument("version").as_string() == Some("update") + if input.borrow().get_argument("command").as_string() == Some("self") + && input.borrow().get_argument("version").as_string() == Some("update") { // TODO(phase-b): set_argument requires &mut InputInterface; input is &dyn here } @@ -274,6 +290,7 @@ impl SelfUpdateCommand { .unwrap_or("") .to_string(); let mut update_version = input + .borrow() .get_argument("version") .as_string() .map(|s| s.to_string()) @@ -289,7 +306,9 @@ impl SelfUpdateCommand { .unwrap_or(""), )?; - if versions_util.get_channel()? == "stable" && input.get_argument("version").is_null() { + if versions_util.get_channel()? == "stable" + && input.borrow().get_argument("version").is_null() + { // if requesting stable channel and no specific version, avoid automatically upgrading to the next major // simply output a warning that the next major stable is available and let users upgrade to it manually if version_compare(¤t_major_version, &update_major_version, "<") { @@ -394,7 +413,12 @@ impl SelfUpdateCommand { ); // remove all backups except for the most recent, if any - if input.get_option("clean-backups").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("clean-backups") + .as_bool() + .unwrap_or(false) + { let last_backup = self.get_last_backup_version(&rollback_dir); self.clean_backups(&rollback_dir, last_backup.as_deref()); } @@ -616,7 +640,12 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ } // remove saved installations of composer - if input.get_option("clean-backups").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("clean-backups") + .as_bool() + .unwrap_or(false) + { // TODO(phase-b): self.clean_backups conflicts with earlier `self.get_io()` borrow } @@ -807,7 +836,7 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ /// @throws FilesystemException pub(crate) fn rollback( &mut self, - _output: &dyn OutputInterface, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, rollback_dir: &str, local_filename: &str, ) -> Result<i64> { diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index 299ba1e..e14ab15 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -78,41 +78,49 @@ impl ShowCommand { pub fn execute( &mut self, - input: &mut dyn InputInterface, - output: &mut dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { self.version_parser = VersionParser::new(); - if input.get_option("tree").as_bool() == Some(true) { - self.init_styles(output); + if input.borrow().get_option("tree").as_bool() == Some(true) { + self.init_styles(output.clone()); } let mut composer = self.try_composer(None, None); - if input.get_option("installed").as_bool() == Some(true) - && input.get_option("self").as_bool() != Some(true) + if input.borrow().get_option("installed").as_bool() == Some(true) + && input.borrow().get_option("self").as_bool() != Some(true) { self.get_io().write_error("<warning>You are using the deprecated option \"installed\". Only installed packages are shown by default now. The --all option can be used to show all packages.</warning>"); } - if input.get_option("outdated").as_bool() == Some(true) { - input.set_option("latest", PhpMixed::Bool(true)); - } else if input.get_option("ignore").as_list().map_or(0, |l| l.len()) > 0 { + if input.borrow().get_option("outdated").as_bool() == Some(true) { + input + .borrow_mut() + .set_option("latest", PhpMixed::Bool(true)); + } else if input + .borrow() + .get_option("ignore") + .as_list() + .map_or(0, |l| l.len()) + > 0 + { self.get_io().write_error("<warning>You are using the option \"ignore\" for action other than \"outdated\", it will be ignored.</warning>"); } - if input.get_option("direct").as_bool() == Some(true) - && (input.get_option("all").as_bool() == Some(true) - || input.get_option("available").as_bool() == Some(true) - || input.get_option("platform").as_bool() == Some(true)) + if input.borrow().get_option("direct").as_bool() == Some(true) + && (input.borrow().get_option("all").as_bool() == Some(true) + || input.borrow().get_option("available").as_bool() == Some(true) + || input.borrow().get_option("platform").as_bool() == Some(true)) { self.get_io().write_error("The --direct (-D) option is not usable in combination with --all, --platform (-p) or --available (-a)"); return Ok(1); } - if input.get_option("tree").as_bool() == Some(true) - && (input.get_option("all").as_bool() == Some(true) - || input.get_option("available").as_bool() == Some(true)) + if input.borrow().get_option("tree").as_bool() == Some(true) + && (input.borrow().get_option("all").as_bool() == Some(true) + || input.borrow().get_option("available").as_bool() == Some(true)) { self.get_io().write_error("The --tree (-t) option is not usable in combination with --all or --available (-a)"); @@ -120,9 +128,9 @@ impl ShowCommand { } let only_count: usize = [ - input.get_option("patch-only").as_bool() == Some(true), - input.get_option("minor-only").as_bool() == Some(true), - input.get_option("major-only").as_bool() == Some(true), + input.borrow().get_option("patch-only").as_bool() == Some(true), + input.borrow().get_option("minor-only").as_bool() == Some(true), + input.borrow().get_option("major-only").as_bool() == Some(true), ] .iter() .filter(|b| **b) @@ -135,8 +143,8 @@ impl ShowCommand { return Ok(1); } - if input.get_option("tree").as_bool() == Some(true) - && input.get_option("latest").as_bool() == Some(true) + if input.borrow().get_option("tree").as_bool() == Some(true) + && input.borrow().get_option("latest").as_bool() == Some(true) { self.get_io().write_error( "The --tree (-t) option is not usable in combination with --latest (-l)", @@ -145,8 +153,8 @@ impl ShowCommand { return Ok(1); } - if input.get_option("tree").as_bool() == Some(true) - && input.get_option("path").as_bool() == Some(true) + if input.borrow().get_option("tree").as_bool() == Some(true) + && input.borrow().get_option("path").as_bool() == Some(true) { self.get_io().write_error( "The --tree (-t) option is not usable in combination with --path (-P)", @@ -156,6 +164,7 @@ impl ShowCommand { } let format = input + .borrow() .get_option("format") .as_string() .unwrap_or("text") @@ -176,7 +185,7 @@ impl ShowCommand { return Ok(1); } - let platform_req_filter = self.get_platform_requirement_filter(input)?; + let platform_req_filter = self.get_platform_requirement_filter(input.clone())?; // init repos let mut platform_overrides: IndexMap<String, PhpMixed> = IndexMap::new(); @@ -207,20 +216,20 @@ impl ShowCommand { let installed_repo: RepositoryInterfaceHandle; let repos: RepositoryInterfaceHandle; - if input.get_option("self").as_bool() == Some(true) - && input.get_option("installed").as_bool() != Some(true) - && input.get_option("locked").as_bool() != Some(true) + if input.borrow().get_option("self").as_bool() == Some(true) + && input.borrow().get_option("installed").as_bool() != Some(true) + && input.borrow().get_option("locked").as_bool() != Some(true) { let composer = self.require_composer(None, None)?; let package = crate::package::RootPackageInterfaceHandle::dup( composer.borrow_partial().get_package(), ); - if input.get_option("name-only").as_bool() == Some(true) { + if input.borrow().get_option("name-only").as_bool() == Some(true) { self.get_io().write(&package.get_name()); return Ok(0); } - if input.get_argument("package").as_string().is_some() { + if input.borrow().get_argument("package").as_string().is_some() { return Err(InvalidArgumentException { message: "You cannot use --self together with a package name".to_string(), code: 0, @@ -234,14 +243,14 @@ impl ShowCommand { RepositoryInterfaceHandle::new(RootPackageRepository::new(package.clone())), ])); single_package = Some(package.clone().into()); - } else if input.get_option("platform").as_bool() == Some(true) { + } else if input.borrow().get_option("platform").as_bool() == Some(true) { installed_repo = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ RepositoryInterfaceHandle::new(make_platform_repo()?), ])); repos = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ RepositoryInterfaceHandle::new(make_platform_repo()?), ])); - } else if input.get_option("available").as_bool() == Some(true) { + } else if input.borrow().get_option("available").as_bool() == Some(true) { let mut ir = InstalledRepository::new(vec![RepositoryInterfaceHandle::new( make_platform_repo()?, )]); @@ -276,7 +285,7 @@ impl ShowCommand { )); installed_repo = RepositoryInterfaceHandle::new(ir); } - } else if input.get_option("all").as_bool() == Some(true) && composer.is_some() { + } else if input.borrow().get_option("all").as_bool() == Some(true) && composer.is_some() { let mut composer_ref = crate::command::composer_full_mut(composer.as_ref().unwrap()); let local_repo = composer_ref .get_repository_manager() @@ -316,7 +325,7 @@ impl ShowCommand { composite_input.push(r.clone()); } repos = RepositoryInterfaceHandle::new(CompositeRepository::new(composite_input)); - } else if input.get_option("all").as_bool() == Some(true) { + } else if input.borrow().get_option("all").as_bool() == Some(true) { let default_repos = RepositoryFactory::default_repos_with_default_manager(self.get_io())?; let names: Vec<String> = default_repos.keys().cloned().collect(); @@ -332,7 +341,7 @@ impl ShowCommand { composite_input.push(v); } repos = RepositoryInterfaceHandle::new(CompositeRepository::new(composite_input)); - } else if input.get_option("locked").as_bool() == Some(true) { + } else if input.borrow().get_option("locked").as_bool() == Some(true) { if composer.is_none() || !crate::command::composer_full_mut(composer.as_ref().unwrap()) .get_locker() @@ -348,10 +357,11 @@ impl ShowCommand { let mut composer_ref = crate::command::composer_full_mut(composer.as_ref().unwrap()); let locker_rc = composer_ref.get_locker().clone(); let mut locker = locker_rc.borrow_mut(); - let lr = - locker.get_locked_repository(input.get_option("no-dev").as_bool() != Some(true))?; + let lr = locker.get_locked_repository( + input.borrow().get_option("no-dev").as_bool() != Some(true), + )?; let lr_handle: RepositoryInterfaceHandle = lr.into(); - if input.get_option("self").as_bool() == Some(true) { + if input.borrow().get_option("self").as_bool() == Some(true) { // TODO(phase-b): LockArrayRepository needs add_package via WritableRepositoryInterface; // skipping the insertion here keeps compile clean. let _ = &lr_handle; @@ -382,16 +392,17 @@ impl ShowCommand { }; let root_pkg = composer_local.get_package(); - let root_repo: RepositoryInterfaceHandle = if input.get_option("self").as_bool() - == Some(true) - { - RepositoryInterfaceHandle::new(RootPackageRepository::new( - crate::package::RootPackageInterfaceHandle::dup(composer_local.get_package()), - )) - } else { - RepositoryInterfaceHandle::new(InstalledArrayRepository::new()?) - }; - if input.get_option("no-dev").as_bool() == Some(true) { + let root_repo: RepositoryInterfaceHandle = + if input.borrow().get_option("self").as_bool() == Some(true) { + RepositoryInterfaceHandle::new(RootPackageRepository::new( + crate::package::RootPackageInterfaceHandle::dup( + composer_local.get_package(), + ), + )) + } else { + RepositoryInterfaceHandle::new(InstalledArrayRepository::new()?) + }; + if input.borrow().get_option("no-dev").as_bool() == Some(true) { let local_packages = composer_local .get_repository_manager() .borrow() @@ -452,7 +463,7 @@ impl ShowCommand { let command_event = CommandEvent::new6( PluginEvents::COMMAND, "show", - input, + input.clone(), output, vec![], IndexMap::new(), @@ -466,14 +477,17 @@ impl ShowCommand { .dispatch(Some(&_event_name), None)?; } - if input.get_option("latest").as_bool() == Some(true) && composer.is_none() { + if input.borrow().get_option("latest").as_bool() == Some(true) && composer.is_none() { self.get_io().write_error( "No composer.json found in the current directory, disabling \"latest\" option", ); - input.set_option("latest", PhpMixed::Bool(false)); + input + .borrow_mut() + .set_option("latest", PhpMixed::Bool(false)); } let package_filter: Option<String> = input + .borrow() .get_argument("package") .as_string() .map(|s| s.to_string()); @@ -487,11 +501,11 @@ impl ShowCommand { &*installed_repo.borrow(), &repos, pf, - input.get_argument("version"), + input.borrow().get_argument("version"), )?; if let Some(ref pkg) = matched_package { - if input.get_option("direct").as_bool() == Some(true) { + if input.borrow().get_option("direct").as_bool() == Some(true) { if !in_array( PhpMixed::String(pkg.get_name()), &PhpMixed::List( @@ -515,9 +529,9 @@ impl ShowCommand { } if matched_package.is_none() { - let options = input.get_options(); + let options = input.borrow().get_options(); let mut hint = String::new(); - if input.get_option("locked").as_bool() == Some(true) { + if input.borrow().get_option("locked").as_bool() == Some(true) { hint.push_str(" in lock file"); } if options.contains_key("working-dir") { @@ -530,12 +544,12 @@ impl ShowCommand { )); } if PlatformRepository::is_platform_package(pf) - && input.get_option("platform").as_bool() != Some(true) + && input.borrow().get_option("platform").as_bool() != Some(true) { hint.push_str(", try using --platform (-p) to show platform packages"); } - if input.get_option("all").as_bool() != Some(true) - && input.get_option("available").as_bool() != Some(true) + if input.borrow().get_option("all").as_bool() != Some(true) + && input.borrow().get_option("available").as_bool() != Some(true) { hint.push_str( ", try using --available (-a) to show all available packages", @@ -557,7 +571,7 @@ impl ShowCommand { // assert(isset($versions)); let mut exit_code: i64 = 0; - if input.get_option("tree").as_bool() == Some(true) { + if input.borrow().get_option("tree").as_bool() == Some(true) { let array_tree = self.generate_package_tree( package.clone().into(), &*installed_repo.borrow(), @@ -586,19 +600,31 @@ impl ShowCommand { } let mut latest_package: Option<crate::package::PackageInterfaceHandle> = None; - if input.get_option("latest").as_bool() == Some(true) { + if input.borrow().get_option("latest").as_bool() == Some(true) { latest_package = self.find_latest_package( package.clone().into(), composer.as_ref().unwrap(), &mut platform_repo, - input.get_option("major-only").as_bool().unwrap_or(false), - input.get_option("minor-only").as_bool().unwrap_or(false), - input.get_option("patch-only").as_bool().unwrap_or(false), + input + .borrow() + .get_option("major-only") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("minor-only") + .as_bool() + .unwrap_or(false), + input + .borrow() + .get_option("patch-only") + .as_bool() + .unwrap_or(false), &*platform_req_filter, )?; } - if input.get_option("outdated").as_bool() == Some(true) - && input.get_option("strict").as_bool() == Some(true) + if input.borrow().get_option("outdated").as_bool() == Some(true) + && input.borrow().get_option("strict").as_bool() == Some(true) && latest_package.is_some() && latest_package .as_ref() @@ -614,7 +640,7 @@ impl ShowCommand { { exit_code = 1; } - if input.get_option("path").as_bool() == Some(true) { + if input.borrow().get_option("path").as_bool() == Some(true) { self.get_io().write_no_newline(&package.get_name()); let path = { let composer_ref = composer.as_ref().unwrap(); @@ -654,7 +680,7 @@ impl ShowCommand { } // show tree view if requested - if input.get_option("tree").as_bool() == Some(true) { + if input.borrow().get_option("tree").as_bool() == Some(true) { let root_requires = self.get_root_requires(); let mut packages = installed_repo.get_packages()?; packages.sort_by(|a, b| { @@ -716,15 +742,15 @@ impl ShowCommand { } let mut package_list_filter: Option<Vec<String>> = None; - if input.get_option("direct").as_bool() == Some(true) { + if input.borrow().get_option("direct").as_bool() == Some(true) { package_list_filter = Some(self.get_root_requires()); } - if input.get_option("path").as_bool() == Some(true) && composer.is_none() { + if input.borrow().get_option("path").as_bool() == Some(true) && composer.is_none() { self.get_io().write_error( "No composer.json found in the current directory, disabling \"path\" option", ); - input.set_option("path", PhpMixed::Bool(false)); + input.borrow_mut().set_option("path", PhpMixed::Bool(false)); } for repo in RepositoryUtils::flatten_repositories(repos.clone(), false) { @@ -799,13 +825,14 @@ impl ShowCommand { } } - let show_all_types = input.get_option("all").as_bool() == Some(true); - let show_latest = input.get_option("latest").as_bool() == Some(true); - let show_major_only = input.get_option("major-only").as_bool() == Some(true); - 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 show_all_types = input.borrow().get_option("all").as_bool() == Some(true); + let show_latest = input.borrow().get_option("latest").as_bool() == Some(true); + let show_major_only = input.borrow().get_option("major-only").as_bool() == Some(true); + let show_minor_only = input.borrow().get_option("minor-only").as_bool() == Some(true); + let show_patch_only = input.borrow().get_option("patch-only").as_bool() == Some(true); let ignored_packages_regex = base_package::package_names_to_regexp( &input + .borrow() .get_option("ignore") .as_list() .map(|l| { @@ -865,21 +892,21 @@ impl ShowCommand { } } - let write_path = input.get_option("name-only").as_bool() != Some(true) - && input.get_option("path").as_bool() == Some(true); - write_version = input.get_option("name-only").as_bool() != Some(true) - && input.get_option("path").as_bool() != Some(true) + let write_path = input.borrow().get_option("name-only").as_bool() != Some(true) + && input.borrow().get_option("path").as_bool() == Some(true); + write_version = input.borrow().get_option("name-only").as_bool() != Some(true) + && input.borrow().get_option("path").as_bool() != Some(true) && *show_version; let write_latest = write_version && show_latest; - write_description = input.get_option("name-only").as_bool() != Some(true) - && input.get_option("path").as_bool() != Some(true); + write_description = input.borrow().get_option("name-only").as_bool() != Some(true) + && input.borrow().get_option("path").as_bool() != Some(true); let write_release_date = write_latest - && (input.get_option("sort-by-age").as_bool() == Some(true) + && (input.borrow().get_option("sort-by-age").as_bool() == Some(true) || format == "json"); let mut has_outdated_packages = false; - if input.get_option("sort-by-age").as_bool() == Some(true) { + if input.borrow().get_option("sort-by-age").as_bool() == Some(true) { type_packages.sort_by(|_ka, a, _kb, b| match (a, b) { (PackageOrName::Pkg(a), PackageOrName::Pkg(b)) => { a.get_release_date().cmp(&b.get_release_date()) @@ -917,14 +944,14 @@ impl ShowCommand { package_is_up_to_date || (latest_package.is_none() && show_major_only); let package_is_ignored = Preg::is_match(&ignored_packages_regex, &package.get_pretty_name())?; - if input.get_option("outdated").as_bool() == Some(true) + if input.borrow().get_option("outdated").as_bool() == Some(true) && (package_is_up_to_date || package_is_ignored) { continue; } - if input.get_option("outdated").as_bool() == Some(true) - || input.get_option("strict").as_bool() == Some(true) + if input.borrow().get_option("outdated").as_bool() == Some(true) + || input.borrow().get_option("strict").as_bool() == Some(true) { has_outdated_packages = true; } @@ -946,7 +973,8 @@ impl ShowCommand { true, )), ); - if format != "json" || input.get_option("name-only").as_bool() != Some(true) + if format != "json" + || input.borrow().get_option("name-only").as_bool() != Some(true) { package_view_data.insert( "homepage".to_string(), @@ -1122,7 +1150,9 @@ impl ShowCommand { write_release_date, }, ); - if input.get_option("strict").as_bool() == Some(true) && has_outdated_packages { + if input.borrow().get_option("strict").as_bool() == Some(true) + && has_outdated_packages + { exit_code = 1; break; } @@ -1155,7 +1185,7 @@ impl ShowCommand { .collect(), ))); } else { - if input.get_option("latest").as_bool() == Some(true) + if input.borrow().get_option("latest").as_bool() == Some(true) && view_data.values().any(|v| !v.is_empty()) { let io = self.get_io(); @@ -1163,7 +1193,7 @@ impl ShowCommand { io.write_error("Legend:"); io.write_error("! patch or minor release available - update recommended"); io.write_error("~ major release available - update possible"); - if input.get_option("outdated").as_bool() != Some(true) { + if input.borrow().get_option("outdated").as_bool() != Some(true) { io.write_error("= up to date version"); } } else { @@ -1172,7 +1202,7 @@ impl ShowCommand { io.write_error( "- <comment>major</comment> release available - update possible", ); - if input.get_option("outdated").as_bool() != Some(true) { + if input.borrow().get_option("outdated").as_bool() != Some(true) { io.write_error("- <info>up to date</info> version"); } } @@ -1215,7 +1245,7 @@ impl ShowCommand { } } - if write_latest && input.get_option("direct").as_bool() != Some(true) { + if write_latest && input.borrow().get_option("direct").as_bool() != Some(true) { let mut direct_deps: Vec<IndexMap<String, PhpMixed>> = Vec::new(); let mut transitive_deps: Vec<IndexMap<String, PhpMixed>> = Vec::new(); for pkg in packages.iter() { @@ -2186,7 +2216,10 @@ impl ShowCommand { } /// Init styles for tree - pub(crate) fn init_styles(&mut self, output: &mut dyn OutputInterface) { + pub(crate) fn init_styles( + &mut self, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { self.colors = vec![ "green".to_string(), "yellow".to_string(), @@ -2200,7 +2233,7 @@ impl ShowCommand { // TODO(phase-b): OutputInterface::get_formatter returns &OutputFormatter, but // set_style requires &mut. Resolution requires interior-mutability refactor of // OutputFormatter wiring across symfony shim. - let _ = (output.get_formatter(), color); + let _ = (output.borrow().get_formatter(), color); } } diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs index f651e59..0c9abc9 100644 --- a/crates/shirabe/src/command/status_command.rs +++ b/crates/shirabe/src/command/status_command.rs @@ -41,15 +41,16 @@ impl StatusCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer_rc = self.require_composer(None, None)?; { let composer = crate::command::composer_full(&composer_rc); // TODO(plugin): dispatch CommandEvent - let command_event = CommandEvent::new(PluginEvents::COMMAND, "status", input, output); + let command_event = + CommandEvent::new(PluginEvents::COMMAND, "status", input.clone(), output); composer .get_event_dispatcher() .borrow_mut() @@ -84,7 +85,10 @@ impl StatusCommand { Ok(exit_code) } - fn do_execute(&mut self, input: &dyn InputInterface) -> Result<i64> { + fn do_execute( + &mut self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + ) -> Result<i64> { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); let io = self.get_io().clone(); @@ -212,7 +216,12 @@ impl StatusCommand { io.write_error("<error>You have changes in the following dependencies:</error>"); for (path, changes) in &errors { - if input.get_option("verbose").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false) + { let indented_changes = changes .lines() .map(|line| format!(" {}", line.trim_start())) @@ -230,7 +239,12 @@ impl StatusCommand { io.write_error("<warning>You have unpushed changes on the current branch in the following dependencies:</warning>"); for (path, changes) in &unpushed_changes { - if input.get_option("verbose").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false) + { let indented_changes = changes .lines() .map(|line| format!(" {}", line.trim_start())) @@ -250,7 +264,12 @@ impl StatusCommand { ); for (path, changes) in &vcs_version_changes { - if input.get_option("verbose").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false) + { let current_version = { let v = changes["current"] .get("version") @@ -311,7 +330,11 @@ impl StatusCommand { } if (!errors.is_empty() || !unpushed_changes.is_empty() || !vcs_version_changes.is_empty()) - && !input.get_option("verbose").as_bool().unwrap_or(false) + && !input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false) { io.write_error("Use --verbose (-v) to see a list of files"); } diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs index 92a4288..d050ec7 100644 --- a/crates/shirabe/src/command/suggests_command.rs +++ b/crates/shirabe/src/command/suggests_command.rs @@ -42,8 +42,8 @@ impl SuggestsCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); @@ -65,10 +65,13 @@ impl SuggestsCommand { vec![], platform_overrides, )?)); - let locked_repo = composer - .get_locker() - .borrow_mut() - .get_locked_repository(!input.get_option("no-dev").as_bool().unwrap_or(false))?; + let locked_repo = composer.get_locker().borrow_mut().get_locked_repository( + !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false), + )?; installed_repos.push(locked_repo.into()); } else { // TODO(phase-b): Config::get returns PhpMixed; need to coerce to IndexMap<String, PhpMixed> @@ -90,7 +93,7 @@ impl SuggestsCommand { let mut installed_repo = InstalledRepository::new(installed_repos); let mut reporter = SuggestedPackagesReporter::new(self.get_io().clone()); - let filter = input.get_argument("packages"); + let filter = input.borrow().get_argument("packages"); let mut packages = RepositoryInterface::get_packages(&mut installed_repo)?; let root_pkg_as_base: crate::package::BasePackageHandle = composer.get_package().clone().into(); @@ -104,18 +107,28 @@ impl SuggestsCommand { let mut mode = SuggestedPackagesReporter::MODE_BY_PACKAGE; - if input.get_option("by-suggestion").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("by-suggestion") + .as_bool() + .unwrap_or(false) + { mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION; } - if input.get_option("by-package").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("by-package") + .as_bool() + .unwrap_or(false) + { mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE; } - if input.get_option("list").as_bool().unwrap_or(false) { + if input.borrow().get_option("list").as_bool().unwrap_or(false) { mode = SuggestedPackagesReporter::MODE_LIST; } let only_dependents_of: Option<crate::package::PackageInterfaceHandle> = - if empty(&filter) && !input.get_option("all").as_bool().unwrap_or(false) { + if empty(&filter) && !input.borrow().get_option("all").as_bool().unwrap_or(false) { Some(composer.get_package().clone().into()) } else { None diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index e28d407..7731e4f 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -73,18 +73,23 @@ impl UpdateCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let io = self.get_io().clone(); - if input.get_option("dev").as_bool().unwrap_or(false) { + if input.borrow().get_option("dev").as_bool().unwrap_or(false) { io.write_error3( "<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>", true, io_interface::NORMAL, ); } - if input.get_option("no-suggest").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-suggest") + .as_bool() + .unwrap_or(false) + { io.write_error3( "<warning>You are using the deprecated option \"--no-suggest\". It has no effect and will break in Composer 3.</warning>", true, @@ -104,6 +109,7 @@ impl UpdateCommand { } let mut packages: Vec<String> = input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -114,6 +120,7 @@ impl UpdateCommand { .unwrap_or_default(); let mut reqs: IndexMap<String, String> = self.format_requirements( input + .borrow() .get_option("with") .as_list() .map(|l| { @@ -200,7 +207,12 @@ impl UpdateCommand { } } - if input.get_option("patch-only").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("patch-only") + .as_bool() + .unwrap_or(false) + { if !composer.get_locker().borrow_mut().is_locked() { return Err(InvalidArgumentException { message: "patch-only can only be used with a lock file present".to_string(), @@ -245,7 +257,12 @@ impl UpdateCommand { } } - if input.get_option("interactive").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("interactive") + .as_bool() + .unwrap_or(false) + { packages = self.get_packages_interactively( io.clone(), input, @@ -255,9 +272,19 @@ impl UpdateCommand { )?; } - if input.get_option("root-reqs").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("root-reqs") + .as_bool() + .unwrap_or(false) + { let mut requires: Vec<String> = array_keys(&root_package.get_requires()); - if !input.get_option("no-dev").as_bool().unwrap_or(false) { + if !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false) + { requires = array_merge( // TODO(phase-b): array_merge for Vec<String> todo!("requires as PhpMixed"), @@ -292,7 +319,7 @@ impl UpdateCommand { true, ) }); - let update_mirrors = input.get_option("lock").as_bool().unwrap_or(false) + let update_mirrors = input.borrow().get_option("lock").as_bool().unwrap_or(false) || filtered_packages.len() != packages.len(); packages = filtered_packages; @@ -313,7 +340,13 @@ impl UpdateCommand { composer .get_installation_manager() .borrow_mut() - .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); + .set_output_progress( + !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + ); let mut install = Installer::create(io.clone(), &composer_handle); @@ -322,6 +355,7 @@ impl UpdateCommand { self.get_preferred_install_options(&*config.borrow(), input, false)?; let optimize = input + .borrow() .get_option("optimize-autoloader") .as_bool() .unwrap_or(false) @@ -331,6 +365,7 @@ impl UpdateCommand { .as_bool() .unwrap_or(false); let authoritative = input + .borrow() .get_option("classmap-authoritative") .as_bool() .unwrap_or(false) @@ -340,11 +375,13 @@ impl UpdateCommand { .as_bool() .unwrap_or(false); let apcu_prefix: Option<String> = input + .borrow() .get_option("apcu-autoloader-prefix") .as_string() .map(|s| s.to_string()); let apcu = apcu_prefix.is_some() || input + .borrow() .get_option("apcu-autoloader") .as_bool() .unwrap_or(false) @@ -354,6 +391,7 @@ impl UpdateCommand { .as_bool() .unwrap_or(false); let minimal_changes = input + .borrow() .get_option("minimal-changes") .as_bool() .unwrap_or(false) @@ -365,12 +403,14 @@ impl UpdateCommand { let mut update_allow_transitive_dependencies: i64 = Request::UPDATE_ONLY_LISTED; if input + .borrow() .get_option("with-all-dependencies") .as_bool() .unwrap_or(false) { update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; } else if input + .borrow() .get_option("with-dependencies") .as_bool() .unwrap_or(false) @@ -382,23 +422,65 @@ impl UpdateCommand { let _ = UpdateAllowTransitiveDeps::UpdateOnlyListed; install - .set_dry_run(input.get_option("dry-run").as_bool().unwrap_or(false)) - .set_verbose(input.get_option("verbose").as_bool().unwrap_or(false)) + .set_dry_run( + input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false), + ) + .set_verbose( + input + .borrow() + .get_option("verbose") + .as_bool() + .unwrap_or(false), + ) .set_prefer_source(prefer_source) .set_prefer_dist(prefer_dist) - .set_dev_mode(!input.get_option("no-dev").as_bool().unwrap_or(false)) - .set_dump_autoloader(!input.get_option("no-autoloader").as_bool().unwrap_or(false)) + .set_dev_mode( + !input + .borrow() + .get_option("no-dev") + .as_bool() + .unwrap_or(false), + ) + .set_dump_autoloader( + !input + .borrow() + .get_option("no-autoloader") + .as_bool() + .unwrap_or(false), + ) .set_optimize_autoloader(optimize) .set_class_map_authoritative(authoritative) .set_apcu_autoloader(apcu, apcu_prefix.clone()) .set_update(true) - .set_install(!input.get_option("no-install").as_bool().unwrap_or(false)) + .set_install( + !input + .borrow() + .get_option("no-install") + .as_bool() + .unwrap_or(false), + ) .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.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_prefer_stable( + input + .borrow() + .get_option("prefer-stable") + .as_bool() + .unwrap_or(false), + ) + .set_prefer_lowest( + input + .borrow() + .get_option("prefer-lowest") + .as_bool() + .unwrap_or(false), + ) // TODO(phase-b): VersionParser::parse_constraints returns Arc<dyn ...> but // Installer::set_temporary_constraints expects IndexMap<String, Box<dyn ...>>; // bridge the constraint storage types later. @@ -411,14 +493,19 @@ impl UpdateCommand { ) .set_minimal_update(minimal_changes); - if input.get_option("no-plugins").as_bool().unwrap_or(false) { + if input + .borrow() + .get_option("no-plugins") + .as_bool() + .unwrap_or(false) + { install.disable_plugins(); } let mut result = install.run()?; - if result == 0 && !input.get_option("lock").as_bool().unwrap_or(false) { - let mut bump_after_update = input.get_option("bump-after-update"); + if result == 0 && !input.borrow().get_option("lock").as_bool().unwrap_or(false) { + let mut bump_after_update = input.borrow().get_option("bump-after-update"); // PHP: false === $bumpAfterUpdate (strict) if matches!(bump_after_update, PhpMixed::Bool(false)) { bump_after_update = composer.get_config().borrow().get("bump-after-update"); @@ -438,8 +525,13 @@ impl UpdateCommand { io.clone(), bump_after_update.as_string() == Some("dev"), bump_after_update.as_string() == Some("no-dev"), - input.get_option("dry-run").as_bool().unwrap_or(false), input + .borrow() + .get_option("dry-run") + .as_bool() + .unwrap_or(false), + input + .borrow() .get_argument("packages") .as_list() .map(|l| { @@ -461,12 +553,12 @@ impl UpdateCommand { fn get_packages_interactively( &self, io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, composer: &PartialComposerHandle, packages: Vec<String>, ) -> Result<Vec<String>> { - if !input.is_interactive() { + if !input.borrow().is_interactive() { return Err(InvalidArgumentException { message: "--interactive cannot be used in non-interactive terminals.".to_string(), code: 0, diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs index b168b03..eaae2db 100644 --- a/crates/shirabe/src/command/validate_command.rs +++ b/crates/shirabe/src/command/validate_command.rs @@ -110,10 +110,11 @@ impl ValidateCommand { pub fn execute( &mut self, - input: &dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let file = input + .borrow() .get_argument("file") .as_string() .map(|s| s.to_string()) @@ -131,17 +132,28 @@ impl ValidateCommand { } let validator = ConfigValidator::new(io.clone()); - let check_all = if input.get_option("no-check-all").as_bool().unwrap_or(false) { + let check_all = if input + .borrow() + .get_option("no-check-all") + .as_bool() + .unwrap_or(false) + { 0 } else { ValidatingArrayLoader::CHECK_ALL }; let check_publish = !input + .borrow() .get_option("no-check-publish") .as_bool() .unwrap_or(false); - let check_lock = !input.get_option("no-check-lock").as_bool().unwrap_or(false); + let check_lock = !input + .borrow() + .get_option("no-check-lock") + .as_bool() + .unwrap_or(false); let check_version = if input + .borrow() .get_option("no-check-version") .as_bool() .unwrap_or(false) @@ -150,12 +162,17 @@ impl ValidateCommand { } else { ConfigValidator::CHECK_VERSION }; - let is_strict = input.get_option("strict").as_bool().unwrap_or(false); + let is_strict = input + .borrow() + .get_option("strict") + .as_bool() + .unwrap_or(false); let (mut errors, mut publish_errors, mut warnings) = validator.validate(&file, check_all, check_version); let mut lock_errors: Vec<String> = vec![]; - let composer = self.create_composer_instance(input, io.clone(), None, false, None)?; + let composer = + self.create_composer_instance(input.clone(), io.clone(), None, false, None)?; let mut composer = crate::command::composer_full_mut(&composer); let check_lock = (check_lock && composer @@ -164,7 +181,11 @@ impl ValidateCommand { .get("lock") .as_bool() .unwrap_or(true)) - || input.get_option("check-lock").as_bool().unwrap_or(false); + || input + .borrow() + .get_option("check-lock") + .as_bool() + .unwrap_or(false); // TODO(phase-b): get_missing_requirement_info needs &package from composer while // locker holds &mut composer; cloning lock state isn't trivial. Use todo!() for the // package-arg subexpression below. @@ -202,6 +223,7 @@ impl ValidateCommand { let mut exit_code = exit_code; if input + .borrow() .get_option("with-dependencies") .as_bool() .unwrap_or(false) diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index 8bd34ad..124cc9b 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -165,10 +165,10 @@ impl Application { pub fn run( &mut self, - input: Option<&mut dyn InputInterface>, - output: Option<&mut dyn OutputInterface>, + input: Option<std::rc::Rc<std::cell::RefCell<dyn InputInterface>>>, + output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>, ) -> anyhow::Result<i64> { - // TODO(phase-b): Factory::create_output returns ConsoleOutput, not Box<dyn OutputInterface>. + // TODO(phase-b): Factory::create_output returns ConsoleOutput, not std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>. // The PHP code falls back to a default output when none is supplied; for now we // forward the caller-provided output as-is. self.inner.run(input, output) @@ -176,11 +176,15 @@ impl Application { pub fn do_run( &mut self, - input: &mut dyn InputInterface, - output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> anyhow::Result<i64> { - self.disable_plugins_by_default = input.has_parameter_option(&["--no-plugins"], false); - self.disable_scripts_by_default = input.has_parameter_option(&["--no-scripts"], false); + self.disable_plugins_by_default = input + .borrow() + .has_parameter_option(&["--no-plugins"], false); + self.disable_scripts_by_default = input + .borrow() + .has_parameter_option(&["--no-scripts"], false); // PHP: static $stdin = null; // We use an Option here to mimic the lazy initialization. @@ -196,7 +200,7 @@ impl Application { || matches!(stdin, PhpMixed::Null) || !Platform::is_tty(Some(stdin))) { - input.set_interactive(false); + input.borrow_mut().set_interactive(false); } let mut helpers: Vec<PhpMixed> = vec![]; @@ -217,7 +221,7 @@ impl Application { // not a borrow; passing None until the IO sharing story is settled. ErrorHandler::register(None); - if input.has_parameter_option(&["--no-cache"], false) { + if input.borrow().has_parameter_option(&["--no-cache"], false) { self.io .write_error3("Disabling cache usage", true, io_interface::DEBUG); Platform::put_env( @@ -231,7 +235,7 @@ impl Application { } // switch working dir - let new_work_dir = self.get_new_working_dir(input)?; + let new_work_dir = self.get_new_working_dir(input.clone())?; let mut old_working_dir: Option<String> = None; if let Some(ref nwd) = new_work_dir { old_working_dir = Some(Platform::get_cwd(true).unwrap_or_default()); @@ -254,7 +258,7 @@ impl Application { // determine command name to be executed without including plugin commands let mut command_name: Option<String> = Some(String::new()); - let raw_command_name = self.get_command_name_before_binding(input); + let raw_command_name = self.get_command_name_before_binding(input.clone()); if let Some(ref raw) = raw_command_name { match self.inner.find(raw) { Ok(cmd) => { @@ -302,10 +306,10 @@ impl Application { && !file_exists(&Factory::get_composer_file().unwrap_or_default()) && use_parent_dir_if_no_json_available.as_bool() != Some(false) && (command_name.as_deref() != Some("config") - || (input.has_parameter_option(&["--file"], true) == false - && input.has_parameter_option(&["-f"], true) == false)) - && input.has_parameter_option(&["--help"], true) == false - && input.has_parameter_option(&["-h"], true) == false + || (input.borrow().has_parameter_option(&["--file"], true) == false + && input.borrow().has_parameter_option(&["-f"], true) == false)) + && input.borrow().has_parameter_option(&["--help"], true) == false + && input.borrow().has_parameter_option(&["-h"], true) == false { let mut dir = dirname(&Platform::get_cwd(true).unwrap_or_default()); let home_value = Platform::get_env("HOME") @@ -386,7 +390,9 @@ impl Application { Box::new(PhpMixed::String("list".to_string())), Box::new(PhpMixed::String("help".to_string())), ]); - let may_need_plugin_command = !input.has_parameter_option(&["--version", "-V"], false) + let may_need_plugin_command = !input + .borrow() + .has_parameter_option(&["--version", "-V"], false) && (command_name.is_none() || in_array( command_name.as_deref().unwrap_or("").into(), @@ -478,7 +484,7 @@ impl Application { // determine command name to be executed incl plugin commands, and check if it's a proxy command let is_proxy_command = false; - if let Some(ref name) = self.get_command_name_before_binding(input) { + if let Some(ref name) = self.get_command_name_before_binding(input.clone()) { if let Ok(command) = self.inner.find(name) { // TODO(phase-b): BaseApplication::find returns PhpMixed; we cannot yet // extract a typed command name or detect proxy commands without the @@ -714,7 +720,7 @@ impl Application { let mut start_time: Option<f64> = None; let result_outcome: anyhow::Result<i64> = (|| -> anyhow::Result<i64> { - if input.has_parameter_option(&["--profile"], false) { + if input.borrow().has_parameter_option(&["--profile"], false) { start_time = Some(microtime(true)); // TODO(phase-b): enable_debugging is defined only on ConsoleIO, not // through IOInterface. Skip until the IO concrete type is known here. @@ -724,7 +730,10 @@ impl Application { // TODO(phase-b): BaseApplication exposes only `run`, not `do_run`. let result: i64 = todo!("BaseApplication::do_run"); - if input.has_parameter_option(&["--version", "-V"], true) { + if input + .borrow() + .has_parameter_option(&["--version", "-V"], true) + { self.io.write_error(&sprintf( "<info>PHP</info> version <comment>%s</comment> (%s)", &[PHP_VERSION.into(), PHP_BINARY.into()], @@ -780,20 +789,6 @@ impl Application { self.hint_common_errors(&e, output); - // TODO(phase-b): method_exists/as_any on the inner application and - // output trait objects are not yet supported; replicate the catch-all - // branch unconditionally. - if false { - let _ = <dyn ConsoleOutputInterface>::is_console_output_interface; - // self.inner.render_throwable expects &mut dyn OutputInterface. - // Skipped while output is &dyn OutputInterface here. - let code = e - .downcast_ref::<RuntimeException>() - .map(|r| r.code) - .unwrap_or(0); - return Ok(max_i64(1, code)); - } - // override TransportException's code for the purpose of parent::run() using it as process exit code // as http error codes are all beyond the 255 range of permitted exit codes if e.downcast_ref::<TransportException>().is_some() { @@ -814,8 +809,12 @@ impl Application { outcome } - fn get_new_working_dir(&self, input: &dyn InputInterface) -> anyhow::Result<Option<String>> { + fn get_new_working_dir( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + ) -> anyhow::Result<Option<String>> { let working_dir = input + .borrow() .get_parameter_option(&["--working-dir", "-d"], PhpMixed::Null, true) .as_string() .map(|s| s.to_string()); @@ -835,10 +834,18 @@ impl Application { Ok(working_dir) } - fn hint_common_errors(&mut self, exception: &anyhow::Error, output: &dyn OutputInterface) { + fn hint_common_errors( + &mut self, + exception: &anyhow::Error, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, + ) { let is_logic_or_error = exception.downcast_ref::<ShimLogicException>().is_some(); - if is_logic_or_error && output.get_verbosity() < output_interface::VERBOSITY_VERBOSE { - output.set_verbosity(output_interface::VERBOSITY_VERBOSE); + if is_logic_or_error + && output.borrow().get_verbosity() < output_interface::VERBOSITY_VERBOSE + { + output + .borrow_mut() + .set_verbosity(output_interface::VERBOSITY_VERBOSE); } Silencer::suppress(None); @@ -1051,7 +1058,10 @@ impl Application { } /// This ensures we can find the correct command name even if a global input option is present before it - fn get_command_name_before_binding(&self, input: &dyn InputInterface) -> Option<String> { + fn get_command_name_before_binding( + &self, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + ) -> Option<String> { let mut input = clone(&input); // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. // TODO(phase-b): BaseApplication::get_definition returns PhpMixed, not InputDefinition. diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs index 7122a4b..bea2e76 100644 --- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs +++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs @@ -627,7 +627,10 @@ impl EventDispatcher { .to_string(); let mut input = StringInput::new(&input_str); let mut output = output; - Ok(app.run(Some(&mut input), Some(&mut output))?) + Ok(app.run( + Some(std::rc::Rc::new(std::cell::RefCell::new(input))), + Some(std::rc::Rc::new(std::cell::RefCell::new(output))), + )?) })(); match result { Ok(v) => r#return = v, diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index 836fd95..0696c1c 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -44,7 +44,8 @@ impl BufferIO { // Real fix requires unifying the two crate paths. let _ = formatter; let _ = StreamOutput::new(stream, verbosity, Some(decorated)); - let output: Box<dyn OutputInterface> = todo!("StreamOutput as Box<dyn OutputInterface>"); + let output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> = + todo!("StreamOutput as std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>"); // TODO(phase-b): symfony console helper modules live under both `symfony::console` // and `symfony::component::console`; QuestionHelper::new is not yet provided. diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index 44e5609..edb0a97 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -34,7 +34,7 @@ pub struct ConsoleIO { authentications: indexmap::IndexMap<String, indexmap::IndexMap<String, Option<String>>>, pub(crate) input: Box<dyn InputInterface>, - pub(crate) output: Box<dyn OutputInterface>, + pub(crate) output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, pub(crate) helper_set: HelperSet, pub(crate) last_message: RefCell<String>, pub(crate) last_message_err: RefCell<String>, @@ -68,7 +68,7 @@ impl ConsoleIO { /// @param HelperSet $helperSet The helperSet instance pub fn new( input: Box<dyn InputInterface>, - output: Box<dyn OutputInterface>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, helper_set: HelperSet, ) -> Self { let mut verbosity_map = IndexMap::new(); @@ -99,7 +99,7 @@ impl ConsoleIO { /// @param string[]|string $messages fn do_write(&self, messages: PhpMixed, newline: bool, stderr: bool, verbosity: i64, raw: bool) { let mut sf_verbosity = *self.verbosity_map.get(&verbosity).unwrap_or(&0); - if sf_verbosity > self.output.get_verbosity() { + if sf_verbosity > self.output.borrow().get_verbosity() { return; } @@ -142,8 +142,8 @@ impl ConsoleIO { messages }; - if stderr && let Some(console_output) = self.output.as_console_output_interface() { - console_output.get_error_output().write( + if stderr && let Some(console_output) = self.output.borrow().as_console_output_interface() { + console_output.get_error_output().borrow().write( &Self::to_string_list(&messages).join(if newline { "\n" } else { "" }), newline, sf_verbosity, @@ -157,7 +157,7 @@ impl ConsoleIO { return; } - self.output.write( + self.output.borrow().write( &Self::to_string_list(&messages).join(if newline { "\n" } else { "" }), newline, sf_verbosity, @@ -256,15 +256,15 @@ impl ConsoleIO { } pub fn get_table(&self) -> Table { - Table::new(&*self.output) + Table::new(self.output.clone()) } - fn get_error_output(&self) -> &dyn OutputInterface { - if let Some(console_output) = self.output.as_console_output_interface() { + fn get_error_output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> { + if let Some(console_output) = self.output.borrow().as_console_output_interface() { return console_output.get_error_output(); } - &*self.output + self.output.clone() } /// Sanitize string to remove control characters @@ -379,19 +379,19 @@ impl IOInterfaceImmutable for ConsoleIO { } fn is_verbose(&self) -> bool { - self.output.is_verbose() + self.output.borrow().is_verbose() } fn is_very_verbose(&self) -> bool { - self.output.is_very_verbose() + self.output.borrow().is_very_verbose() } fn is_debug(&self) -> bool { - self.output.is_debug() + self.output.borrow().is_debug() } fn is_decorated(&self) -> bool { - self.output.is_decorated() + self.output.borrow().is_decorated() } fn write3(&self, message: &str, newline: bool, verbosity: i64) { diff --git a/crates/shirabe/src/plugin/command_event.rs b/crates/shirabe/src/plugin/command_event.rs index bcfe256..838260c 100644 --- a/crates/shirabe/src/plugin/command_event.rs +++ b/crates/shirabe/src/plugin/command_event.rs @@ -10,25 +10,25 @@ use shirabe_php_shim::PhpMixed; pub struct CommandEvent { inner: Event, command_name: String, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, } impl CommandEvent { - // TODO(phase-b): input/output dropped because storing &dyn references in an event would - // require lifetime parameters; restore once Plugin API needs them. pub fn new( name: &str, command_name: &str, - _input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Self { - Self::new6(name, command_name, _input, _output, vec![], IndexMap::new()) + Self::new6(name, command_name, input, output, vec![], IndexMap::new()) } pub fn new6( name: &str, command_name: &str, - _input: &dyn InputInterface, - _output: &dyn OutputInterface, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, args: Vec<String>, flags: IndexMap<String, PhpMixed>, ) -> Self { @@ -36,9 +36,19 @@ impl CommandEvent { Self { inner, command_name: command_name.to_string(), + input, + output, } } + pub fn get_input(&self) -> std::rc::Rc<std::cell::RefCell<dyn InputInterface>> { + self.input.clone() + } + + pub fn get_output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> { + self.output.clone() + } + pub fn get_name(&self) -> &str { self.inner.get_name() } diff --git a/crates/shirabe/src/plugin/pre_command_run_event.rs b/crates/shirabe/src/plugin/pre_command_run_event.rs index 2af2626..21eb232 100644 --- a/crates/shirabe/src/plugin/pre_command_run_event.rs +++ b/crates/shirabe/src/plugin/pre_command_run_event.rs @@ -7,14 +7,26 @@ use shirabe_external_packages::symfony::component::console::input::InputInterfac #[derive(Debug)] pub struct PreCommandRunEvent { inner: Event, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, command: String, } impl PreCommandRunEvent { - // TODO(phase-b): input dropped because storing a &dyn reference would need lifetime params. - pub fn new(name: String, _input: &dyn InputInterface, command: String) -> Self { + pub fn new( + name: String, + input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, + command: String, + ) -> Self { let inner = Event::new(name, vec![], indexmap::IndexMap::new()); - Self { inner, command } + Self { + inner, + input, + command, + } + } + + pub fn get_input(&self) -> std::rc::Rc<std::cell::RefCell<dyn InputInterface>> { + self.input.clone() } pub fn get_name(&self) -> &str { |
