diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-26 20:04:02 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-26 20:04:02 +0900 |
| commit | f411daceacad66e0bd774fda7d3c5ef8533cc55c (patch) | |
| tree | eefb065e4d676a3f7031ca49bab21c773b00b134 /crates/shirabe | |
| parent | 1921f173ea219cb4b25847294d2d3fa465550fbb (diff) | |
| download | php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.tar.gz php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.tar.zst php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.zip | |
refactor(io): share IOInterface via Rc<RefCell<dyn _>> handle
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe')
110 files changed, 887 insertions, 624 deletions
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index 3786011..59cf6ae 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -25,6 +25,7 @@ use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::installer::InstallationManager; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::json::JsonFile; use crate::package::Locker; @@ -40,7 +41,7 @@ use crate::util::Platform; #[derive(Debug)] pub struct AutoloadGenerator { event_dispatcher: std::rc::Rc<std::cell::RefCell<EventDispatcher>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: Option<bool>, class_map_authoritative: bool, apcu: bool, @@ -53,9 +54,10 @@ pub struct AutoloadGenerator { impl AutoloadGenerator { pub fn new( event_dispatcher: std::rc::Rc<std::cell::RefCell<EventDispatcher>>, - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> Self { - let io: Box<dyn IOInterface> = io.unwrap_or_else(|| Box::new(NullIO::new())); + let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = + io.unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(NullIO::new()))); Self { event_dispatcher, diff --git a/crates/shirabe/src/autoload/class_map_generator.rs b/crates/shirabe/src/autoload/class_map_generator.rs index da086ad..6aa6492 100644 --- a/crates/shirabe/src/autoload/class_map_generator.rs +++ b/crates/shirabe/src/autoload/class_map_generator.rs @@ -6,6 +6,7 @@ use shirabe_class_map_generator::class_map_generator::ClassMapGenerator as Exter use shirabe_php_shim::PhpMixed; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; #[derive(Debug)] pub struct ClassMapGenerator; @@ -41,7 +42,7 @@ impl ClassMapGenerator { pub fn create_map( path: PhpMixed, excluded: Option<String>, - mut io: Option<Box<dyn IOInterface>>, + mut io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, namespace: Option<String>, autoload_type: Option<String>, scanned_files: &mut IndexMap<String, bool>, diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs index c4fd181..c6566f2 100644 --- a/crates/shirabe/src/cache.rs +++ b/crates/shirabe/src/cache.rs @@ -12,6 +12,7 @@ use shirabe_php_shim::{ }; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Filesystem; use crate::util::Platform; use crate::util::Silencer; @@ -19,7 +20,7 @@ use crate::util::Silencer; /// Reads/writes to a filesystem cache #[derive(Debug)] pub struct Cache { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, root: String, enabled: Option<bool>, allowlist: String, @@ -36,7 +37,7 @@ impl Cache { /// @param Filesystem $filesystem optional filesystem instance /// @param bool $readOnly whether the cache is in readOnly mode pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, cache_dir: &str, allowlist: Option<&str>, filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs index ddb9f12..3e74d6e 100644 --- a/crates/shirabe/src/command/about_command.rs +++ b/crates/shirabe/src/command/about_command.rs @@ -6,6 +6,7 @@ use crate::command::HasBaseCommandData; use crate::composer; use crate::composer::ComposerHandle; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use shirabe_external_packages::symfony::component::console::input::InputInterface; use shirabe_external_packages::symfony::component::console::output::OutputInterface; diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index 1b21242..225b092 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -14,6 +14,7 @@ use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::archiver::ArchiveManager; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; @@ -111,9 +112,9 @@ impl ArchiveCommand { }); // TODO(phase-b): clone_box to release self borrow held by get_io. - let mut io_box = self.get_io().clone_box(); + let io_box = self.get_io().clone(); let return_code = self.archive( - io_box.as_mut(), + io_box.clone(), &config, input .get_argument("package") @@ -153,7 +154,7 @@ impl ArchiveCommand { pub fn archive( &mut self, - io: &mut dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, package_name: Option<String>, version: Option<String>, @@ -173,12 +174,17 @@ impl ArchiveCommand { &composer_archive_manager_ref } else { let factory = Factory; - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(()))); + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))); let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_http_downloader(io, config, indexmap::IndexMap::new())?, + Factory::create_http_downloader(io.clone(), config, indexmap::IndexMap::new())?, )); - let download_manager = - factory.create_download_manager(io, config, &http_downloader, &process, None)?; + let download_manager = factory.create_download_manager( + io.clone(), + config, + &http_downloader, + &process, + None, + )?; let loop_ = std::rc::Rc::new(std::cell::RefCell::new(Loop::new( http_downloader.clone(), Some(process), @@ -190,7 +196,7 @@ impl ArchiveCommand { let package: crate::package::CompletePackageInterfaceHandle = if let Some(name) = package_name { - match self.select_package(io, &name, version.as_deref())? { + match self.select_package(io.clone(), &name, version.as_deref())? { Some(p) => p, None => return Ok(1), } @@ -227,7 +233,7 @@ impl ArchiveCommand { pub fn select_package( &mut self, - io: &mut dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, package_name: &str, version: Option<&str>, ) -> Result<Option<crate::package::CompletePackageInterfaceHandle>> { @@ -253,7 +259,7 @@ impl ArchiveCommand { repo = CompositeRepository::new(repos); min_stability = composer.get_package().get_minimum_stability().to_string(); } else { - let default_repos = RepositoryFactory::default_repos_with_default_manager(io)?; + let default_repos = RepositoryFactory::default_repos_with_default_manager(io.clone())?; let repo_names: Vec<String> = default_repos.keys().cloned().collect(); io.write_error(&format!( "No composer.json found in the current directory, searching packages from {}", diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs index d3dadbd..ec8b495 100644 --- a/crates/shirabe/src/command/audit_command.rs +++ b/crates/shirabe/src/command/audit_command.rs @@ -6,6 +6,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::composer::PartialComposerHandle; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::repository::CanonicalPackagesTrait; use crate::repository::InstalledRepository; use crate::repository::RepositoryInterface; @@ -127,7 +128,7 @@ impl AuditCommand { let _ = ignore_severities; Ok(auditor .audit( - self.get_io(), + &mut *self.get_io().borrow_mut(), &repo_set, packages, &audit_format, diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 9511c4d..6b673ed 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -13,6 +13,8 @@ use shirabe_php_shim::{ InvalidArgumentException, LogicException, PhpMixed, RuntimeException, UnexpectedValueException, count, explode, in_array, is_string, max, }; +use std::cell::RefCell; +use std::rc::Rc; use crate::advisory::AuditConfig; use crate::advisory::Auditor; @@ -27,6 +29,7 @@ use crate::factory::Factory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::package::version::VersionParser; use crate::plugin::PluginEvents; @@ -184,9 +187,9 @@ pub trait BaseCommand { /// Whether or not this command is meant to call another command. fn is_proxy_command(&self) -> bool; - fn get_io(&mut self) -> &mut dyn IOInterface; + fn get_io(&mut self) -> Rc<RefCell<dyn IOInterface>>; - fn set_io(&mut self, io: Box<dyn IOInterface>); + fn set_io(&mut self, io: Rc<RefCell<dyn IOInterface>>); // TODO(cli-completion): fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions); @@ -201,7 +204,7 @@ pub trait BaseCommand { fn create_composer_instance( &self, input: &dyn InputInterface, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<IndexMap<String, PhpMixed>>, disable_plugins: bool, disable_scripts: Option<bool>, @@ -254,7 +257,7 @@ pub trait BaseCommand { #[derive(Debug)] pub struct BaseCommandData { pub(crate) composer: Option<PartialComposerHandle>, - pub(crate) io: Option<Box<dyn IOInterface>>, + pub(crate) io: Option<Rc<RefCell<dyn IOInterface>>>, } pub trait HasBaseCommandData { @@ -269,11 +272,11 @@ pub trait HasBaseCommandData { &mut self.base_command_data_mut().composer } - fn io(&self) -> Option<&dyn IOInterface> { - self.base_command_data().io.as_deref() + fn io(&self) -> Option<Rc<RefCell<dyn IOInterface>>> { + self.base_command_data().io.clone() } - fn io_mut(&mut self) -> &mut Option<Box<dyn IOInterface>> { + fn io_mut(&mut self) -> &mut Option<Rc<RefCell<dyn IOInterface>>> { &mut self.base_command_data_mut().io } } @@ -335,16 +338,16 @@ impl<C: HasBaseCommandData> BaseCommand for C { false } - fn get_io(&mut self) -> &mut dyn IOInterface { + fn get_io(&mut self) -> Rc<RefCell<dyn IOInterface>> { if self.io().is_none() { // TODO(phase-b): requires inner Symfony Application access - *self.io_mut() = Some(Box::new(NullIO::new())); + *self.io_mut() = Some(Rc::new(RefCell::new(NullIO::new()))); } - &mut **self.io_mut().as_mut().unwrap() + self.io().unwrap() } - fn set_io(&mut self, io: Box<dyn IOInterface>) { + fn set_io(&mut self, io: Rc<RefCell<dyn IOInterface>>) { *self.io_mut() = Some(io); } @@ -363,9 +366,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { // TODO(phase-b): `$this instanceof SelfUpdateCommand` not representable let composer = self.try_composer(Some(disable_plugins), Some(disable_scripts)); - // TODO(phase-b): re-borrow self for get_io after try_composer move - let io_ptr: *const dyn IOInterface = self.get_io(); - let io = unsafe { &*io_ptr }; + let io = self.get_io(); let disable_plugins_kind = if disable_plugins { crate::factory::DisablePlugins::All @@ -373,7 +374,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { crate::factory::DisablePlugins::None }; let composer = if composer.is_none() { - Factory::create_global(io, disable_plugins_kind, disable_scripts) + Factory::create_global(io.clone(), disable_plugins_kind, disable_scripts) } else { composer }; @@ -476,7 +477,7 @@ impl<C: HasBaseCommandData> BaseCommand for C { fn create_composer_instance( &self, input: &dyn InputInterface, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<IndexMap<String, PhpMixed>>, disable_plugins: bool, disable_scripts: Option<bool>, diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs index 85e68b3..1b7294e 100644 --- a/crates/shirabe/src/command/base_config_command.rs +++ b/crates/shirabe/src/command/base_config_command.rs @@ -37,9 +37,9 @@ pub trait BaseConfigCommand: BaseCommand { } // TODO(phase-b): clone_box to release the &mut self borrow held by get_io. - let io = self.get_io().clone_box(); + let io = self.get_io().clone(); *self.config_mut() = Some(std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_config(Some(io.as_ref()), None)?, + Factory::create_config(Some(io.clone()), None)?, ))); let config_rc = self.config().unwrap().clone(); @@ -62,7 +62,7 @@ pub trait BaseConfigCommand: BaseCommand { self.set_config_file(Some(JsonFile::new( config_file.clone(), None, - Some(io.clone_box()), + Some(io.clone()), )?)); // TODO(phase-b): JsonConfigSource::new takes owned JsonFile, but PHP shares the same // instance with $this->configFile. Needs Rc<RefCell<JsonFile>> refactor on both sides. diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 8ea10b5..f449629 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -10,6 +10,8 @@ use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::Bound; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; +use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::CompletePackageInterface; use crate::package::Link; use crate::package::Package; diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index 5d886e7..fd5edd9 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -11,6 +11,7 @@ use shirabe_semver::constraint::SimpleConstraint; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::Link; use crate::repository::InstalledRepository; diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 6412160..476be75 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -25,6 +25,7 @@ use crate::config::JsonConfigSource; use crate::console::input::InputArgument; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::base_package::{self, BasePackage}; use crate::util::Filesystem; @@ -130,7 +131,7 @@ impl ConfigCommand { self.auth_config_file = Some(JsonFile::new( auth_config_file, None, - Some(self.get_io().clone_box()), + Some(self.get_io().clone()), )?); // TODO(phase-b): JsonConfigSource::new takes owned JsonFile (PHP sharing semantics). // Skipping auth_config_source assignment until Rc<RefCell<JsonFile>> refactor lands. @@ -257,6 +258,7 @@ impl ConfigCommand { { let config_rc = self.config.as_ref().unwrap().clone(); self.get_io() + .borrow_mut() .load_configuration(&mut *config_rc.borrow_mut())?; } diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index e0cb3d4..d5a8a0c 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -30,6 +30,7 @@ use crate::installer::Installer; use crate::installer::ProjectInstaller; use crate::installer::SuggestedPackagesReporter; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; @@ -113,7 +114,7 @@ impl CreateProjectCommand { ) -> 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: Box<dyn IOInterface> = self.get_io().clone_box(); + 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)?; @@ -160,9 +161,8 @@ impl CreateProjectCommand { Some(repository_url_opt) }; - let mut io = io; self.install_project( - &mut *io, + io, config, input, input @@ -207,7 +207,7 @@ impl CreateProjectCommand { #[allow(clippy::too_many_arguments)] pub fn install_project( &mut self, - io: &mut dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, input: &dyn InputInterface, package_name: Option<String>, @@ -247,14 +247,15 @@ impl CreateProjectCommand { .unwrap_or_else(PlatformRequirementFilterFactory::ignore_nothing); // we need to manually load the configuration to pass the auth credentials to the io interface! - io.load_configuration(&mut *config.borrow_mut())?; + io.borrow_mut() + .load_configuration(&mut *config.borrow_mut())?; - self.suggested_packages_reporter = Some(SuggestedPackagesReporter::new(io.clone_box())); + self.suggested_packages_reporter = Some(SuggestedPackagesReporter::new(io.clone())); let installed_from_vcs = if let Some(package_name) = package_name.as_ref() { self.install_root_package( input, - io, + io.clone(), &config, package_name, &*platform_requirement_filter, @@ -278,8 +279,13 @@ impl CreateProjectCommand { unlink("composer.lock"); } - let mut composer_handle = - self.create_composer_instance(input, io, None, disable_plugins, Some(disable_scripts))?; + let mut composer_handle = self.create_composer_instance( + input, + io.clone(), + None, + disable_plugins, + Some(disable_scripts), + )?; // add the repository to the composer.json and use it for the install run later if let Some(repos) = repositories.as_ref() { @@ -287,7 +293,7 @@ impl CreateProjectCommand { for (index, repo) in repos.iter().enumerate() { let config = crate::command::composer_full(&composer_handle).get_config(); let repo_config = - RepositoryFactory::config_from_string(io, &config, repo, true)?; + RepositoryFactory::config_from_string(io.clone(), &config, repo, true)?; let composer_json_repositories_config = crate::command::composer_full(&composer_handle) .get_config() @@ -332,8 +338,13 @@ impl CreateProjectCommand { ); } - composer_handle = - self.create_composer_instance(input, io, None, disable_plugins, None)?; + composer_handle = self.create_composer_instance( + input, + io.clone(), + None, + disable_plugins, + None, + )?; } } } @@ -371,7 +382,7 @@ impl CreateProjectCommand { .borrow_mut() .set_output_progress(!no_progress); - let mut installer = Installer::create(io.clone_box(), &composer_handle); + let mut installer = Installer::create(io.clone(), &composer_handle); // TODO(phase-b): set_suggested_packages_reporter takes by value but PHP class // means shared ownership; needs Rc<SuggestedPackagesReporter> for proper sharing. installer @@ -379,7 +390,7 @@ impl CreateProjectCommand { .set_prefer_dist(prefer_dist) .set_dev_mode(install_dev_packages) .set_platform_requirement_filter(platform_requirement_filter.clone_box()) - .set_suggested_packages_reporter(SuggestedPackagesReporter::new(io.clone_box())) + .set_suggested_packages_reporter(SuggestedPackagesReporter::new(io.clone())) .set_optimize_autoloader( config .borrow_mut() @@ -540,7 +551,7 @@ impl CreateProjectCommand { fn install_root_package( &self, input: &dyn InputInterface, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, package_name: &str, platform_requirement_filter: &dyn PlatformRequirementFilterInterface, @@ -584,7 +595,7 @@ impl CreateProjectCommand { directory = rtrim(&directory, Some("/\\")); let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone_box(), + io.clone(), )))); let fs = std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(Some(process)))); if !fs.borrow().is_absolute_path(&directory) { @@ -704,7 +715,7 @@ impl CreateProjectCommand { let composer_handle = self.create_composer_instance( input, - io, + io.clone(), Some(config.borrow_mut().all(0)?), disable_plugins, Some(disable_scripts), @@ -727,7 +738,7 @@ impl CreateProjectCommand { // TODO(phase-b): default_repos needs &mut RepositoryManager but we hold &RepositoryManager. let _ = rm; repository_set.add_repository(Box::new(CompositeRepository::new( - RepositoryFactory::default_repos(Some(io), Some(config.clone()), None)? + RepositoryFactory::default_repos(Some(io.clone()), Some(config.clone()), None)? .into_iter() .map(|(_, v)| v) .collect(), @@ -735,7 +746,7 @@ impl CreateProjectCommand { } else { for repo in repositories.unwrap() { let mut repo_config = - RepositoryFactory::config_from_string(io, &config, repo, true)?; + RepositoryFactory::config_from_string(io.clone(), &config, repo, true)?; let is_packagist_disabled = (repo_config.contains_key("packagist") && repo_config.len() == 1 && repo_config.get("packagist").and_then(|v| v.as_bool()) == Some(false)) @@ -767,7 +778,7 @@ impl CreateProjectCommand { } repository_set.add_repository(RepositoryFactory::create_repo( - io, + io.clone(), &config, repo_config.clone(), None, @@ -803,7 +814,7 @@ impl CreateProjectCommand { &stability, None, 0, - Some(io), + Some(&*io.borrow()), PhpMixed::Bool(true), )?; @@ -913,7 +924,7 @@ impl CreateProjectCommand { true, false, )?; - im.notify_installs(io); + im.notify_installs(&*io.borrow()); // collect suggestions // TODO(phase-b): self.suggested_packages_reporter is on the outer scope via &self @@ -947,7 +958,7 @@ impl CreateProjectCommand { fn create_composer_instance( &self, input: &dyn InputInterface, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<indexmap::IndexMap<String, PhpMixed>>, disable_plugins: bool, disable_scripts: Option<bool>, diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index fe57e5c..c64fc39 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -27,6 +27,7 @@ use crate::downloader::TransportException; use crate::factory::Factory; use crate::io::BufferIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::json::JsonFile; use crate::json::JsonValidationException; @@ -78,7 +79,7 @@ impl DiagnoseCommand { output: &dyn OutputInterface, ) -> anyhow::Result<i64> { let mut composer = self.try_composer(None, None); - let io_boxed: Box<dyn IOInterface> = self.get_io().clone_box(); + let io_boxed: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io().clone(); let config: std::rc::Rc<std::cell::RefCell<Config>>; if let Some(ref mut c) = composer { @@ -103,7 +104,7 @@ impl DiagnoseCommand { .map(std::rc::Rc::clone) .unwrap_or_else(|| { std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io_boxed.clone_box(), + io_boxed.clone(), )))) }), ); @@ -111,12 +112,13 @@ impl DiagnoseCommand { config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)); self.process = Some(std::rc::Rc::new(std::cell::RefCell::new( - ProcessExecutor::new(Some(io_boxed.clone_box())), + ProcessExecutor::new(Some(io_boxed.clone())), ))); } // TODO(phase-b): clone_box to release self borrow held by get_io. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = io_box.as_ref(); + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; let mut config_inner: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); config_inner.insert("secure-http".to_string(), Box::new(PhpMixed::Bool(false))); @@ -133,7 +135,7 @@ impl DiagnoseCommand { ); self.http_downloader = Some(std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_http_downloader(io, &config, indexmap::IndexMap::new())?, + Factory::create_http_downloader(io_box.clone(), &config, indexmap::IndexMap::new())?, ))); if strpos(file!(), "phar:") == Some(0) { @@ -295,7 +297,7 @@ impl DiagnoseCommand { .collect(); let composer_repo = ComposerRepository::new( repo_arr_unboxed, - self.get_io().clone_box(), + self.get_io().clone(), &*config.borrow(), self.http_downloader.clone().unwrap(), None, @@ -427,7 +429,7 @@ impl DiagnoseCommand { } fn check_composer_schema(&mut self) -> anyhow::Result<PhpMixed> { - let validator = ConfigValidator::new(self.get_io().clone_box()); + let validator = ConfigValidator::new(self.get_io().clone()); let (errors, _, warnings) = validator.validate(&Factory::get_composer_file()?, 0, 0); if !errors.is_empty() || !warnings.is_empty() { @@ -680,7 +682,7 @@ impl DiagnoseCommand { return Ok(result); } - self.get_io().set_authentication( + self.get_io().borrow_mut().set_authentication( domain.to_string(), token.to_string(), Some("x-oauth-basic".to_string()), @@ -744,7 +746,7 @@ impl DiagnoseCommand { } if let Some(t) = token { - self.get_io().set_authentication( + self.get_io().borrow_mut().set_authentication( domain.to_string(), t.to_string(), Some("x-oauth-basic".to_string()), @@ -939,7 +941,7 @@ impl DiagnoseCommand { // TODO(phase-b): ComposerRepository does not implement RepositoryInterface yet let _composer_repo = ComposerRepository::new( repo_config, - Box::new(NullIO::new()), + std::rc::Rc::new(std::cell::RefCell::new(NullIO::new())), config, self.http_downloader.clone().unwrap(), None, diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index 275e635..6f361da 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -8,6 +8,7 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed, file_exists}; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs index 23aa103..3f70cf8 100644 --- a/crates/shirabe/src/command/exec_command.rs +++ b/crates/shirabe/src/command/exec_command.rs @@ -9,6 +9,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; #[derive(Debug)] pub struct ExecCommand { diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs index f829342..340424f 100644 --- a/crates/shirabe/src/command/fund_command.rs +++ b/crates/shirabe/src/command/fund_command.rs @@ -15,6 +15,7 @@ use shirabe_semver::constraint::MatchAllConstraint; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::AliasPackage; use crate::package::CompletePackage; diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs index a8cf1d4..a3d5a78 100644 --- a/crates/shirabe/src/command/global_command.rs +++ b/crates/shirabe/src/command/global_command.rs @@ -13,6 +13,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputArgument; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Filesystem; use crate::util::Platform; diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs index 673039a..c048bfd 100644 --- a/crates/shirabe/src/command/home_command.rs +++ b/crates/shirabe/src/command/home_command.rs @@ -9,6 +9,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::CompletePackageInterfaceHandle; use crate::package::PackageInterface; use crate::package::RootPackageInterface; @@ -73,8 +74,9 @@ impl HomeCommand { ) -> Result<i64> { let repos = self.initialize_repos()?; // TODO(phase-b): clone_box to release self borrow held by get_io. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = io_box.as_ref(); + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; let mut return_code: i64 = 0; let packages: Vec<String> = input @@ -172,7 +174,7 @@ impl HomeCommand { } fn open_browser(&mut self, url: &str) { - let mut process = ProcessExecutor::new(Some(self.get_io().clone_box())); + let mut process = ProcessExecutor::new(Some(self.get_io().clone())); if Platform::is_windows() { let _ = process.execute( PhpMixed::from(vec!["start", "\"web\"", "explorer", url]), diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index 122642d..730658c 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -23,6 +23,7 @@ use crate::composer::PartialComposerHandle; use crate::console::input::InputOption; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::json::JsonValidationException; use crate::package::base_package::{self, BasePackage}; @@ -52,7 +53,7 @@ impl PackageDiscoveryTrait for InitCommand { todo!() } - fn get_io(&self) -> &dyn IOInterface { + fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { todo!() } @@ -193,11 +194,12 @@ impl InitCommand { .unwrap_or_default(); if (repositories.len() as i64) > 0 { let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config( - Some(io), + Some(io.clone()), None, )?)); for repo in &repositories { - let repo_config = RepositoryFactory::config_from_string(io, &config, repo, true)?; + let repo_config = + RepositoryFactory::config_from_string(io.clone(), &config, repo, true)?; let entry = options .entry("repositories".to_string()) .or_insert_with(|| PhpMixed::List(vec![])); @@ -445,17 +447,20 @@ impl InitCommand { .unwrap_or_default(); if (repositories.len() as i64) > 0 { let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config( - Some(io), + Some(io.clone()), None, )?)); - io.load_configuration(&mut *config.borrow_mut())?; - let mut repo_manager = RepositoryFactory::manager(io, &config, None, None, None)?; + io.borrow_mut() + .load_configuration(&mut *config.borrow_mut())?; + let mut repo_manager = + RepositoryFactory::manager(io.clone(), &config, None, None, None)?; let mut repos: Vec<Box<dyn crate::repository::RepositoryInterface>> = vec![Box::new(PlatformRepository::new(vec![], IndexMap::new())?)]; let mut create_default_packagist_repo = true; for repo in &repositories { - let repo_config = RepositoryFactory::config_from_string(io, &config, repo, true)?; + let repo_config = + RepositoryFactory::config_from_string(io.clone(), &config, repo, true)?; let is_packagist_false = repo_config .get("packagist") .map(|v| v.as_bool() == Some(false)) @@ -471,7 +476,7 @@ impl InitCommand { continue; } repos.push(RepositoryFactory::create_repo( - io, + io.clone(), &config, repo_config, Some(&mut repo_manager), @@ -486,7 +491,7 @@ impl InitCommand { PhpMixed::String("https://repo.packagist.org".to_string()), ); repos.push(RepositoryFactory::create_repo( - io, + io.clone(), &config, default_config, Some(&mut repo_manager), @@ -943,7 +948,7 @@ impl InitCommand { return self.git_config.clone().unwrap_or_default(); } - let mut process = ProcessExecutor::new(Some(self.get_io().clone_box())); + let mut process = ProcessExecutor::new(Some(self.get_io().clone())); let mut output = String::new(); if process.execute_args( diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs index a0710dc..4c869f4 100644 --- a/crates/shirabe/src/command/install_command.rs +++ b/crates/shirabe/src/command/install_command.rs @@ -67,8 +67,9 @@ impl InstallCommand { output: &dyn OutputInterface, ) -> Result<i64> { // TODO(phase-b): clone_box to release self borrow held by get_io. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = io_box.as_ref(); + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; if input.get_option("dev").as_bool().unwrap_or(false) { io.write_error("<warning>You are using the deprecated option \"--dev\". It has no effect and will break in Composer 3.</warning>"); @@ -114,7 +115,7 @@ impl InstallCommand { .borrow_mut() .dispatch(Some(command_event.get_name()), None); - let mut install = Installer::create(io.clone_box(), &composer_handle); + let mut install = Installer::create(io_box.clone(), &composer_handle); let config = composer.get_config(); let (prefer_source, prefer_dist) = diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs index 2bcecd5..6c7ea73 100644 --- a/crates/shirabe/src/command/licenses_command.rs +++ b/crates/shirabe/src/command/licenses_command.rs @@ -14,6 +14,7 @@ use shirabe_php_shim::{PhpMixed, RuntimeException, UnexpectedValueException}; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::BasePackage; use crate::package::CompletePackage; diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index a268469..e8a56a9 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -20,6 +20,7 @@ use crate::factory::Factory; use crate::filter::platform_requirement_filter::IgnoreAllPlatformRequirementFilter; use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::BasePackage; use crate::package::CompletePackageInterface; use crate::package::PackageInterface; @@ -40,7 +41,7 @@ pub trait PackageDiscoveryTrait { fn get_repository_sets_mut(&mut self) -> &mut IndexMap<String, RepositorySet>; // PHP: trait dependencies (provided by BaseCommand) - fn get_io(&self) -> &dyn IOInterface; + fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>>; fn try_composer(&self) -> Option<PartialComposerHandle>; fn require_composer( &self, @@ -61,8 +62,8 @@ pub trait PackageDiscoveryTrait { // TODO(phase-b): PlatformRepository::new() signature Box::new(todo!("PlatformRepository::new()") as PlatformRepository), ]; - let mut io_owned: Box<dyn IOInterface> = todo!("clone self.get_io() into a Box"); - for (_, repo) in RepositoryFactory::default_repos_with_default_manager(&mut *io_owned) + let io_owned: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io(); + for (_, repo) in RepositoryFactory::default_repos_with_default_manager(io_owned) .unwrap() .into_iter() { diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index 1e3114b..ad4739b 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -16,6 +16,7 @@ use crate::dependency_resolver::Transaction; use crate::dependency_resolver::operation::InstallOperation; use crate::dependency_resolver::operation::UninstallOperation; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::base_package; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index 49b21dc..9ea7329 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -17,6 +17,7 @@ use crate::dependency_resolver::Request; use crate::factory::Factory; use crate::installer::Installer; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::BasePackage; use crate::package::base_package; @@ -510,8 +511,9 @@ impl RemoveCommand { .borrow_mut() .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); - // TODO(phase-b): Installer::create expects Box<dyn IOInterface>; io here is &mut dyn IOInterface - let io_box: Box<dyn IOInterface> = todo!("share IOInterface as Box<dyn IOInterface>"); + // TODO(phase-b): Installer::create expects std::rc::Rc<std::cell::RefCell<dyn IOInterface>>; io here is &mut dyn IOInterface + let io_box: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = + todo!("share IOInterface as Box<dyn IOInterface>"); let mut install = Installer::create(io_box, &composer_handle); let update_dev_mode = !input.get_option("update-no-dev").as_bool().unwrap_or(false); diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index 1f6599b..bf000e4 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -17,6 +17,7 @@ use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; #[derive(Debug)] diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index f0dce6b..66e1134 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -25,6 +25,7 @@ use crate::factory::Factory; use crate::installer::Installer; use crate::installer::InstallerEvents; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::json::JsonManipulator; use crate::package::base_package; @@ -67,7 +68,7 @@ impl PackageDiscoveryTrait for RequireCommand { todo!() } - fn get_io(&self) -> &dyn IOInterface { + fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { todo!() } @@ -845,10 +846,12 @@ impl RequireCommand { .borrow_mut() .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); - // TODO(phase-b): Installer::create takes Box<dyn IOInterface> for ownership but io is a + // TODO(phase-b): Installer::create takes std::rc::Rc<std::cell::RefCell<dyn IOInterface>> for ownership but io is a // borrowed &dyn here; needs Rc<dyn IOInterface> for proper sharing. - let mut install = - Installer::create(todo!("share io as Box<dyn IOInterface>"), &composer_handle); + let mut install = Installer::create( + todo!("share io as std::rc::Rc<std::cell::RefCell<dyn IOInterface>>"), + &composer_handle, + ); let (prefer_source, prefer_dist) = self.get_preferred_install_options(&*composer.get_config().borrow(), input, false)?; diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs index e58ad24..9118463 100644 --- a/crates/shirabe/src/command/run_script_command.rs +++ b/crates/shirabe/src/command/run_script_command.rs @@ -10,6 +10,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::script::Event as ScriptEvent; use crate::script::ScriptEvents; use crate::util::Platform; diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs index 9375ec7..2033cfc 100644 --- a/crates/shirabe/src/command/search_command.rs +++ b/crates/shirabe/src/command/search_command.rs @@ -4,6 +4,7 @@ use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputArgument; use crate::console::input::InputOption; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; @@ -73,8 +74,8 @@ impl SearchCommand { c } else { // TODO(phase-b): clone_box to release self borrow held by get_io. - let io_box = self.get_io().clone_box(); - self.create_composer_instance(input, io_box.as_ref(), None, false, None)? + let io_box = self.get_io().clone(); + self.create_composer_instance(input, io_box, None, false, None)? }; let composer_ref = crate::command::composer_full(&composer); // TODO(phase-b): get_local_repository returns &dyn InstalledRepositoryInterface but we need Box<dyn RepositoryInterface> diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs index 80d8b01..fc22ab7 100644 --- a/crates/shirabe/src/command/self_update_command.rs +++ b/crates/shirabe/src/command/self_update_command.rs @@ -28,6 +28,7 @@ use crate::console::input::InputOption; use crate::downloader::FilesystemException; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::self_update::Keys; use crate::self_update::Versions; use crate::util::Filesystem; @@ -132,7 +133,7 @@ impl SelfUpdateCommand { let io = self.get_io(); let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_http_downloader(io, &config, indexmap::IndexMap::new())?, + Factory::create_http_downloader(io.clone(), &config, indexmap::IndexMap::new())?, )); let mut versions_util = Versions::new(config.clone(), http_downloader.clone()); @@ -142,7 +143,7 @@ impl SelfUpdateCommand { for channel in Versions::CHANNELS { if input.get_option(channel).as_bool().unwrap_or(false) { requested_channel = Some(channel.to_string()); - versions_util.set_channel(channel.to_string(), Some(io))??; + versions_util.set_channel(channel.to_string(), Some(&*io.borrow()))??; break; } } diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index c7c2f9d..f3c1b3d 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -22,6 +22,7 @@ use crate::dependency_resolver::DefaultPolicy; use crate::dependency_resolver::PolicyInterface; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::CompletePackageInterface; use crate::package::Link; @@ -1144,7 +1145,7 @@ impl ShowCommand { ), ); } - let io: &mut dyn IOInterface = self.get_io(); + let io = self.get_io(); io.write(&JsonFile::encode( &PhpMixed::Array( json_map @@ -1158,7 +1159,7 @@ impl ShowCommand { if input.get_option("latest").as_bool() == Some(true) && view_data.values().any(|v| !v.is_empty()) { - let io: &mut dyn IOInterface = self.get_io(); + let io = self.get_io(); if !io.is_decorated() { io.write_error("Legend:"); io.write_error("! patch or minor release available - update recommended"); @@ -1315,7 +1316,7 @@ impl ShowCommand { write_release_date: bool, release_date_length: usize, ) { - let io: &mut dyn IOInterface = self.get_io(); + let io = self.get_io(); let pad_name = write_version || write_latest || write_release_date || write_description; let pad_version = write_latest || write_release_date || write_description; let pad_latest = write_description || write_release_date; @@ -2681,7 +2682,7 @@ impl ShowCommand { &best_stability, None, 0, - Some(self.get_io()), + Some(&*self.get_io().borrow()), PhpMixed::Bool(true), )?; while let Some(ref c) = candidate { diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs index 69c36a1..182dec8 100644 --- a/crates/shirabe/src/command/status_command.rs +++ b/crates/shirabe/src/command/status_command.rs @@ -87,8 +87,9 @@ impl StatusCommand { let composer = self.require_composer(None, None)?; let mut composer = crate::command::composer_full_mut(&composer); // TODO(phase-b): release the &mut self borrow held by get_io via clone_box. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = io_box.as_ref(); + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; let mut errors: IndexMap<String, String> = IndexMap::new(); let mut unpushed_changes: IndexMap<String, String> = IndexMap::new(); @@ -101,12 +102,16 @@ impl StatusCommand { .borrow() .get_process_executor() .map(std::rc::Rc::clone) - .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)))); + .unwrap_or_else(|| { + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io_box.clone(), + )))) + }); let mut guesser = VersionGuesser::new( composer.get_config(), process_executor.clone(), parser.clone(), - Some(io_box.clone_box()), + Some(io_box.clone()), ); let dumper = ArrayDumper::new(); diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs index 8166c1e..b4693a2 100644 --- a/crates/shirabe/src/command/suggests_command.rs +++ b/crates/shirabe/src/command/suggests_command.rs @@ -90,8 +90,9 @@ impl SuggestsCommand { } let installed_repo = InstalledRepository::new(installed_repos); - // TODO(phase-b): SuggestedPackagesReporter::new expects Box<dyn IOInterface>; self.get_io() returns &mut dyn IOInterface - let io_box: Box<dyn IOInterface> = todo!("share IOInterface as Box<dyn IOInterface>"); + // TODO(phase-b): SuggestedPackagesReporter::new expects std::rc::Rc<std::cell::RefCell<dyn IOInterface>>; self.get_io() returns &mut dyn IOInterface + let io_box: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = + todo!("share IOInterface as Box<dyn IOInterface>"); let mut reporter = SuggestedPackagesReporter::new(io_box); let filter = input.get_argument("packages"); diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index 06984e2..f879283 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -77,8 +77,9 @@ impl UpdateCommand { ) -> Result<i64> { // TODO(phase-b): clone_box avoids the &mut self conflict with require_composer // below; revisit when get_io can return an Rc/Arc owned handle. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = &*io_box; + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; if input.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>", @@ -311,7 +312,7 @@ impl UpdateCommand { .borrow_mut() .set_output_progress(!input.get_option("no-progress").as_bool().unwrap_or(false)); - let mut install = Installer::create(io.clone_box(), &composer_handle); + let mut install = Installer::create(io_box.clone(), &composer_handle); let config = composer.get_config(); let (prefer_source, prefer_dist) = diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs index 814f09f..cb8fa89 100644 --- a/crates/shirabe/src/command/validate_command.rs +++ b/crates/shirabe/src/command/validate_command.rs @@ -119,8 +119,9 @@ impl ValidateCommand { .map(Ok) .unwrap_or_else(Factory::get_composer_file)?; // TODO(phase-b): get_io() takes &mut self via BaseCommand; clone_box to release the borrow. - let io_box = self.get_io().clone_box(); - let io: &dyn IOInterface = io_box.as_ref(); + let io_box = self.get_io().clone(); + let io_ref = io_box.borrow(); + let io: &dyn IOInterface = &*io_ref; if !std::path::Path::new(&file).exists() { io.write_error(&format!("<error>{} not found.</error>", file)); @@ -131,7 +132,7 @@ impl ValidateCommand { return Ok(3); } - let validator = ConfigValidator::new(io.clone_box()); + let validator = ConfigValidator::new(io_box.clone()); let check_all = if input.get_option("no-check-all").as_bool().unwrap_or(false) { 0 } else { @@ -156,7 +157,7 @@ impl ValidateCommand { validator.validate(&file, check_all, check_version); let mut lock_errors: Vec<String> = vec![]; - let composer = self.create_composer_instance(input, io, None, false, None)?; + let composer = self.create_composer_instance(input, io_box.clone(), None, false, None)?; let mut composer = crate::command::composer_full_mut(&composer); let check_lock = (check_lock && composer diff --git a/crates/shirabe/src/compiler.rs b/crates/shirabe/src/compiler.rs index bad682b..e9a0ca4 100644 --- a/crates/shirabe/src/compiler.rs +++ b/crates/shirabe/src/compiler.rs @@ -45,7 +45,7 @@ impl Compiler { shirabe_php_shim::unlink(phar_file); } - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(()))); + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))); let command = Git::build_rev_list_command( &process, diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index 0b04e26..3a6b847 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -77,6 +77,7 @@ use crate::factory::Factory; use crate::installer::Installer; use crate::io::ConsoleIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::json::JsonValidationException; use crate::util::ErrorHandler; @@ -89,7 +90,7 @@ use crate::util::Silencer; pub struct Application { inner: BaseApplication, pub(crate) composer: Option<PartialComposerHandle>, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, has_plugin_commands: bool, disable_plugins_by_default: bool, disable_scripts_by_default: bool, @@ -124,7 +125,8 @@ impl Application { date_default_timezone_set(&tz); } - let io: Box<dyn IOInterface> = Box::new(NullIO::new()); + let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = + std::rc::Rc::new(std::cell::RefCell::new(NullIO::new())); SHUTDOWN_REGISTERED.get_or_init(|| { register_shutdown_function(Box::new(|| { @@ -207,7 +209,7 @@ impl Application { let _ = ConsoleIO::new; let _ = HelperSet::new(helpers); // self.io stays as the NullIO that was set during construction. - let io_owned = self.io.clone_box(); + let io_owned = self.io.clone(); let _ = io_owned; // Register error handler again to pass it the IO instance @@ -458,7 +460,7 @@ impl Application { } } - let mut ghe = GithubActionError::new(self.io.clone_box()); + let mut ghe = GithubActionError::new(self.io.clone()); ghe.emit(&pe.message, file.as_deref(), line); return Err(e); @@ -783,7 +785,7 @@ impl Application { Ok(see.get_code()) } else { - let mut ghe = GithubActionError::new(self.io.clone_box()); + let mut ghe = GithubActionError::new(self.io.clone()); ghe.emit(&e.to_string(), None, None); self.hint_common_errors(&e, output); @@ -993,22 +995,18 @@ impl Application { let disable_scripts = disable_scripts.unwrap_or(self.disable_scripts_by_default); if self.composer.is_none() { - let io_for_factory: Box<dyn IOInterface> = if Platform::is_input_completion_process() { - Box::new(NullIO::new()) - } else { - self.io.clone_box() - }; + let io_for_factory: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = + if Platform::is_input_completion_process() { + std::rc::Rc::new(std::cell::RefCell::new(NullIO::new())) + } else { + self.io.clone() + }; let disable_plugins_enum = if disable_plugins { crate::factory::DisablePlugins::All } else { crate::factory::DisablePlugins::None }; - match Factory::create( - &*io_for_factory, - None, - disable_plugins_enum, - disable_scripts, - ) { + match Factory::create(io_for_factory, None, disable_plugins_enum, disable_scripts) { Ok(c) => self.composer = Some(c), Err(e) => { if e.downcast_ref::<JsonValidationException>().is_some() @@ -1044,8 +1042,8 @@ impl Application { todo!() } - pub fn get_io(&self) -> &dyn IOInterface { - &*self.io + pub fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { + self.io.clone() } pub fn get_help(&self) -> String { @@ -1175,7 +1173,7 @@ impl Application { } fn get_use_parent_dir_config_value(&self) -> PhpMixed { - let config = match Factory::create_config(Some(&*self.io), None) { + let config = match Factory::create_config(Some(self.io.clone()), None) { Ok(c) => c, Err(_) => return PhpMixed::Bool(false), }; diff --git a/crates/shirabe/src/console/github_action_error.rs b/crates/shirabe/src/console/github_action_error.rs index 933ba0a..963f5c1 100644 --- a/crates/shirabe/src/console/github_action_error.rs +++ b/crates/shirabe/src/console/github_action_error.rs @@ -1,15 +1,16 @@ //! ref: composer/src/Composer/Console/GithubActionError.php use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Platform; #[derive(Debug)] pub struct GithubActionError { - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl GithubActionError { - pub fn new(io: Box<dyn IOInterface>) -> Self { + pub fn new(io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) -> Self { Self { io } } diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index 38a083e..0efaac5 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -22,6 +22,7 @@ use crate::dependency_resolver::Request; use crate::dependency_resolver::SecurityAdvisoryPoolFilter; use crate::event_dispatcher::EventDispatcher; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::AliasPackage; use crate::package::BasePackageHandle; use crate::package::CompleteAliasPackage; @@ -46,7 +47,7 @@ pub struct PoolBuilder { temporary_constraints: IndexMap<String, AnyConstraint>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, pool_optimizer: Option<PoolOptimizer>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, alias_map: IndexMap<String, IndexMap<i64, AliasPackage>>, packages_to_load: IndexMap<String, AnyConstraint>, loaded_packages: IndexMap<String, AnyConstraint>, @@ -87,7 +88,7 @@ impl PoolBuilder { stability_flags: IndexMap<String, i64>, root_aliases: IndexMap<String, IndexMap<String, IndexMap<String, String>>>, root_references: IndexMap<String, String>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, pool_optimizer: Option<PoolOptimizer>, temporary_constraints: IndexMap<String, AnyConstraint>, diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs index 2610f07..4f8d91d 100644 --- a/crates/shirabe/src/dependency_resolver/solver.rs +++ b/crates/shirabe/src/dependency_resolver/solver.rs @@ -29,6 +29,7 @@ use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFil use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::BasePackageHandle; #[derive(Debug)] @@ -51,7 +52,7 @@ pub struct Solver { pub test_flag_learned_positive_literal: bool, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl Solver { @@ -61,7 +62,7 @@ impl Solver { pub fn new( policy: Box<dyn PolicyInterface>, pool: std::rc::Rc<std::cell::RefCell<Pool>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> Self { let decisions = Decisions::new(pool.clone()); Self { diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs index 6c8a8f9..ffa2ef4 100644 --- a/crates/shirabe/src/downloader/archive_downloader.rs +++ b/crates/shirabe/src/downloader/archive_downloader.rs @@ -11,6 +11,8 @@ use shirabe_php_shim::{ use crate::dependency_resolver::operation::InstallOperation; use crate::downloader::DownloaderInterface; use crate::downloader::FileDownloader; +use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; use crate::util::Platform; diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs index f5201cc..83a03c4 100644 --- a/crates/shirabe/src/downloader/download_manager.rs +++ b/crates/shirabe/src/downloader/download_manager.rs @@ -13,6 +13,7 @@ use shirabe_php_shim::{ use crate::downloader::DownloaderInterface; use crate::exception::IrrecoverableDownloadException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; @@ -20,7 +21,7 @@ use crate::util::Filesystem; #[derive(Debug)] pub struct DownloadManager { /// @var IOInterface - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var bool prefer_dist: bool, /// @var bool @@ -40,7 +41,7 @@ impl DownloadManager { /// @param bool $preferSource prefer downloading from source /// @param Filesystem|null $filesystem custom Filesystem object pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, prefer_source: bool, filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, ) -> Self { diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs index dc8c5a5..4f2ee63 100644 --- a/crates/shirabe/src/downloader/file_downloader.rs +++ b/crates/shirabe/src/downloader/file_downloader.rs @@ -25,6 +25,8 @@ use crate::downloader::TransportException; use crate::event_dispatcher::EventDispatcher; use crate::exception::IrrecoverableDownloadException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; +use crate::io::IOInterfaceMutable; use crate::io::NullIO; use crate::package::PackageInterface; use crate::package::comparer::Comparer; @@ -55,7 +57,7 @@ pub static RESPONSE_HEADERS: LazyLock<Mutex<IndexMap<String, Vec<String>>>> = #[derive(Debug)] pub struct FileDownloader { /// @var IOInterface - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var Config pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, /// @var HttpDownloader @@ -92,7 +94,7 @@ impl FileDownloader { /// Constructor. pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -102,7 +104,7 @@ impl FileDownloader { ) -> Self { let process = process.unwrap_or_else(|| { std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone_box(), + io.clone(), )))) }); let filesystem = filesystem.unwrap_or_else(|| { diff --git a/crates/shirabe/src/downloader/fossil_downloader.rs b/crates/shirabe/src/downloader/fossil_downloader.rs index 1e164b8..9f0277e 100644 --- a/crates/shirabe/src/downloader/fossil_downloader.rs +++ b/crates/shirabe/src/downloader/fossil_downloader.rs @@ -4,6 +4,7 @@ use crate::config::Config; use crate::downloader::DownloaderInterface; use crate::downloader::VcsDownloaderBase; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; use crate::util::ProcessExecutor; @@ -18,7 +19,7 @@ pub struct FossilDownloader { impl FossilDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -46,7 +47,7 @@ impl FossilDownloader { ) -> Result<Option<PhpMixed>> { self.inner.config.borrow_mut().prohibit_url_by_config( &url, - Some(self.inner.io.as_ref()), + Some(&*self.inner.io.borrow()), &indexmap::IndexMap::new(), )?; @@ -107,7 +108,7 @@ impl FossilDownloader { ) -> Result<Option<PhpMixed>> { self.inner.config.borrow_mut().prohibit_url_by_config( &url, - Some(self.inner.io.as_ref()), + Some(&*self.inner.io.borrow()), &indexmap::IndexMap::new(), )?; diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs index a5e3638..b3e00a2 100644 --- a/crates/shirabe/src/downloader/git_downloader.rs +++ b/crates/shirabe/src/downloader/git_downloader.rs @@ -14,6 +14,7 @@ use crate::config::Config; use crate::downloader::DvcsDownloaderInterface; use crate::downloader::VcsDownloaderBase; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; use crate::util::Git as GitUtil; @@ -35,14 +36,14 @@ pub struct GitDownloader { impl GitDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, ) -> Self { let inner = VcsDownloaderBase::new(io, config, process, fs); let git_util = GitUtil::new( - inner.io.clone_box(), + inner.io.clone(), inner.config.clone(), inner.process.clone(), inner.filesystem.clone(), diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs index fe44fed..55dff10 100644 --- a/crates/shirabe/src/downloader/gzip_downloader.rs +++ b/crates/shirabe/src/downloader/gzip_downloader.rs @@ -27,7 +27,7 @@ pub struct GzipDownloader { impl GzipDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, diff --git a/crates/shirabe/src/downloader/hg_downloader.rs b/crates/shirabe/src/downloader/hg_downloader.rs index b12e348..d4c53f3 100644 --- a/crates/shirabe/src/downloader/hg_downloader.rs +++ b/crates/shirabe/src/downloader/hg_downloader.rs @@ -4,6 +4,7 @@ use crate::config::Config; use crate::downloader::DownloaderInterface; use crate::downloader::VcsDownloaderBase; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; use crate::util::Hg as HgUtils; @@ -18,7 +19,7 @@ pub struct HgDownloader { impl HgDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -53,7 +54,7 @@ impl HgDownloader { url: String, ) -> Result<Option<PhpMixed>> { let hg_utils = HgUtils::new( - &*self.inner.io, + self.inner.io.clone(), &*self.inner.config.borrow(), &self.inner.process, ); @@ -108,7 +109,7 @@ impl HgDownloader { url: String, ) -> Result<Option<PhpMixed>> { let hg_utils = HgUtils::new( - &*self.inner.io, + self.inner.io.clone(), &*self.inner.config.borrow(), &self.inner.process, ); diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs index 46090ac..44541d4 100644 --- a/crates/shirabe/src/downloader/path_downloader.rs +++ b/crates/shirabe/src/downloader/path_downloader.rs @@ -19,6 +19,7 @@ use crate::downloader::FileDownloader; use crate::downloader::VcsCapableDownloaderInterface; use crate::event_dispatcher::EventDispatcher; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::package::archiver::ArchivableFilesFinder; use crate::package::dumper::ArrayDumper; @@ -39,7 +40,7 @@ impl PathDownloader { const STRATEGY_MIRROR: i64 = 20; pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -391,7 +392,7 @@ impl PathDownloader { self.inner.config.clone(), self.inner.process.clone(), parser.clone(), - Some(self.inner.io.clone_box()), + Some(self.inner.io.clone()), ); let dumper = ArrayDumper::new(); diff --git a/crates/shirabe/src/downloader/perforce_downloader.rs b/crates/shirabe/src/downloader/perforce_downloader.rs index b3f7bc9..f967111 100644 --- a/crates/shirabe/src/downloader/perforce_downloader.rs +++ b/crates/shirabe/src/downloader/perforce_downloader.rs @@ -4,6 +4,7 @@ use crate::config::Config; use crate::downloader::DownloaderInterface; use crate::downloader::VcsDownloaderBase; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::repository::VcsRepository; use crate::util::Filesystem; @@ -22,7 +23,7 @@ pub struct PerforceDownloader { impl PerforceDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -103,7 +104,7 @@ impl PerforceDownloader { url, path, self.inner.process.clone(), - self.inner.io.clone_box(), + self.inner.io.clone(), )); } diff --git a/crates/shirabe/src/downloader/phar_downloader.rs b/crates/shirabe/src/downloader/phar_downloader.rs index a598437..235e0bb 100644 --- a/crates/shirabe/src/downloader/phar_downloader.rs +++ b/crates/shirabe/src/downloader/phar_downloader.rs @@ -23,7 +23,7 @@ pub struct PharDownloader { impl PharDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs index f482582..bb0c2f8 100644 --- a/crates/shirabe/src/downloader/rar_downloader.rs +++ b/crates/shirabe/src/downloader/rar_downloader.rs @@ -26,7 +26,7 @@ pub struct RarDownloader { impl RarDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs index 162e91c..f3683dd 100644 --- a/crates/shirabe/src/downloader/svn_downloader.rs +++ b/crates/shirabe/src/downloader/svn_downloader.rs @@ -9,6 +9,7 @@ use crate::config::Config; use crate::downloader::DownloaderInterface; use crate::downloader::VcsDownloaderBase; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::repository::VcsRepository; use crate::util::Filesystem; @@ -23,7 +24,7 @@ pub struct SvnDownloader { impl SvnDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -44,7 +45,7 @@ impl SvnDownloader { SvnUtil::clean_env(); let mut util = SvnUtil::new( url.to_string(), - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), ); @@ -126,7 +127,7 @@ impl SvnDownloader { let mut util = SvnUtil::new( url.to_string(), - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), ); @@ -186,7 +187,7 @@ impl SvnDownloader { ) -> anyhow::Result<String> { let mut util = SvnUtil::new( base_url.to_string(), - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), ); @@ -381,7 +382,7 @@ impl SvnDownloader { let mut util = SvnUtil::new( base_url, - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), ); diff --git a/crates/shirabe/src/downloader/tar_downloader.rs b/crates/shirabe/src/downloader/tar_downloader.rs index 09b6f5a..7314ecd 100644 --- a/crates/shirabe/src/downloader/tar_downloader.rs +++ b/crates/shirabe/src/downloader/tar_downloader.rs @@ -23,7 +23,7 @@ pub struct TarDownloader { impl TarDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs index 096e93d..736d433 100644 --- a/crates/shirabe/src/downloader/vcs_downloader.rs +++ b/crates/shirabe/src/downloader/vcs_downloader.rs @@ -17,6 +17,7 @@ use crate::downloader::ChangeReportInterface; use crate::downloader::DownloaderInterface; use crate::downloader::VcsCapableDownloaderInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::package::dumper::ArrayDumper; use crate::package::version::VersionGuesser; @@ -26,7 +27,7 @@ use crate::util::ProcessExecutor; #[derive(Debug)] pub struct VcsDownloaderBase { - pub io: Box<dyn IOInterface>, + pub io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub config: std::rc::Rc<std::cell::RefCell<Config>>, pub process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, pub filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -35,13 +36,14 @@ pub struct VcsDownloaderBase { impl VcsDownloaderBase { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, ) -> Self { - let process = process - .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(())))); + let process = process.unwrap_or_else(|| { + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))) + }); let filesystem = fs.unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None)))); Self { @@ -72,7 +74,7 @@ impl VcsDownloaderBase { pub trait VcsDownloader: DownloaderInterface + ChangeReportInterface + VcsCapableDownloaderInterface { - fn io(&self) -> &dyn IOInterface; + fn io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>>; fn io_mut(&mut self) -> &mut dyn IOInterface; fn config(&self) -> &std::rc::Rc<std::cell::RefCell<Config>>; fn config_mut(&mut self) -> &mut std::rc::Rc<std::cell::RefCell<Config>>; @@ -442,7 +444,7 @@ pub trait VcsDownloader: self.config().clone(), self.process().clone(), parser.clone(), - Some(self.io().clone_box()), + Some(self.io().clone()), ); let dumper = ArrayDumper::new(); diff --git a/crates/shirabe/src/downloader/xz_downloader.rs b/crates/shirabe/src/downloader/xz_downloader.rs index ca867df..fd83b28 100644 --- a/crates/shirabe/src/downloader/xz_downloader.rs +++ b/crates/shirabe/src/downloader/xz_downloader.rs @@ -22,7 +22,7 @@ pub struct XzDownloader { impl XzDownloader { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs index eb057e0..4c72b91 100644 --- a/crates/shirabe/src/downloader/zip_downloader.rs +++ b/crates/shirabe/src/downloader/zip_downloader.rs @@ -3,6 +3,8 @@ use crate::downloader::ArchiveDownloader; use crate::downloader::DownloaderInterface; use crate::downloader::FileDownloader; +use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::IniHelper; use crate::util::Platform; @@ -33,7 +35,7 @@ pub struct ZipDownloader { impl ZipDownloader { pub fn new( - io: Box<dyn crate::io::IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn crate::io::IOInterface>>, config: std::rc::Rc<std::cell::RefCell<crate::config::Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, event_dispatcher: Option< diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs index ae86fcf..3a037c9 100644 --- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs +++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs @@ -31,6 +31,7 @@ use crate::installer::InstallerEvent; use crate::installer::PackageEvent; use crate::io::ConsoleIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::plugin::CommandEvent; use crate::plugin::PreCommandRunEvent; use crate::repository::RepositoryInterface; @@ -62,7 +63,7 @@ pub enum Callable { #[derive(Debug)] pub struct EventDispatcher { pub(crate) composer: PartialComposerWeakHandle, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) loader: Option<ClassLoader>, pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, pub(crate) listeners: IndexMap<String, IndexMap<i64, Vec<Callable>>>, @@ -76,11 +77,13 @@ pub struct EventDispatcher { impl EventDispatcher { pub fn new( composer: PartialComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, ) -> Self { let process = process.unwrap_or_else(|| { - std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io))) + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))) }); let event_stack: Vec<String> = Vec::new(); let skip_scripts_env = @@ -609,7 +612,8 @@ impl EventDispatcher { // TODO(phase-b): IOInterface needs an `as_any` shim before // `instanceof ConsoleIO` can be expressed; treat io as a // generic IOInterface for now. - let _io_ref: &dyn IOInterface = &*self.io; + let _io_ref_guard = self.io.borrow(); + let _io_ref: &dyn IOInterface = &*_io_ref_guard; let downcast: Option<&ConsoleIO> = None; let output: ConsoleOutput = if let Some(_console_io) = downcast { // TODO(plugin): \ReflectionProperty to read private `output` from ConsoleIO @@ -1311,9 +1315,9 @@ impl EventDispatcher { // ---- helpers ---- - fn io_clone(&self) -> Box<dyn IOInterface> { + fn io_clone(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { // TODO(phase-b): IOInterface is not Clone — placeholder until io ownership is resolved. - todo!("clone Box<dyn IOInterface>") + todo!("clone std::rc::Rc<std::cell::RefCell<dyn IOInterface>>") } fn composer(&self) -> PartialComposerHandle { diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs index 1370080..0084c73 100644 --- a/crates/shirabe/src/factory.rs +++ b/crates/shirabe/src/factory.rs @@ -43,6 +43,7 @@ use crate::installer::LibraryInstaller; use crate::installer::MetapackageInstaller; use crate::installer::PluginInstaller; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::json::JsonValidationException; use crate::package::Locker; @@ -231,7 +232,7 @@ impl Factory { } pub fn create_config( - io: Option<&dyn IOInterface>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, cwd: Option<&str>, ) -> anyhow::Result<Config> { let cwd = match cwd { @@ -262,9 +263,9 @@ impl Factory { // load global config let global_config_path = format!("{}/config.json", config.get_str("home")?); - let mut file = JsonFile::new(global_config_path.clone(), None, io.map(|i| i.clone_box()))?; + let mut file = JsonFile::new(global_config_path.clone(), None, io.clone())?; if file.exists() { - if let Some(io_ref) = io { + if let Some(io_ref) = &io { io_ref.write_error3( &format!("Loading config file {}", file.get_path()), true, @@ -273,11 +274,11 @@ impl Factory { } // TODO(phase-b): validate_json_schema takes ownership of JsonFile; recreate it Self::validate_json_schema( - io, + io.clone(), ValidateJsonInput::File(JsonFile::new( global_config_path.clone(), None, - io.map(|i| i.clone_box()), + io.clone(), )?), JsonFile::LAX_SCHEMA, None, @@ -323,9 +324,9 @@ impl Factory { // load global auth file let auth_file_path = format!("{}/auth.json", config.get_str("home")?); - let mut auth_file = JsonFile::new(auth_file_path.clone(), None, io.map(|i| i.clone_box()))?; + let mut auth_file = JsonFile::new(auth_file_path.clone(), None, io.clone())?; if auth_file.exists() { - if let Some(io_ref) = io { + if let Some(io_ref) = &io { io_ref.write_error3( &format!("Loading config file {}", auth_file.get_path()), true, @@ -334,12 +335,8 @@ impl Factory { } // TODO(phase-b): validate_json_schema takes ownership; recreate JsonFile Self::validate_json_schema( - io, - ValidateJsonInput::File(JsonFile::new( - auth_file_path.clone(), - None, - io.map(|i| i.clone_box()), - )?), + io.clone(), + ValidateJsonInput::File(JsonFile::new(auth_file_path.clone(), None, io.clone())?), JsonFile::AUTH_SCHEMA, None, )?; @@ -433,7 +430,7 @@ impl Factory { /// Creates a Composer instance pub fn create_composer( &self, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, local_config: Option<LocalConfigInput>, disable_plugins: DisablePlugins, cwd: Option<&str>, @@ -465,7 +462,7 @@ impl Factory { if let Some(LocalConfigInput::Path(path)) = &local_config { composer_file = Some(path.clone()); - let mut file = JsonFile::new(path.clone(), None, Some(io.clone_box()))?; + let mut file = JsonFile::new(path.clone(), None, Some(io.clone()))?; if !file.exists() { let message = if path == "./composer.json" || path == "composer.json" { @@ -512,7 +509,7 @@ impl Factory { } // Load config and override with local config/auth config - let mut config = Self::create_config(Some(io), Some(&cwd))?; + let mut config = Self::create_config(Some(io.clone()), Some(&cwd))?; let is_global = local_config_source != Config::SOURCE_UNKNOWN && realpath(&config.get_str("home")?) == realpath(&dirname(&local_config_source)); config.merge(&local_config_data, &local_config_source); @@ -531,7 +528,7 @@ impl Factory { JsonFile::new( realpath(composer_file_path).unwrap_or_default(), None, - Some(io.clone_box()), + Some(io.clone()), )?, false, ))); @@ -542,7 +539,7 @@ impl Factory { dirname(&realpath(composer_file_path).unwrap_or_default()) ), None, - Some(io.clone_box()), + Some(io.clone()), )?; if local_auth_file.exists() { io.write_error3( @@ -566,7 +563,7 @@ impl Factory { } // make sure we load the auth env again over the local auth.json + composer.json config - Self::load_composer_auth_env(&mut config, Some(io))?; + Self::load_composer_auth_env(&mut config, Some(io.clone()))?; let vendor_dir = config.get_str("vendor-dir")?; @@ -620,10 +617,10 @@ impl Factory { } let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( - Self::create_http_downloader(io, &config, IndexMap::new())?, + Self::create_http_downloader(io.clone(), &config, IndexMap::new())?, )); let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new( - Some(io.clone_box()), + Some(io.clone()), ))); let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new( http_downloader.clone(), @@ -635,7 +632,7 @@ impl Factory { let dispatcher = { let mut d = EventDispatcher::new( PartialComposerWeakHandle::from_weak(composer_weak.clone()), - io.clone_box(), + io.clone(), Some(process.clone()), ); d.set_run_scripts(!disable_scripts); @@ -645,7 +642,7 @@ impl Factory { // initialize repository manager let rm = std::rc::Rc::new(std::cell::RefCell::new(RepositoryFactory::manager( - io, + io.clone(), &config, Some(http_downloader.clone()), Some(dispatcher.clone()), @@ -665,14 +662,14 @@ impl Factory { config.clone(), process.clone(), parser.clone(), - Some(io.clone_box()), + Some(io.clone()), ); let mut loader = self.load_root_package( rm.clone(), config.clone(), parser, guesser, - io.clone_box(), + io.clone(), ); let package = loader.load( local_config_data @@ -688,7 +685,7 @@ impl Factory { // load local repository self.add_local_repository( - io, + io.clone(), &mut rm.borrow_mut(), &vendor_dir, composer.get_package().clone(), @@ -700,7 +697,7 @@ impl Factory { let im = std::rc::Rc::new(std::cell::RefCell::new( self.create_installation_manager( r#loop.clone(), - io.clone_box(), + io.clone(), Some(dispatcher.clone()), ), )); @@ -709,7 +706,7 @@ impl Factory { if let PartialOrFullComposer::Full(ref mut composer_full) = composer { // initialize download manager let dm = self.create_download_manager( - io, + io.clone(), &config, &http_downloader, &process, @@ -719,7 +716,7 @@ impl Factory { // initialize autoload generator let generator = - AutoloadGenerator::new(dispatcher.clone(), Some(io.clone_box())); + AutoloadGenerator::new(dispatcher.clone(), Some(io.clone())); composer_full.set_autoload_generator(std::rc::Rc::new( std::cell::RefCell::new(generator), )); @@ -731,7 +728,7 @@ impl Factory { } // add installers to the manager (must happen after download manager is created since they read it out of $composer) - self.create_default_installers(&im, &composer, io, Some(&process)); + self.create_default_installers(&im, &composer, io.clone(), Some(&process)); // init locker if possible if let PartialOrFullComposer::Full(ref mut composer_full) = composer { @@ -754,7 +751,7 @@ impl Factory { } let locker = Locker::new( - io.clone_box(), + io.clone(), JsonFile::new( if lock_enabled { lock_file @@ -762,7 +759,7 @@ impl Factory { Platform::get_dev_null() }, None, - Some(io.clone_box()), + Some(io.clone()), )?, im.clone(), &file_get_contents(composer_file_path).unwrap_or_default(), @@ -781,12 +778,8 @@ impl Factory { 448, ); let locker = Locker::new( - io.clone_box(), - JsonFile::new( - Platform::get_dev_null(), - None, - Some(io.clone_box()), - )?, + io.clone(), + JsonFile::new(Platform::get_dev_null(), None, Some(io.clone()))?, im.clone(), &lock_contents, process.clone(), @@ -799,7 +792,7 @@ impl Factory { if let Some(full_composer) = composer.as_full_mut() { let global_composer = if !full_composer.is_global() { self.create_global_composer( - io, + io.clone(), &*config.borrow(), disable_plugins, disable_scripts, @@ -809,7 +802,7 @@ impl Factory { None }; let mut pm = self.create_plugin_manager( - io, + &*io.borrow(), ComposerWeakHandle::from_weak(composer_weak.clone()), global_composer, disable_plugins, @@ -857,19 +850,19 @@ impl Factory { } pub fn create_global( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, disable_plugins: DisablePlugins, disable_scripts: bool, ) -> Option<PartialComposerHandle> { let factory = Self; - let config = Self::create_config(Some(io), None).ok()?; + let config = Self::create_config(Some(io.clone()), None).ok()?; factory.create_global_composer(io, &config, disable_plugins, disable_scripts, true) } fn add_local_repository( &self, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, rm: &mut RepositoryManager, vendor_dir: &str, root_package: RootPackageInterfaceHandle, @@ -883,7 +876,7 @@ impl Factory { JsonFile::new( format!("{}/composer/installed.json", vendor_dir), None, - Some(io.clone_box()), + Some(io.clone()), ) .expect("installed.json path is always valid"), true, @@ -896,7 +889,7 @@ impl Factory { fn create_global_composer( &self, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &Config, disable_plugins: DisablePlugins, disable_scripts: bool, @@ -913,7 +906,7 @@ impl Factory { }; match self.create_composer( - io, + io.clone(), Some(LocalConfigInput::Path(format!( "{}/composer.json", config.get_str("home").ok()? @@ -937,7 +930,7 @@ impl Factory { pub fn create_download_manager( &self, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: &std::rc::Rc<std::cell::RefCell<HttpDownloader>>, process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, @@ -950,7 +943,7 @@ impl Factory { .unwrap_or(0); let cache = if cache_files_ttl > 0 { let mut cache = Cache::new( - io.clone_box(), + io.clone(), &config.borrow_mut().get_str("cache-files-dir")?, Some("a-z0-9_./"), None, @@ -972,7 +965,7 @@ impl Factory { process.clone(), )))); - let mut dm = DownloadManager::new(io.clone_box(), false, Some(fs.clone())); + let mut dm = DownloadManager::new(io.clone(), false, Some(fs.clone())); let preferred = config.borrow_mut().get("preferred-install"); match preferred.as_string() { Some("dist") => { @@ -1006,7 +999,7 @@ impl Factory { dm.set_downloader( "git", Box::new(GitDownloader::new( - io.clone_box(), + io.clone(), config.clone(), Some(process.clone()), Some(fs.clone()), @@ -1015,7 +1008,7 @@ impl Factory { dm.set_downloader( "svn", Box::new(SvnDownloader::new( - io.clone_box(), + io.clone(), config.clone(), process.clone(), fs.clone(), @@ -1024,7 +1017,7 @@ impl Factory { dm.set_downloader( "fossil", Box::new(FossilDownloader::new( - io.clone_box(), + io.clone(), config.clone(), process.clone(), fs.clone(), @@ -1033,7 +1026,7 @@ impl Factory { dm.set_downloader( "hg", Box::new(HgDownloader::new( - io.clone_box(), + io.clone(), config.clone(), process.clone(), fs.clone(), @@ -1042,7 +1035,7 @@ impl Factory { dm.set_downloader( "perforce", Box::new(PerforceDownloader::new( - io.clone_box(), + io.clone(), config.clone(), process.clone(), fs.clone(), @@ -1051,7 +1044,7 @@ impl Factory { dm.set_downloader( "zip", Box::new(ZipDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1063,7 +1056,7 @@ impl Factory { dm.set_downloader( "rar", Box::new(RarDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1075,7 +1068,7 @@ impl Factory { dm.set_downloader( "tar", Box::new(TarDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1087,7 +1080,7 @@ impl Factory { dm.set_downloader( "gzip", Box::new(GzipDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1099,7 +1092,7 @@ impl Factory { dm.set_downloader( "xz", Box::new(XzDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1111,7 +1104,7 @@ impl Factory { dm.set_downloader( "phar", Box::new(PharDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1123,7 +1116,7 @@ impl Factory { dm.set_downloader( "file", Box::new(FileDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1135,7 +1128,7 @@ impl Factory { dm.set_downloader( "path", Box::new(PathDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader.clone(), event_dispatcher.cloned(), @@ -1181,7 +1174,7 @@ impl Factory { pub fn create_installation_manager( &self, r#loop: std::rc::Rc<std::cell::RefCell<Loop>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, ) -> InstallationManager { InstallationManager::new(r#loop, io, event_dispatcher) @@ -1191,7 +1184,7 @@ impl Factory { &self, im: &std::rc::Rc<std::cell::RefCell<InstallationManager>>, composer: &PartialOrFullComposer, - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, process: Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, ) { let fs = std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new( @@ -1221,7 +1214,7 @@ impl Factory { // TODO(phase-b): BinaryInstaller is a PHP class so it can't be cloned. Sharing requires // Rc<RefCell<BinaryInstaller>>; for now construct one per installer. let _binary_installer = BinaryInstaller::new( - io.clone_box(), + io.clone(), bin_dir.clone(), bin_compat.clone(), Some(fs.clone()), @@ -1251,13 +1244,13 @@ impl Factory { config: std::rc::Rc<std::cell::RefCell<Config>>, parser: VersionParser, guesser: VersionGuesser, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> RootPackageLoader { RootPackageLoader::new(rm, config, Some(parser), Some(guesser), Some(io)) } pub fn create( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: Option<LocalConfigInput>, disable_plugins: DisablePlugins, disable_scripts: bool, @@ -1296,7 +1289,7 @@ impl Factory { /// If you are calling this in a plugin, you probably should instead use `$composer->getLoop()->getHttpDownloader()` pub fn create_http_downloader( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, options: IndexMap<String, PhpMixed>, ) -> anyhow::Result<HttpDownloader> { @@ -1366,7 +1359,7 @@ impl Factory { array_replace_recursive(http_downloader_options, options.clone()); } let http_downloader_result: anyhow::Result<HttpDownloader> = Ok(HttpDownloader::new( - io.clone_box(), + io.clone(), config.clone(), http_downloader_options, disable_tls, @@ -1402,7 +1395,7 @@ impl Factory { fn load_composer_auth_env( config: &mut Config, - io: Option<&dyn IOInterface>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> anyhow::Result<()> { let composer_auth_env = Platform::get_env("COMPOSER_AUTH"); let composer_auth_env_str = match composer_auth_env { @@ -1420,7 +1413,7 @@ impl Factory { })); } - if let Some(io_ref) = io { + if let Some(io_ref) = &io { io_ref.write_error3( "Loading auth config from COMPOSER_AUTH", true, @@ -1468,7 +1461,7 @@ impl Factory { } fn validate_json_schema( - io: Option<&dyn IOInterface>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, file_or_data: ValidateJsonInput, schema: i64, source: Option<&str>, diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs index c3ef76f..70062aa 100644 --- a/crates/shirabe/src/installer.rs +++ b/crates/shirabe/src/installer.rs @@ -69,6 +69,7 @@ use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFil use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::AliasPackage; use crate::package::CompletePackage; use crate::package::CompletePackageInterface; @@ -104,7 +105,7 @@ use shirabe_semver::constraint::SimpleConstraint; #[derive(Debug)] pub struct Installer { - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, pub(crate) package: RootPackageInterfaceHandle, // TODO can we get rid of the below and just use the package itself? @@ -161,7 +162,7 @@ impl Installer { pub const ERROR_TRANSPORT_EXCEPTION: i64 = 100; pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, package: RootPackageInterfaceHandle, download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>, @@ -171,7 +172,7 @@ impl Installer { event_dispatcher: std::rc::Rc<std::cell::RefCell<EventDispatcher>>, autoload_generator: std::rc::Rc<std::cell::RefCell<AutoloadGenerator>>, ) -> Self { - let suggested_packages_reporter = SuggestedPackagesReporter::new(io.clone_box()); + let suggested_packages_reporter = SuggestedPackagesReporter::new(io.clone()); let platform_requirement_filter = PlatformRequirementFilterFactory::ignore_nothing(); let write_lock = config.borrow_mut().get("lock").as_bool().unwrap_or(false); @@ -325,7 +326,7 @@ impl Installer { { self.installation_manager .borrow_mut() - .notify_installs(&*self.io); + .notify_installs(&*self.io.borrow()); } return Err(e); } @@ -343,7 +344,7 @@ impl Installer { { self.installation_manager .borrow_mut() - .notify_installs(&*self.io); + .notify_installs(&*self.io.borrow()); } if self.update { @@ -650,7 +651,7 @@ impl Installer { let _ = allow_list; } - // TODO(phase-b): create_pool takes owned Request, Box<dyn IOInterface>, Option<Rc<...>> + // TODO(phase-b): create_pool takes owned Request, std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, Option<Rc<...>> // but locally we only have refs. PHP classes (IO, dispatcher) shouldn't Clone. let mut pool: Option<Pool> = { let _ = (&request, &self.event_dispatcher, &policy, &repository_set); @@ -1095,7 +1096,7 @@ impl Installer { } drop(root_requires); - // TODO(phase-b): create_pool takes owned Request, Box<dyn IOInterface>, Option<Rc<...>> + // TODO(phase-b): create_pool takes owned Request, std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, Option<Rc<...>> let pool: Pool = { let _ = (&request, &self.io, &self.event_dispatcher, &repository_set); todo!() @@ -1677,7 +1678,10 @@ impl Installer { } /// Create Installer - pub fn create(io: Box<dyn IOInterface>, composer: &PartialComposerHandle) -> Self { + pub fn create( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + composer: &PartialComposerHandle, + ) -> Self { let composer = crate::composer::composer_full(composer); Self::new( io, diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs index 54e1143..a555d83 100644 --- a/crates/shirabe/src/installer/binary_installer.rs +++ b/crates/shirabe/src/installer/binary_installer.rs @@ -10,6 +10,7 @@ use shirabe_php_shim::{ }; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::util::Filesystem; use crate::util::Platform; @@ -21,14 +22,14 @@ use crate::util::Silencer; pub struct BinaryInstaller { pub(crate) bin_dir: String, pub(crate) bin_compat: String, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>, vendor_dir: Option<String>, } impl BinaryInstaller { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, bin_dir: String, bin_compat: String, filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index 3e455b4..5f312b6 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -23,6 +23,7 @@ use crate::installer::PackageEvents; use crate::installer::PluginInstaller; use crate::io::ConsoleIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::package::PackageInterfaceHandle; use crate::repository::InstalledRepositoryInterface; @@ -39,7 +40,7 @@ pub struct InstallationManager { /// @var array<string, array<PackageInterface>> notifiable_packages: IndexMap<String, Vec<PackageInterfaceHandle>>, loop_: std::rc::Rc<std::cell::RefCell<Loop>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, output_progress: bool, } @@ -47,7 +48,7 @@ pub struct InstallationManager { impl InstallationManager { pub fn new( loop_: std::rc::Rc<std::cell::RefCell<Loop>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, ) -> Self { Self { diff --git a/crates/shirabe/src/installer/installer_event.rs b/crates/shirabe/src/installer/installer_event.rs index 3c02b7f..5adde1c 100644 --- a/crates/shirabe/src/installer/installer_event.rs +++ b/crates/shirabe/src/installer/installer_event.rs @@ -9,7 +9,7 @@ use crate::io::IOInterface; pub struct InstallerEvent { inner: Event, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, execute_operations: bool, transaction: Transaction, @@ -19,7 +19,7 @@ impl InstallerEvent { pub fn new( event_name: String, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, execute_operations: bool, transaction: Transaction, @@ -39,8 +39,8 @@ impl InstallerEvent { &self.composer } - pub fn get_io(&self) -> &dyn IOInterface { - self.io.as_ref() + pub fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { + self.io.clone() } pub fn is_dev_mode(&self) -> bool { diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index 332bdea..17ec8d4 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -28,7 +28,7 @@ pub struct LibraryInstaller { pub(crate) composer: PartialComposerWeakHandle, pub(crate) vendor_dir: String, pub(crate) download_manager: Option<std::rc::Rc<std::cell::RefCell<DownloadManager>>>, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) r#type: Option<String>, pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>, pub(crate) binary_installer: BinaryInstaller, @@ -37,7 +37,7 @@ pub struct LibraryInstaller { impl LibraryInstaller { /// Initializes library installer. pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, composer: PartialComposerWeakHandle, r#type: Option<String>, filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs index cda7bf0..fe41dd3 100644 --- a/crates/shirabe/src/installer/metapackage_installer.rs +++ b/crates/shirabe/src/installer/metapackage_installer.rs @@ -5,6 +5,7 @@ use crate::dependency_resolver::operation::UninstallOperation; use crate::dependency_resolver::operation::UpdateOperation; use crate::installer::InstallerInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::io_interface; use crate::package::PackageInterface; use crate::package::PackageInterfaceHandle; @@ -14,11 +15,11 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; #[derive(Debug)] pub struct MetapackageInstaller { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl MetapackageInstaller { - pub fn new(io: Box<dyn IOInterface>) -> Self { + pub fn new(io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) -> Self { Self { io } } } diff --git a/crates/shirabe/src/installer/package_event.rs b/crates/shirabe/src/installer/package_event.rs index d0aecc8..342732d 100644 --- a/crates/shirabe/src/installer/package_event.rs +++ b/crates/shirabe/src/installer/package_event.rs @@ -11,7 +11,7 @@ use indexmap::IndexMap; pub struct PackageEvent { inner: Event, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, local_repo: Box<dyn RepositoryInterface>, operations: Vec<Box<dyn OperationInterface>>, @@ -22,7 +22,7 @@ impl PackageEvent { pub fn new( event_name: String, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, local_repo: Box<dyn RepositoryInterface>, operations: Vec<Box<dyn OperationInterface>>, @@ -47,8 +47,8 @@ impl PackageEvent { &self.composer } - pub fn get_io(&self) -> &dyn IOInterface { - self.io.as_ref() + pub fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { + self.io.clone() } pub fn is_dev_mode(&self) -> bool { diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs index 2135bb3..deadcb7 100644 --- a/crates/shirabe/src/installer/plugin_installer.rs +++ b/crates/shirabe/src/installer/plugin_installer.rs @@ -5,6 +5,7 @@ use crate::installer::BinaryInstaller; use crate::installer::InstallerInterface; use crate::installer::LibraryInstaller; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::package::PackageInterfaceHandle; use crate::plugin::PluginManager; @@ -21,7 +22,7 @@ pub struct PluginInstaller { impl PluginInstaller { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, composer: PartialComposerWeakHandle, fs: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, binary_installer: Option<BinaryInstaller>, diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs index fc4165a..8faae9c 100644 --- a/crates/shirabe/src/installer/suggested_packages_reporter.rs +++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs @@ -1,6 +1,7 @@ //! ref: composer/src/Composer/Installer/SuggestedPackagesReporter.php use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterface; use crate::repository::InstalledRepository; use crate::repository::RepositoryInterface; @@ -11,7 +12,7 @@ use shirabe_external_packages::symfony::component::console::formatter::OutputFor #[derive(Debug)] pub struct SuggestedPackagesReporter { suggested_packages: Vec<IndexMap<String, String>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl SuggestedPackagesReporter { @@ -19,7 +20,7 @@ impl SuggestedPackagesReporter { pub const MODE_BY_PACKAGE: i64 = 2; pub const MODE_BY_SUGGESTION: i64 = 4; - pub fn new(io: Box<dyn IOInterface>) -> Self { + pub fn new(io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) -> Self { Self { suggested_packages: Vec::new(), io, diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index 867eb0e..ed87b71 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -129,39 +129,9 @@ impl BufferIO { } } -// TODO(phase-b): PHP `class BufferIO extends ConsoleIO` — delegate all IOInterface, -// LoggerInterface, and BaseIO methods to `self.inner` (ConsoleIO). -impl shirabe_external_packages::psr::log::LoggerInterface for BufferIO { - fn emergency(&self, message: &str, context: &[(&str, &str)]) { - self.inner.emergency(message, context) - } - fn alert(&self, message: &str, context: &[(&str, &str)]) { - self.inner.alert(message, context) - } - fn critical(&self, message: &str, context: &[(&str, &str)]) { - self.inner.critical(message, context) - } - fn error(&self, message: &str, context: &[(&str, &str)]) { - self.inner.error(message, context) - } - fn warning(&self, message: &str, context: &[(&str, &str)]) { - self.inner.warning(message, context) - } - fn notice(&self, message: &str, context: &[(&str, &str)]) { - self.inner.notice(message, context) - } - fn info(&self, message: &str, context: &[(&str, &str)]) { - self.inner.info(message, context) - } - fn debug(&self, message: &str, context: &[(&str, &str)]) { - self.inner.debug(message, context) - } - fn log(&self, level: &str, message: &str, context: &[(&str, &str)]) { - self.inner.log(level, message, context) - } -} - -impl crate::io::IOInterface for BufferIO { +// TODO(phase-b): PHP `class BufferIO extends ConsoleIO` — delegate all +// IOInterface and BaseIO methods to `self.inner` (ConsoleIO). +impl crate::io::IOInterfaceImmutable for BufferIO { fn is_interactive(&self) -> bool { self.inner.is_interactive() } @@ -247,6 +217,20 @@ impl crate::io::IOInterface for BufferIO { ) -> indexmap::IndexMap<String, Option<String>> { self.inner.get_authentication(repository_name) } + fn error(&self, message: &str, context: &[(&str, &str)]) { + self.inner.error(message, context) + } + + fn warning(&self, message: &str, context: &[(&str, &str)]) { + self.inner.warning(message, context) + } + + fn debug(&self, message: &str, context: &[(&str, &str)]) { + self.inner.debug(message, context) + } +} + +impl crate::io::IOInterfaceMutable for BufferIO { fn set_authentication( &mut self, repository_name: String, @@ -261,6 +245,8 @@ impl crate::io::IOInterface for BufferIO { } } +impl crate::io::IOInterface for BufferIO {} + impl crate::io::BaseIO for BufferIO { fn authentications( &self, diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index b4bae47..2b1c334 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -5,7 +5,6 @@ use crate::io::io_interface; use indexmap::IndexMap; use indexmap::indexmap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_external_packages::psr::log::LoggerInterface; use shirabe_external_packages::symfony::component::console::helper::HelperSet; use shirabe_external_packages::symfony::component::console::helper::ProgressBar; use shirabe_external_packages::symfony::component::console::helper::Table; @@ -25,6 +24,8 @@ use std::cell::RefCell; use crate::io::BaseIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; +use crate::io::IOInterfaceMutable; use crate::question::StrictConfirmationQuestion; use crate::util::Silencer; @@ -378,49 +379,7 @@ impl ConsoleIO { } } -impl LoggerInterface for ConsoleIO { - // TODO(phase-b): BaseIO's emergency/alert/.../log take PhpMixed and - // IndexMap<String, Box<PhpMixed>> while LoggerInterface takes &str and - // &[(&str, &str)]. Delegation requires reconciling signatures; for now, - // mirror NullIO and panic via todo!(). - fn emergency(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn alert(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn critical(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn error(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn warning(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn notice(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn info(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn debug(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn log(&self, _level: &str, _message: &str, _context: &[(&str, &str)]) { - todo!() - } -} - -impl IOInterface for ConsoleIO { +impl IOInterfaceImmutable for ConsoleIO { fn is_interactive(&self) -> bool { self.input.is_interactive() } @@ -690,6 +649,20 @@ impl IOInterface for ConsoleIO { <Self as BaseIO>::get_authentication(self, repository_name) } + fn error(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn warning(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn debug(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } +} + +impl IOInterfaceMutable for ConsoleIO { fn set_authentication( &mut self, repository_name: String, @@ -704,6 +677,8 @@ impl IOInterface for ConsoleIO { } } +impl IOInterface for ConsoleIO {} + impl BaseIO for ConsoleIO { fn authentications( &self, diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs index 1d97700..aac1488 100644 --- a/crates/shirabe/src/io/io_interface.rs +++ b/crates/shirabe/src/io/io_interface.rs @@ -2,8 +2,9 @@ use crate::config::Config; use indexmap::IndexMap; -use shirabe_external_packages::psr::log::LoggerInterface; use shirabe_php_shim::PhpMixed; +use std::cell::RefCell; +use std::rc::Rc; pub const QUIET: i64 = 1; pub const NORMAL: i64 = 2; @@ -11,7 +12,18 @@ pub const VERBOSE: i64 = 4; pub const VERY_VERBOSE: i64 = 8; pub const DEBUG: i64 = 16; -pub trait IOInterface: LoggerInterface + std::fmt::Debug { +// In PHP this is `IOInterface extends LoggerInterface`. Shirabe does not +// integrate with the PHP runtime, so there is no need for a separate +// `LoggerInterface` entity on the Rust side: the LoggerInterface methods that +// Composer actually invokes through an IO are folded directly into this trait +// (each is annotated as originating from LoggerInterface). +// +// On the Rust side the interface is split into an immutable part (`&self` +// methods, below) and a mutable part (`IOInterfaceMutable`, the `&mut self` +// methods). The shared handle `Rc<RefCell<dyn IOInterface>>` implements only +// `IOInterfaceImmutable`, so the mutating methods are reachable only via +// `io.borrow_mut()` — enforced at compile time rather than at runtime. +pub trait IOInterfaceImmutable: std::fmt::Debug { fn is_interactive(&self) -> bool; fn is_verbose(&self) -> bool; @@ -112,6 +124,19 @@ pub trait IOInterface: LoggerInterface + std::fmt::Debug { fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>>; + // From PHP `LoggerInterface` (which `IOInterface` extends). Only the + // variants Composer actually calls through an IO are kept. + fn error(&self, message: &str, context: &[(&str, &str)]); + + fn warning(&self, message: &str, context: &[(&str, &str)]); + + fn debug(&self, message: &str, context: &[(&str, &str)]); +} + +// The `&mut self` part of PHP `IOInterface`. The shared handle does NOT +// implement this trait, so these methods can only be reached through +// `io.borrow_mut()`. +pub trait IOInterfaceMutable { fn set_authentication( &mut self, repository_name: String, @@ -120,8 +145,190 @@ pub trait IOInterface: LoggerInterface + std::fmt::Debug { ); fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()>; +} + +// PHP `IOInterface`. This is the type used for the shared trait object +// `dyn IOInterface`; its vtable carries both the immutable and mutable methods. +pub trait IOInterface: IOInterfaceImmutable + IOInterfaceMutable {} + +// Shared-ownership handle for a PHP IO instance (reference semantics). It +// exposes only the immutable surface; mutating methods (`set_authentication`, +// `load_configuration`) are reached via `io.borrow_mut()`. Because the handle +// does not implement `IOInterfaceMutable`, calling those directly on the handle +// is a compile error rather than a runtime panic. +impl IOInterfaceImmutable for Rc<RefCell<dyn IOInterface>> { + fn is_interactive(&self) -> bool { + self.borrow().is_interactive() + } + + fn is_verbose(&self) -> bool { + self.borrow().is_verbose() + } + + fn is_very_verbose(&self) -> bool { + self.borrow().is_very_verbose() + } + + fn is_debug(&self) -> bool { + self.borrow().is_debug() + } + + fn is_decorated(&self) -> bool { + self.borrow().is_decorated() + } + + fn write(&self, message: &str) { + self.borrow().write(message) + } + + fn write2(&self, message: &str, newline: bool) { + self.borrow().write2(message, newline) + } + + fn write_no_newline(&self, message: &str) { + self.borrow().write_no_newline(message) + } + + fn write3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write3(message, newline, verbosity) + } + + fn write_error(&self, message: &str) { + self.borrow().write_error(message) + } + + fn write_error2(&self, message: &str, newline: bool) { + self.borrow().write_error2(message, newline) + } + + fn write_error_no_newline(&self, message: &str) { + self.borrow().write_error_no_newline(message) + } + + fn write_error3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_error3(message, newline, verbosity) + } + + fn write_raw(&self, message: &str) { + self.borrow().write_raw(message) + } + + fn write_raw2(&self, message: &str, newline: bool) { + self.borrow().write_raw2(message, newline) + } + + fn write_raw3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_raw3(message, newline, verbosity) + } + + fn write_error_raw(&self, message: &str) { + self.borrow().write_error_raw(message) + } + + fn write_error_raw2(&self, message: &str, newline: bool) { + self.borrow().write_error_raw2(message, newline) + } + + fn write_error_raw3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_error_raw3(message, newline, verbosity) + } + + fn overwrite(&self, message: &str) { + self.borrow().overwrite(message) + } + + fn overwrite2(&self, message: &str, newline: bool) { + self.borrow().overwrite2(message, newline) + } + + fn overwrite3(&self, message: &str, newline: bool, size: Option<i64>) { + self.borrow().overwrite3(message, newline, size) + } + + fn overwrite4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) { + self.borrow().overwrite4(message, newline, size, verbosity) + } + + fn overwrite_error(&self, message: &str) { + self.borrow().overwrite_error(message) + } + + fn overwrite_error2(&self, message: &str, newline: bool) { + self.borrow().overwrite_error2(message, newline) + } + + fn overwrite_error3(&self, message: &str, newline: bool, size: Option<i64>) { + self.borrow().overwrite_error3(message, newline, size) + } + + fn overwrite_error4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) { + self.borrow() + .overwrite_error4(message, newline, size, verbosity) + } + + fn ask(&self, question: String, default: PhpMixed) -> PhpMixed { + self.borrow().ask(question, default) + } + + fn ask_confirmation(&self, question: String, default: bool) -> bool { + self.borrow().ask_confirmation(question, default) + } + + fn ask_and_validate( + &self, + question: String, + validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, + attempts: Option<i64>, + default: PhpMixed, + ) -> PhpMixed { + self.borrow() + .ask_and_validate(question, validator, attempts, default) + } + + fn ask_and_hide_answer(&self, question: String) -> Option<String> { + self.borrow().ask_and_hide_answer(question) + } + + fn select( + &self, + question: String, + choices: Vec<String>, + default: PhpMixed, + attempts: PhpMixed, + error_message: String, + multiselect: bool, + ) -> PhpMixed { + self.borrow().select( + question, + choices, + default, + attempts, + error_message, + multiselect, + ) + } + + fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>> { + self.borrow().get_authentications() + } + + fn has_authentication(&self, repository_name: &str) -> bool { + self.borrow().has_authentication(repository_name) + } + + fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>> { + self.borrow().get_authentication(repository_name) + } + + fn error(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().error(message, context) + } + + fn warning(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().warning(message, context) + } - fn clone_box(&self) -> Box<dyn IOInterface> { - todo!() + fn debug(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().debug(message, context) } } diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs index e3cfdcf..b0e11f1 100644 --- a/crates/shirabe/src/io/null_io.rs +++ b/crates/shirabe/src/io/null_io.rs @@ -2,7 +2,8 @@ use crate::io::BaseIO; use crate::io::IOInterface; -use shirabe_external_packages::psr::log::LoggerInterface; +use crate::io::IOInterfaceImmutable; +use crate::io::IOInterfaceMutable; use shirabe_php_shim::PhpMixed; #[derive(Debug)] @@ -18,7 +19,7 @@ impl NullIO { } } -impl IOInterface for NullIO { +impl IOInterfaceImmutable for NullIO { fn is_interactive(&self) -> bool { false } @@ -109,6 +110,20 @@ impl IOInterface for NullIO { <Self as BaseIO>::get_authentication(self, repository_name) } + fn error(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn warning(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn debug(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } +} + +impl IOInterfaceMutable for NullIO { fn set_authentication( &mut self, repository_name: String, @@ -123,6 +138,8 @@ impl IOInterface for NullIO { } } +impl IOInterface for NullIO {} + impl BaseIO for NullIO { fn authentications( &self, @@ -136,41 +153,3 @@ impl BaseIO for NullIO { &mut self.authentications } } - -impl LoggerInterface for NullIO { - fn emergency(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn alert(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn critical(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn error(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn warning(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn notice(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn info(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn debug(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn log(&self, level: &str, message: &str, context: &[(&str, &str)]) { - todo!() - } -} diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs index 3340cae..2450b0a 100644 --- a/crates/shirabe/src/json/json_file.rs +++ b/crates/shirabe/src/json/json_file.rs @@ -19,6 +19,7 @@ use shirabe_php_shim::{ use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonValidationException; use crate::util::Filesystem; use crate::util::HttpDownloader; @@ -31,7 +32,7 @@ pub struct JsonFile { /// @var ?HttpDownloader http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, /// @var ?IOInterface - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, /// @var string indent: String, } @@ -69,7 +70,7 @@ impl JsonFile { pub fn new( path: String, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> Result<Self> { if http_downloader.is_none() && Preg::is_match(r"{^https?://}i", &path).unwrap_or(false) { return Err(InvalidArgumentException { diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs index e235e31..1b4d155 100644 --- a/crates/shirabe/src/package/loader/root_package_loader.rs +++ b/crates/shirabe/src/package/loader/root_package_loader.rs @@ -8,6 +8,7 @@ use shirabe_php_shim::{ use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::CompletePackageInterface; use crate::package::PackageInterface; use crate::package::RootAliasPackage; @@ -30,7 +31,7 @@ pub struct RootPackageLoader { manager: std::rc::Rc<std::cell::RefCell<RepositoryManager>>, config: std::rc::Rc<std::cell::RefCell<Config>>, version_guesser: VersionGuesser, - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, } impl RootPackageLoader { @@ -39,17 +40,17 @@ impl RootPackageLoader { config: std::rc::Rc<std::cell::RefCell<Config>>, parser: Option<VersionParser>, version_guesser: Option<VersionGuesser>, - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> Self { let inner = ArrayLoader::new(parser, true); let version_guesser = version_guesser.unwrap_or_else(|| { - let mut process_executor = ProcessExecutor::new(io.as_deref().map(|i| i.clone_box())); + let mut process_executor = ProcessExecutor::new(io.clone()); process_executor.enable_async(); VersionGuesser::new( config.clone(), std::rc::Rc::new(std::cell::RefCell::new(process_executor)), inner.version_parser.clone(), - io.as_ref().map(|i| i.clone_box()), + io.clone(), ) }); Self { diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index bdeb391..8ba2cc2 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -60,7 +60,7 @@ pub struct Locker { impl Locker { /// Initializes packages locker. pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, lock_file: JsonFile, installation_manager: std::rc::Rc<std::cell::RefCell<InstallationManager>>, composer_file_contents: &str, diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs index 47054a4..c7ffd65 100644 --- a/crates/shirabe/src/package/version/version_guesser.rs +++ b/crates/shirabe/src/package/version/version_guesser.rs @@ -37,7 +37,7 @@ pub struct VersionGuesser { version_parser: VersionParser, /// @var IOInterface|null - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, } /// PHP: @phpstan-type Version array{version, commit, pretty_version, feature_version?, feature_pretty_version?} @@ -55,7 +55,7 @@ impl VersionGuesser { config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, version_parser: VersionParser, - io: Option<Box<dyn IOInterface>>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> Self { Self { config, @@ -284,7 +284,7 @@ impl VersionGuesser { GitUtil::check_for_repo_ownership_error( &self.process.borrow().get_error_output(), path, - self.io.as_deref(), + self.io.clone(), ); if version.is_none() || is_detached { diff --git a/crates/shirabe/src/platform/hhvm_detector.rs b/crates/shirabe/src/platform/hhvm_detector.rs index 736b4b2..3566f1f 100644 --- a/crates/shirabe/src/platform/hhvm_detector.rs +++ b/crates/shirabe/src/platform/hhvm_detector.rs @@ -51,7 +51,7 @@ impl HhvmDetector { let hhvm_path = finder.find("hhvm", None, &[]); if let Some(hhvm_path) = hhvm_path { let executor = self.process_executor.get_or_insert_with(|| { - std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(()))) + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))) }); let mut version_output = shirabe_php_shim::PhpMixed::Null; let cmd = shirabe_php_shim::PhpMixed::List( diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index ddde387..a03fcaa 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -21,6 +21,7 @@ use crate::composer::{ComposerHandle, ComposerWeakHandle}; use crate::event_dispatcher::EventSubscriberInterface; use crate::installer::InstallerInterface; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::CompletePackage; use crate::package::Link; use crate::package::Locker; @@ -51,7 +52,7 @@ pub enum DisablePlugins { #[derive(Debug)] pub struct PluginManager { pub(crate) composer: ComposerWeakHandle, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) global_composer: Option<PartialComposerHandle>, pub(crate) version_parser: VersionParser, pub(crate) disable_plugins: DisablePlugins, @@ -72,7 +73,7 @@ static mut CLASS_COUNTER: i64 = 0; impl PluginManager { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, composer: ComposerWeakHandle, global_composer: Option<PartialComposerHandle>, disable_plugins: DisablePlugins, @@ -475,7 +476,7 @@ impl PluginManager { String::new() } )); - plugin.activate(&self.composer_full(), &*self.io); + plugin.activate(&self.composer_full(), &*self.io.borrow()); // TODO(plugin): if plugin is EventSubscriberInterface, hook into the event dispatcher // The PHP code calls $this->composer->getEventDispatcher()->addSubscriber($plugin); @@ -502,7 +503,7 @@ impl PluginManager { self.io .write_error(&format!("Unloading plugin {}", get_class_obj(plugin))); let mut removed = self.plugins.remove(index); - removed.deactivate(&self.composer_full(), &*self.io); + removed.deactivate(&self.composer_full(), &*self.io.borrow()); // TODO(plugin): remove_listener accepts any callable/object in PHP; here we have // a plugin instance and need to translate to a Callable, which is not portable @@ -515,7 +516,7 @@ impl PluginManager { // TODO(plugin): plugin uninstall hook self.io .write_error(&format!("Uninstalling plugin {}", get_class_obj(plugin))); - plugin.uninstall(&self.composer_full(), &*self.io); + plugin.uninstall(&self.composer_full(), &*self.io.borrow()); } fn load_repository( diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs index eff8fef..1509de3 100644 --- a/crates/shirabe/src/repository/artifact_repository.rs +++ b/crates/shirabe/src/repository/artifact_repository.rs @@ -9,6 +9,7 @@ use shirabe_php_shim::{ }; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::BasePackage; use crate::package::loader::ArrayLoader; @@ -24,7 +25,7 @@ pub struct ArtifactRepository { pub(crate) loader: Box<dyn LoaderInterface>, pub(crate) lookup: String, pub(crate) repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl std::fmt::Debug for ArtifactRepository { @@ -39,7 +40,7 @@ impl std::fmt::Debug for ArtifactRepository { impl ArtifactRepository { pub fn new( repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> anyhow::Result<Self> { if !extension_loaded("zip") { return Err(RuntimeException { diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index 5c16f8e..d055e59 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -21,6 +21,7 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::event_dispatcher::EventDispatcher; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::BasePackageHandle; use crate::package::PackageInterface; @@ -84,7 +85,7 @@ pub struct ComposerRepository { url: String, /// non-empty-string base_url: String, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, r#loop: std::rc::Rc<std::cell::RefCell<Loop>>, pub(crate) cache: Cache, @@ -141,7 +142,7 @@ impl ConfigurableRepositoryInterface for ComposerRepository { impl ComposerRepository { pub fn new( mut repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &Config, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -258,10 +259,11 @@ impl ComposerRepository { let base_url = base_url_trimmed.trim_end_matches('/').to_string(); assert!(!base_url.is_empty()); - // TODO(phase-b): Cache::new expects Box<dyn IOInterface> but io is also stored in self.io; + // TODO(phase-b): Cache::new expects std::rc::Rc<std::cell::RefCell<dyn IOInterface>> but io is also stored in self.io; // need shared ownership (Rc) for IOInterface. Using todo!() placeholder. - let cache: Cache = - todo!("Cache::new requires Box<dyn IOInterface> but io is also moved into self.io"); + let cache: Cache = todo!( + "Cache::new requires std::rc::Rc<std::cell::RefCell<dyn IOInterface>> but io is also moved into self.io" + ); let version_parser = VersionParser::new(); let loader = ArrayLoader::new(Some(version_parser.clone()), true); @@ -2930,7 +2932,7 @@ impl ComposerRepository { .as_array() .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); - HttpDownloader::output_warnings(&*self.io, &self.url, &data_local); + HttpDownloader::output_warnings(&*self.io.borrow(), &self.url, &data_local); if let Some(ck) = cache_key_owned.as_ref() { if !ck.is_empty() && !self.cache.is_read_only() { @@ -3127,7 +3129,7 @@ impl ComposerRepository { .as_array() .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); - HttpDownloader::output_warnings(&*self.io, &self.url, &data); + HttpDownloader::output_warnings(&*self.io.borrow(), &self.url, &data); let last_modified_date = response.get_header("last-modified"); response.collect(); @@ -3305,7 +3307,7 @@ impl ComposerRepository { .as_array() .map(|a| a.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect()) .unwrap_or_default(); - HttpDownloader::output_warnings(self.io.as_ref(), &self.url, &data); + HttpDownloader::output_warnings(&*self.io.borrow(), &self.url, &data); let last_modified_date = response.get_header("last-modified"); response.collect(); diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs index 8d9965e..51acff0 100644 --- a/crates/shirabe/src/repository/path_repository.rs +++ b/crates/shirabe/src/repository/path_repository.rs @@ -44,7 +44,7 @@ impl ConfigurableRepositoryInterface for PathRepository { impl PathRepository { pub fn new( repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -67,14 +67,14 @@ impl PathRepository { let url = Platform::expand_path(&url_str); let process = process.unwrap_or_else(|| { std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone_box(), + io.clone(), )))) }); let version_guesser = VersionGuesser::new( config, process.clone(), VersionParser::new(), - Some(io.clone_box()), + Some(io.clone()), ); let mut options = repo_config .get("options") diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs index 0f2e6b2..908704c 100644 --- a/crates/shirabe/src/repository/repository_factory.rs +++ b/crates/shirabe/src/repository/repository_factory.rs @@ -21,7 +21,7 @@ pub struct RepositoryFactory; impl RepositoryFactory { pub fn config_from_string( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, repository: &str, allow_filesystem: bool, @@ -42,9 +42,9 @@ impl RepositoryFactory { let mut json = JsonFile::new( repository.to_string(), Some(std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_http_downloader(io, config, IndexMap::new())?, + Factory::create_http_downloader(io.clone(), config, IndexMap::new())?, ))), - Some(io.clone_box()), + Some(io.clone()), )?; let data = json.read()?; let has_packages = data.get("packages").map_or(false, |v| !v.is_null()); @@ -97,18 +97,19 @@ impl RepositoryFactory { } pub fn from_string( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, repository: &str, allow_filesystem: bool, rm: Option<&mut RepositoryManager>, ) -> anyhow::Result<Box<dyn RepositoryInterface>> { - let repo_config = Self::config_from_string(io, config, repository, allow_filesystem)?; + let repo_config = + Self::config_from_string(io.clone(), config, repository, allow_filesystem)?; Self::create_repo(io, config, repo_config, rm) } pub fn create_repo( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, repo_config: IndexMap<String, PhpMixed>, rm: Option<&mut RepositoryManager>, @@ -141,7 +142,7 @@ impl RepositoryFactory { } pub fn default_repos( - io: Option<&dyn IOInterface>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, config: Option<std::rc::Rc<std::cell::RefCell<Config>>>, rm: Option<&mut RepositoryManager>, ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> { @@ -149,7 +150,7 @@ impl RepositoryFactory { Some(c) => c, None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)), }; - if let Some(_io) = io { + if let Some(_io) = &io { // TODO(phase-b): IOInterface::load_configuration requires &mut self, but this // function takes &dyn IOInterface. Wider refactor needed; skip for now. } @@ -164,7 +165,7 @@ impl RepositoryFactory { code: 0, })?; owned_rm = Self::manager( - io, + io.clone(), &config, Some(std::rc::Rc::new(std::cell::RefCell::new( Factory::create_http_downloader(io, &config, IndexMap::new())?, @@ -181,7 +182,7 @@ impl RepositoryFactory { } pub fn manager( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -190,7 +191,7 @@ impl RepositoryFactory { let http_downloader = match http_downloader { Some(h) => h, None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader( - io, + io.clone(), config, IndexMap::new(), )?)), @@ -198,7 +199,7 @@ impl RepositoryFactory { let process = match process { Some(p) => p, None => { - let mut p = ProcessExecutor::new(io); + let mut p = ProcessExecutor::new(Some(io.clone())); p.enable_async(); std::rc::Rc::new(std::cell::RefCell::new(p)) } @@ -231,14 +232,15 @@ impl RepositoryFactory { } pub fn default_repos_with_default_manager( - io: &mut dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> { let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config( - Some(io), + Some(io.clone()), None, )?)); - let mut manager = Self::manager(io, &config, None, None, None)?; - io.load_configuration(&mut *config.borrow_mut())?; + let mut manager = Self::manager(io.clone(), &config, None, None, None)?; + io.borrow_mut() + .load_configuration(&mut *config.borrow_mut())?; Self::default_repos(Some(io), Some(config), Some(&mut manager)) } diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs index f432d6e..72c3643 100644 --- a/crates/shirabe/src/repository/repository_manager.rs +++ b/crates/shirabe/src/repository/repository_manager.rs @@ -7,6 +7,7 @@ use shirabe_semver::constraint::AnyConstraint; use crate::config::Config; use crate::event_dispatcher::EventDispatcher; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::PackageInterfaceHandle; use crate::repository::FilterRepository; use crate::repository::InstalledRepositoryInterface; @@ -19,7 +20,7 @@ pub struct RepositoryManager { local_repository: Option<Box<dyn InstalledRepositoryInterface>>, repositories: Vec<Box<dyn RepositoryInterface>>, repository_classes: IndexMap<String, String>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -28,19 +29,22 @@ pub struct RepositoryManager { impl RepositoryManager { pub fn new( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, ) -> Self { - let process = process - .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)))); + let process = process.unwrap_or_else(|| { + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))) + }); Self { local_repository: None, repositories: vec![], repository_classes: IndexMap::new(), - io: io.clone_box(), + io, config, http_downloader, event_dispatcher, diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs index 1c9c4a3..3a81eda 100644 --- a/crates/shirabe/src/repository/repository_set.rs +++ b/crates/shirabe/src/repository/repository_set.rs @@ -456,7 +456,7 @@ impl RepositorySet { pub fn create_pool( &mut self, request: Request, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, pool_optimizer: Option<PoolOptimizer>, ignored_types: Vec<String>, @@ -609,7 +609,7 @@ impl RepositorySet { self.create_pool( request, - Box::new(NullIO::new()), + std::rc::Rc::new(std::cell::RefCell::new(NullIO::new())), None, None, vec![], diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs index 72fd6af..ccd0c16 100644 --- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs +++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs @@ -12,6 +12,7 @@ use crate::cache::Cache; use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::repository::vcs::GitDriver; use crate::repository::vcs::VcsDriverBase; @@ -51,7 +52,7 @@ impl ForgejoDriver { self.forgejo_url = Some(forgejo_url); self.inner.cache = Some(Cache::new( - self.inner.io.clone_box(), + self.inner.io.clone(), &cache_dir, None, None, diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs index 4982d17..207a138 100644 --- a/crates/shirabe/src/repository/vcs/fossil_driver.rs +++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs @@ -9,6 +9,7 @@ use shirabe_php_shim::{PhpMixed, RuntimeException, dirname, is_dir, is_file, is_ use crate::cache::Cache; use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::repository::vcs::VcsDriverBase; use crate::util::Filesystem; use crate::util::ProcessExecutor; @@ -31,7 +32,7 @@ impl FossilDriver { // Ensure we are allowed to use this URL by config. self.inner.config.borrow_mut().prohibit_url_by_config( &self.inner.url, - Some(&*self.inner.io), + Some(&*self.inner.io.borrow()), &indexmap::IndexMap::new(), )?; @@ -289,7 +290,12 @@ impl FossilDriver { Ok(self.branches.clone().unwrap_or_default()) } - pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, deep: bool) -> bool { + pub fn supports( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: &Config, + url: &str, + deep: bool, + ) -> bool { if Preg::is_match( r"#(^(?:https?|ssh)://(?:[^@]@)?(?:chiselapp\.com|fossil\.))#i", url, @@ -310,7 +316,7 @@ impl FossilDriver { return false; } - let mut process = ProcessExecutor::new(io); + let mut process = ProcessExecutor::new(Some(io)); let mut output = String::new(); if process.execute_args( &["fossil", "info"].map(|s| s.to_string()).to_vec(), diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs index 19f0246..53e49fc 100644 --- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs @@ -15,6 +15,7 @@ use crate::cache::Cache; use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::repository::vcs::GitDriver; use crate::repository::vcs::VcsDriverBase; @@ -80,7 +81,7 @@ impl GitBitbucketDriver { self.repository = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(); self.inner.origin_url = "bitbucket.org".to_string(); self.inner.cache = Some(Cache::new( - self.inner.io.clone_box(), + self.inner.io.clone(), &implode( "/", &[ @@ -693,7 +694,7 @@ impl GitBitbucketDriver { Err(e) => { // TODO(phase-b): only handle TransportException let mut bitbucket_util = Bitbucket::new( - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), Some(self.inner.http_downloader.clone()), diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs index 69b16b6..665e2fb 100644 --- a/crates/shirabe/src/repository/vcs/git_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_driver.rs @@ -13,6 +13,7 @@ use shirabe_php_shim::{ use crate::cache::Cache; use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::repository::vcs::VcsDriverBase; use crate::util::Filesystem; use crate::util::Git as GitUtil; @@ -31,7 +32,7 @@ pub struct GitDriver { impl GitDriver { pub fn new( repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, @@ -117,7 +118,7 @@ impl GitDriver { } let mut git_util = GitUtil::new( - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), self.inner.process.clone(), std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))), @@ -154,7 +155,7 @@ impl GitDriver { .unwrap_or("") .to_string(); self.inner.cache = Some(Cache::new( - self.inner.io.clone_box(), + self.inner.io.clone(), &format!( "{}/{}", cache_repo_dir, @@ -183,7 +184,7 @@ impl GitDriver { self.root_identifier = Some("master".to_string()); let mut git_util = GitUtil::new( - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), self.inner.process.clone(), std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))), @@ -399,7 +400,7 @@ impl GitDriver { } pub fn supports( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, _config: &Config, url: &str, deep: bool, @@ -419,7 +420,9 @@ impl GitDriver { return Ok(false); } - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io))); + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))); let mut output = String::new(); if process.borrow_mut().execute_args( &["git".to_string(), "tag".to_string()], @@ -432,7 +435,7 @@ impl GitDriver { GitUtil::check_for_repo_ownership_error( &process.borrow().get_error_output(), &url, - Some(io), + Some(io.clone()), )?; } @@ -440,7 +443,9 @@ impl GitDriver { return Ok(false); } - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io))); + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))); // TODO(phase-b): supports() takes &Config; GitUtil now needs Rc<RefCell<Config>>. // Skipping clean Rc construction since we cannot reconstruct one from a borrowed &Config. let _ = _config; @@ -449,7 +454,7 @@ impl GitDriver { )); #[allow(unreachable_code)] let mut git_util = GitUtil::new( - io.clone_box(), + io.clone(), todo!(), process.clone(), std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))), diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs index 6c8d495..5f8266c 100644 --- a/crates/shirabe/src/repository/vcs/github_driver.rs +++ b/crates/shirabe/src/repository/vcs/github_driver.rs @@ -15,6 +15,7 @@ use crate::cache::Cache; use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::repository::vcs::GitDriver; use crate::repository::vcs::VcsDriverBase; @@ -88,7 +89,7 @@ impl GitHubDriver { self.inner.origin_url = "github.com".to_string(); } self.inner.cache = Some(Cache::new( - self.inner.io.clone_box(), + self.inner.io.clone(), &format!( "{}/{}/{}/{}", self.inner @@ -1015,7 +1016,7 @@ impl GitHubDriver { Ok(r) => Ok(r), Err(e) => { let mut git_hub_util = GitHub::new( - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), Some(self.inner.http_downloader.clone()), @@ -1294,7 +1295,7 @@ impl GitHubDriver { repo_config.insert("url".to_string(), PhpMixed::String(url.to_string())); let mut git_driver = GitDriver::new( repo_config, - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), self.inner.http_downloader.clone(), self.inner.process.clone(), diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs index 8a24b7d..e31eb67 100644 --- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs +++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs @@ -15,6 +15,7 @@ use crate::cache::Cache; use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::repository::vcs::GitDriver; use crate::repository::vcs::VcsDriverBase; @@ -183,7 +184,7 @@ impl GitLabDriver { .unwrap_or_default(); self.inner.cache = Some(Cache::new( - self.inner.io.clone_box(), + self.inner.io.clone(), &format!( "{}/{}/{}/{}", self.inner @@ -768,7 +769,7 @@ impl GitLabDriver { repo_config.insert("url".to_string(), PhpMixed::String(url.to_string())); let mut git_driver = GitDriver::new( repo_config, - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), self.inner.http_downloader.clone(), self.inner.process.clone(), @@ -885,7 +886,7 @@ impl GitLabDriver { } Err(e) => { let mut git_lab_util = GitLab::new( - self.inner.io.clone_box(), + self.inner.io.clone(), self.inner.config.clone(), Some(self.inner.process.clone()), Some(self.inner.http_downloader.clone()), diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs index 8f40d2a..ab3bd0c 100644 --- a/crates/shirabe/src/repository/vcs/hg_driver.rs +++ b/crates/shirabe/src/repository/vcs/hg_driver.rs @@ -3,6 +3,7 @@ use crate::cache::Cache; use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::io_interface; use crate::repository::vcs::VcsDriverBase; use crate::util::Filesystem; @@ -61,12 +62,12 @@ impl HgDriver { self.inner.config.borrow_mut().prohibit_url_by_config( &self.inner.url, - Some(&*self.inner.io), + Some(&*self.inner.io.borrow()), &indexmap::IndexMap::new(), )?; let hg_utils = HgUtils::new( - &*self.inner.io, + self.inner.io.clone(), &*self.inner.config.borrow(), &self.inner.process, ); @@ -308,7 +309,12 @@ impl HgDriver { Ok(self.branches.clone().unwrap_or_default()) } - pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, deep: bool) -> bool { + pub fn supports( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: &Config, + url: &str, + deep: bool, + ) -> bool { if Preg::is_match( r"#(^(?:https?|ssh)://(?:[^@]+@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i", url, @@ -324,7 +330,7 @@ impl HgDriver { return false; } - let mut process = crate::util::ProcessExecutor::new(io); + let mut process = crate::util::ProcessExecutor::new(Some(io.clone())); let mut output = String::new(); if process.execute_args( &["hg", "summary"].map(|s| s.to_string()).to_vec(), @@ -340,7 +346,7 @@ impl HgDriver { return false; } - let mut process = crate::util::ProcessExecutor::new(io); + let mut process = crate::util::ProcessExecutor::new(Some(io)); let mut ignored = String::new(); let exit = process.execute_args( &["hg", "identify", "--", url] diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index 35da517..03948b3 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -77,7 +77,7 @@ impl PerforceDriver { self.inner.url.clone(), repo_dir, self.inner.process.clone(), - self.inner.io.clone_box(), + self.inner.io.clone(), )); Ok(()) @@ -167,9 +167,14 @@ impl PerforceDriver { .into()) } - pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, deep: bool) -> bool { + pub fn supports( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + _config: &Config, + url: &str, + deep: bool, + ) -> bool { if deep || Preg::is_match(r"#\b(perforce|p4)\b#i", url).unwrap_or(false) { - return Perforce::check_server_exists(url, &mut ProcessExecutor::new(io)); + return Perforce::check_server_exists(url, &mut ProcessExecutor::new(Some(io))); } false } diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs index a6ca9c1..a793340 100644 --- a/crates/shirabe/src/repository/vcs/svn_driver.rs +++ b/crates/shirabe/src/repository/vcs/svn_driver.rs @@ -468,7 +468,12 @@ impl SvnDriver { self.branches.as_ref().unwrap() } - pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, deep: bool) -> bool { + pub fn supports( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + _config: &Config, + url: &str, + deep: bool, + ) -> bool { let url = Self::normalize_url(url); if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url).unwrap_or(false) { return true; @@ -479,7 +484,7 @@ impl SvnDriver { return false; } - let mut process = ProcessExecutor::new(io); + let mut process = ProcessExecutor::new(Some(io)); let mut ignored_output = String::new(); let exit = process.execute_args( &[ diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs index 71476b9..15dd722 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs @@ -23,7 +23,7 @@ pub struct VcsDriverBase { pub url: String, pub origin_url: String, pub repo_config: IndexMap<String, PhpMixed>, - pub io: Box<dyn IOInterface>, + pub io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub config: std::rc::Rc<std::cell::RefCell<Config>>, pub process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, pub http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, @@ -34,7 +34,7 @@ pub struct VcsDriverBase { impl VcsDriverBase { pub fn new( repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs index a256fb8..804ba51 100644 --- a/crates/shirabe/src/repository/vcs_repository.rs +++ b/crates/shirabe/src/repository/vcs_repository.rs @@ -15,6 +15,7 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::event_dispatcher::EventDispatcher; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::package::loader::ArrayLoader; use crate::package::loader::InvalidPackageException; use crate::package::loader::LoaderInterface; @@ -43,7 +44,7 @@ pub struct VcsRepository { /// @var bool pub(crate) is_very_verbose: bool, /// @var IOInterface - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var Config pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, /// @var VersionParser @@ -87,7 +88,7 @@ impl VcsRepository { /// @param array<string, class-string<VcsDriverInterface>>|null $drivers pub fn new( mut repo_config: IndexMap<String, PhpMixed>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, @@ -158,7 +159,7 @@ impl VcsRepository { let is_very_verbose = io.is_very_verbose(); let process_executor = process.unwrap_or_else(|| { std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone_box(), + io.clone(), )))) }); diff --git a/crates/shirabe/src/script/event.rs b/crates/shirabe/src/script/event.rs index 1bd37dd..60557b0 100644 --- a/crates/shirabe/src/script/event.rs +++ b/crates/shirabe/src/script/event.rs @@ -10,7 +10,7 @@ use shirabe_php_shim::PhpMixed; pub struct Event { inner: BaseEvent, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, originating_event: Option<Box<BaseEvent>>, } @@ -19,7 +19,7 @@ impl Event { pub fn new( name: String, composer: ComposerWeakHandle, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, dev_mode: bool, args: Vec<String>, flags: IndexMap<String, PhpMixed>, @@ -37,8 +37,8 @@ impl Event { &self.composer } - pub fn get_io(&self) -> &dyn IOInterface { - self.io.as_ref() + pub fn get_io(&self) -> std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { + self.io.clone() } pub fn is_dev_mode(&self) -> bool { diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs index 908839e..3471a54 100644 --- a/crates/shirabe/src/util/auth_helper.rs +++ b/crates/shirabe/src/util/auth_helper.rs @@ -13,13 +13,14 @@ use shirabe_php_shim::{ use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Bitbucket; use crate::util::GitHub; use crate::util::GitLab; #[derive(Debug)] pub struct AuthHelper { - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, /// @var array<string, string> Map of origins to message displayed displayed_origin_authentications: IndexMap<String, String>, @@ -41,7 +42,10 @@ pub enum StoreAuth { } impl AuthHelper { - pub fn new(io: Box<dyn IOInterface>, config: std::rc::Rc<std::cell::RefCell<Config>>) -> Self { + pub fn new( + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + ) -> Self { Self { io, config, @@ -339,7 +343,7 @@ impl AuthHelper { let access_token = bitbucket_util.request_token(&origin, &username, &password)?; if !access_token.is_empty() { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin.clone(), "x-token-auth".to_string(), Some(access_token), @@ -452,7 +456,7 @@ impl AuthHelper { ); let username = self.io.ask(" Username: ".to_string(), PhpMixed::Null); let password = self.io.ask_and_hide_answer(" Password: ".to_string()); - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin.to_string(), username.as_string().unwrap_or("").to_string(), password, diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs index 1708447..67ae575 100644 --- a/crates/shirabe/src/util/bitbucket.rs +++ b/crates/shirabe/src/util/bitbucket.rs @@ -8,6 +8,7 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::HttpDownloader; use crate::util::ProcessExecutor; @@ -17,7 +18,7 @@ fn transport_error_code(err: &anyhow::Error) -> Option<i64> { #[derive(Debug)] pub struct Bitbucket { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, @@ -30,7 +31,7 @@ impl Bitbucket { "https://bitbucket.org/site/oauth2/access_token"; pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, @@ -38,13 +39,13 @@ impl Bitbucket { ) -> anyhow::Result<Self> { let process = process.unwrap_or_else(|| { std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone_box(), + io.clone(), )))) }); let http_downloader = match http_downloader { Some(h) => h, None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader( - &*io, + io.clone(), &config, IndexMap::new(), )?)), @@ -88,7 +89,7 @@ impl Bitbucket { == 0 { let output_str = output.as_string().unwrap_or("").trim().to_string(); - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), "x-token-auth".to_string(), Some(output_str), @@ -292,7 +293,7 @@ impl Bitbucket { return Ok(false); } - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), consumer_key.clone(), Some(consumer_secret.clone()), @@ -338,7 +339,7 @@ impl Bitbucket { .unwrap_or_default()); } - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), consumer_key.to_string(), Some(consumer_secret.to_string()), diff --git a/crates/shirabe/src/util/config_validator.rs b/crates/shirabe/src/util/config_validator.rs index ee78c26..47b4968 100644 --- a/crates/shirabe/src/util/config_validator.rs +++ b/crates/shirabe/src/util/config_validator.rs @@ -15,13 +15,13 @@ use shirabe_php_shim::PhpMixed; #[derive(Debug)] pub struct ConfigValidator { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, } impl ConfigValidator { pub const CHECK_VERSION: i64 = 1; - pub fn new(io: Box<dyn IOInterface>) -> Self { + pub fn new(io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) -> Self { Self { io } } @@ -39,7 +39,7 @@ impl ConfigValidator { let mut lax_valid = false; let mut manifest: Option<IndexMap<String, PhpMixed>> = None; - // TODO(phase-b): io type mismatch (&dyn IOInterface vs Box<dyn IOInterface>) + // TODO(phase-b): io type mismatch (&dyn IOInterface vs std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) let mut json = JsonFile::new(file.to_string(), None, None).expect("config file path is always local"); let schema_result: anyhow::Result<()> = (|| -> anyhow::Result<()> { diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs index 73f21d6..ef3d307 100644 --- a/crates/shirabe/src/util/filesystem.rs +++ b/crates/shirabe/src/util/filesystem.rs @@ -819,7 +819,7 @@ impl Filesystem { pub(crate) fn get_process(&mut self) -> std::cell::RefMut<'_, ProcessExecutor> { if self.process_executor.is_none() { self.process_executor = Some(std::rc::Rc::new(std::cell::RefCell::new( - ProcessExecutor::new(()), + ProcessExecutor::new(None), ))); } diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs index 0e1d52d..4db5815 100644 --- a/crates/shirabe/src/util/forgejo.rs +++ b/crates/shirabe/src/util/forgejo.rs @@ -3,19 +3,20 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::io::io_interface; use crate::util::HttpDownloader; #[derive(Debug)] pub struct Forgejo { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, } impl Forgejo { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, ) -> Self { @@ -107,7 +108,7 @@ impl Forgejo { return Ok(Ok(false)); } - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), username.clone(), Some(token.clone()), diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs index f1b71f9..3be37f6 100644 --- a/crates/shirabe/src/util/git.rs +++ b/crates/shirabe/src/util/git.rs @@ -15,6 +15,7 @@ use shirabe_php_shim::{ use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Bitbucket; use crate::util::Filesystem; use crate::util::GitHub; @@ -27,7 +28,7 @@ use crate::util::{AuthHelper, StoreAuth}; #[derive(Debug)] pub struct Git { - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, pub(crate) filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -39,7 +40,7 @@ static VERSION: Mutex<Option<Option<String>>> = Mutex::new(None); impl Git { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, fs: std::rc::Rc<std::cell::RefCell<Filesystem>>, @@ -57,7 +58,7 @@ impl Git { pub fn check_for_repo_ownership_error( output: &str, path: &str, - io: Option<&dyn IOInterface>, + io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> Result<()> { if str_contains(output, "fatal: detected dubious ownership") { let msg = format!( @@ -147,11 +148,9 @@ impl Git { let mut last_command: PhpMixed = PhpMixed::String(String::new()); // Ensure we are allowed to use this URL by config - self.config.borrow_mut().prohibit_url_by_config( - url, - Some(self.io.as_ref()), - &IndexMap::new(), - )?; + self.config + .borrow_mut() + .prohibit_url_by_config(url, Some(&*self.io.borrow()), &IndexMap::new())?; let orig_cwd: Option<String> = if initial_clone { cwd.map(|s| s.to_string()) @@ -238,7 +237,7 @@ impl Git { { let m3 = m.get(&CaptureKey::ByIndex(3)).cloned().unwrap_or_default(); if !self.io.has_authentication(&m3) { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( m3.clone(), rawurldecode(&m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default()), Some(rawurldecode( @@ -386,7 +385,7 @@ impl Git { let m2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(); if !self.io.has_authentication(&m1) { let mut git_hub_util = GitHub::new( - self.io.clone_box(), + self.io.clone(), self.config.clone(), Some(self.process.clone()), self.http_downloader.clone(), @@ -448,7 +447,7 @@ impl Git { } { // bitbucket either through oauth or app password, with fallback to ssh. let mut bitbucket_util = Bitbucket::new( - self.io.clone_box(), + self.io.clone(), self.config.clone(), Some(self.process.clone()), self.http_downloader.clone(), @@ -467,7 +466,7 @@ impl Git { if !bitbucket_util.authorize_oauth(&domain) && self.io.is_interactive() { bitbucket_util.authorize_oauth_interactively(&domain, Some(message)); let access_token = bitbucket_util.get_token(); - self.io.set_authentication( + self.io.borrow_mut().set_authentication( domain.clone(), "x-token-auth".to_string(), Some(access_token), @@ -521,7 +520,7 @@ impl Git { let access_token = bitbucket_util.request_token(&domain, &username, &password)?; if !access_token.is_empty() { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( domain.clone(), "x-token-auth".to_string(), Some(access_token), @@ -613,7 +612,7 @@ impl Git { if !self.io.has_authentication(&m2) { let mut git_lab_util = GitLab::new( - self.io.clone_box(), + self.io.clone(), self.config.clone(), Some(self.process.clone()), self.http_downloader.clone(), @@ -765,10 +764,12 @@ impl Git { command_output.as_deref_mut(), ) == 0 { - self.io - .set_authentication(m2.clone(), username, Some(password)); - let mut auth_helper = - AuthHelper::new(self.io.clone_box(), self.config.clone()); + self.io.borrow_mut().set_authentication( + m2.clone(), + username, + Some(password), + ); + let mut auth_helper = AuthHelper::new(self.io.clone(), self.config.clone()); let store_auth_enum = match &store_auth { PhpMixed::String(s) if s == "prompt" => StoreAuth::Prompt, PhpMixed::Bool(b) => StoreAuth::Bool(*b), diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs index f8a592e..c87a83f 100644 --- a/crates/shirabe/src/util/github.rs +++ b/crates/shirabe/src/util/github.rs @@ -9,12 +9,13 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::HttpDownloader; use crate::util::ProcessExecutor; #[derive(Debug)] pub struct GitHub { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, @@ -25,18 +26,20 @@ impl GitHub { r"{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+|github_pat_[a-zA-Z0-9_]+)$}"; pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, ) -> anyhow::Result<Self> { let process = process.unwrap_or_else(|| { - std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io))) + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))) }); let http_downloader = match http_downloader { Some(h) => h, None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader( - &*io, + io.clone(), &config, IndexMap::new(), )?)), @@ -71,7 +74,7 @@ impl GitHub { (), ) == 0 { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), output.trim().to_string(), Some("x-oauth-basic".to_string()), @@ -216,7 +219,7 @@ impl GitHub { return Ok(false); } - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), token.clone(), Some("x-oauth-basic".to_string()), diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs index 88563db..d42f1cf 100644 --- a/crates/shirabe/src/util/gitlab.rs +++ b/crates/shirabe/src/util/gitlab.rs @@ -9,12 +9,13 @@ use crate::config::Config; use crate::downloader::TransportException; use crate::factory::Factory; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::HttpDownloader; use crate::util::ProcessExecutor; #[derive(Debug)] pub struct GitLab { - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>, pub(crate) process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, pub(crate) http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, @@ -22,18 +23,20 @@ pub struct GitLab { impl GitLab { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>, ) -> anyhow::Result<Self> { let process = process.unwrap_or_else(|| { - std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io))) + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))) }); let http_downloader = match http_downloader { Some(h) => h, None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader( - &*io, + io.clone(), &config, IndexMap::new(), )?)), @@ -76,7 +79,7 @@ impl GitLab { (), ) == 0 { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), output.trim().to_string(), Some("oauth2".to_string()), @@ -106,7 +109,7 @@ impl GitLab { (), ) == 0 { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), token_user.trim().to_string(), Some(token_password.trim().to_string()), @@ -154,11 +157,17 @@ impl GitLab { // 'gitlab-ci-token' to be stored as password. Detect cases where this is reversed // and automatically resolve it. if ["private-token", "gitlab-ci-token", "oauth2"].contains(&username.as_str()) { - self.io - .set_authentication(origin_url.to_string(), password, Some(username)); + self.io.borrow_mut().set_authentication( + origin_url.to_string(), + password, + Some(username), + ); } else { - self.io - .set_authentication(origin_url.to_string(), username, Some(password)); + self.io.borrow_mut().set_authentication( + origin_url.to_string(), + username, + Some(password), + ); } return true; @@ -311,7 +320,7 @@ impl GitLab { .unwrap_or("") .to_string(); - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), access_token.clone(), Some("oauth2".to_string()), @@ -391,7 +400,7 @@ impl GitLab { .unwrap_or("") .to_string(); - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin_url.to_string(), access_token.clone(), Some("oauth2".to_string()), diff --git a/crates/shirabe/src/util/hg.rs b/crates/shirabe/src/util/hg.rs index b6bdd04..47e80e2 100644 --- a/crates/shirabe/src/util/hg.rs +++ b/crates/shirabe/src/util/hg.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::ProcessExecutor; use crate::util::Url; use anyhow::Result; @@ -13,14 +14,14 @@ static VERSION: OnceLock<Option<String>> = OnceLock::new(); #[derive(Debug)] pub struct Hg { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, } impl Hg { pub fn new( - io: &dyn IOInterface, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: &Config, process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, ) -> Self { @@ -35,7 +36,7 @@ impl Hg { ) -> Result<()> { self.config.borrow_mut().prohibit_url_by_config( &url, - Some(&*self.io), + Some(&*self.io.borrow()), &indexmap::IndexMap::new(), )?; diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs index c3a1227..87ea8ef 100644 --- a/crates/shirabe/src/util/http/curl_downloader.rs +++ b/crates/shirabe/src/util/http/curl_downloader.rs @@ -29,6 +29,7 @@ use crate::config::Config; use crate::downloader::MaxFileSizeExceededException; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::HttpDownloader; use crate::util::Platform; use crate::util::StreamContextFactory; @@ -48,7 +49,7 @@ pub struct CurlDownloader { /// @var Job[] jobs: IndexMap<i64, IndexMap<String, PhpMixed>>, /// @var IOInterface - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var Config config: std::rc::Rc<std::cell::RefCell<Config>>, /// @var AuthHelper @@ -122,7 +123,7 @@ static TIMEOUT_WARNING: AtomicBool = AtomicBool::new(false); impl CurlDownloader { /// @param mixed[] $options pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, _options: IndexMap<String, PhpMixed>, _disable_tls: bool, @@ -340,7 +341,7 @@ impl CurlDownloader { { self.config .borrow_mut() - .prohibit_url_by_config(url, Some(&*self.io), &options)?; + .prohibit_url_by_config(url, Some(&*self.io.borrow()), &options)?; } let curl_handle = curl_init(); @@ -1184,7 +1185,7 @@ impl CurlDownloader { == Some("application/json") { HttpDownloader::output_warnings( - &*self.io, + &*self.io.borrow(), job.get("origin").and_then(|v| v.as_string()).unwrap_or(""), &match json_decode(response_ref.inner.get_body().unwrap_or(""), true)? { PhpMixed::Array(a) => a.into_iter().map(|(k, v)| (k, *v)).collect(), diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs index 77d35b1..610bd3a 100644 --- a/crates/shirabe/src/util/http_downloader.rs +++ b/crates/shirabe/src/util/http_downloader.rs @@ -32,7 +32,7 @@ use crate::util::http::Response; #[derive(Debug)] pub struct HttpDownloader { /// @var IOInterface - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var Config config: std::rc::Rc<std::cell::RefCell<Config>>, /// @var array<Job> @@ -103,7 +103,7 @@ impl HttpDownloader { /// @param Config $config The config /// @param mixed[] $options The options pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, options: IndexMap<String, PhpMixed>, disable_tls: bool, @@ -115,8 +115,7 @@ impl HttpDownloader { // The cafile option can be set via config.json let mut self_options: IndexMap<String, PhpMixed> = IndexMap::new(); if disable_tls == false { - self_options = - StreamContextFactory::get_tls_defaults(&options, Some(&*io)).unwrap_or_default(); + self_options = StreamContextFactory::get_tls_defaults(&options, ()).unwrap_or_default(); } // handle the other externally set options normally. @@ -124,7 +123,7 @@ impl HttpDownloader { let curl = if Self::is_curl_enabled() { Some(CurlDownloader::new( - io.clone_box(), + io.clone(), config.clone(), options.clone(), disable_tls, @@ -134,7 +133,7 @@ impl HttpDownloader { }; let rfs = Some(RemoteFilesystem::new( - io.clone_box(), + io.clone(), config.clone(), options.clone(), disable_tls, @@ -317,7 +316,7 @@ impl HttpDownloader { ) .unwrap_or(false) { - self.io.set_authentication( + self.io.borrow_mut().set_authentication( origin.clone(), rawurldecode( m.get(&CaptureKey::ByIndex(1)) diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs index 10c426f..cd384fc 100644 --- a/crates/shirabe/src/util/perforce.rs +++ b/crates/shirabe/src/util/perforce.rs @@ -12,6 +12,7 @@ use shirabe_php_shim::{ }; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Filesystem; use crate::util::Platform; use crate::util::ProcessExecutor; @@ -33,7 +34,7 @@ pub struct Perforce { pub(crate) unique_perforce_client_name: String, pub(crate) windows_flag: bool, pub(crate) command_result: String, - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pub(crate) filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>, } @@ -45,7 +46,7 @@ impl Perforce { path: String, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, is_windows: bool, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> Self { let mut this = Self { path: String::new(), @@ -76,7 +77,7 @@ impl Perforce { port: String, path: String, process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> Self { Self::new(repo_config, port, path, process, Platform::is_windows(), io) } diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs index 74fc30c..36025ea 100644 --- a/crates/shirabe/src/util/process_executor.rs +++ b/crates/shirabe/src/util/process_executor.rs @@ -19,6 +19,7 @@ use shirabe_php_shim::{ }; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::GitHub; use crate::util::Platform; @@ -34,7 +35,7 @@ pub struct ProcessExecutor { /// @var string pub(crate) error_output: String, /// @var ?IOInterface - pub(crate) io: Option<Box<dyn IOInterface>>, + pub(crate) io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, /// @phpstan-var array<int, array<string, mixed>> jobs: IndexMap<i64, Job>, /// @var int @@ -87,11 +88,11 @@ impl ProcessExecutor { const GIT_CMDS_NEED_GIT_DIR: &'static [&'static [&'static str]] = &[&["show"], &["log"], &["branch"], &["remote", "set-url"]]; - pub fn new<I: IntoProcessExecutorIo>(io: I) -> Self { + pub fn new(io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>) -> Self { let mut this = Self { capture_output: false, error_output: String::new(), - io: io.into_process_executor_io(), + io, jobs: IndexMap::new(), running_jobs: 0, max_jobs: 10, @@ -270,7 +271,7 @@ impl ProcessExecutor { }) }; - let io_for_signal = self.io.as_ref().map(|b| &**b as *const dyn IOInterface); + let io_for_signal = self.io.clone(); let signal_handler = SignalHandler::create( vec![ SignalHandler::SIGINT.to_string(), @@ -278,8 +279,7 @@ impl ProcessExecutor { SignalHandler::SIGHUP.to_string(), ], Box::new(move |signal: String, _h: &SignalHandler| { - if let Some(io_ptr) = io_for_signal { - let io = unsafe { &*io_ptr }; + if let Some(io) = &io_for_signal { io.write_error(&format!( "Received {}, aborting when child process is done", signal @@ -1085,45 +1085,6 @@ impl ToTimeoutSeconds for PhpMixed { } } -/// Phase B helper: accept various IO forms for `ProcessExecutor::new`. -/// Note: clones the IO via `clone_box` for borrow forms; this is incidental -/// to Phase B — PHP class semantics should use Rc, but that requires broader -/// refactor. TODO(phase-b): switch to shared ownership when call sites are -/// stabilized. -pub trait IntoProcessExecutorIo { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>>; -} - -impl IntoProcessExecutorIo for Option<Box<dyn IOInterface>> { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>> { - self - } -} - -impl IntoProcessExecutorIo for Box<dyn IOInterface> { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>> { - Some(self) - } -} - -impl IntoProcessExecutorIo for () { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>> { - None - } -} - -impl IntoProcessExecutorIo for &dyn IOInterface { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>> { - Some(self.clone_box()) - } -} - -impl IntoProcessExecutorIo for &mut dyn IOInterface { - fn into_process_executor_io(self) -> Option<Box<dyn IOInterface>> { - Some(self.clone_box()) - } -} - // Suppress unused-import warnings. #[allow(dead_code)] const _USE_PARITY: () = { diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs index 2e77fe8..8d50fa4 100644 --- a/crates/shirabe/src/util/remote_filesystem.rs +++ b/crates/shirabe/src/util/remote_filesystem.rs @@ -16,6 +16,7 @@ use crate::config::Config; use crate::downloader::MaxFileSizeExceededException; use crate::downloader::TransportException; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::AuthHelper; use crate::util::HttpDownloader; use crate::util::Platform; @@ -34,7 +35,7 @@ pub enum GetResult { #[derive(Debug)] pub struct RemoteFilesystem { - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, scheme: String, bytes_max: i64, @@ -56,7 +57,7 @@ pub struct RemoteFilesystem { impl RemoteFilesystem { pub fn new( - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, options: IndexMap<String, PhpMixed>, disable_tls: bool, @@ -64,8 +65,7 @@ impl RemoteFilesystem { ) -> Self { let (computed_options, disable_tls_set) = if !disable_tls { ( - // TODO(phase-b): logger is None placeholder; should pass `&*io` if a Logger view is available. - StreamContextFactory::get_tls_defaults(&options, None) + StreamContextFactory::get_tls_defaults(&options, ()) .unwrap_or_else(|_| IndexMap::new()), false, ) @@ -75,7 +75,7 @@ impl RemoteFilesystem { let merged = array_replace_recursive(computed_options, options); let auth_helper = - auth_helper.unwrap_or_else(|| AuthHelper::new(io.clone_box(), config.clone())); + auth_helper.unwrap_or_else(|| AuthHelper::new(io.clone(), config.clone())); Self { io, config, @@ -292,7 +292,7 @@ impl RemoteFilesystem { { let _ = self.config.borrow_mut().prohibit_url_by_config( &file_url, - Some(&*self.io), + Some(&*self.io.borrow()), &indexmap::IndexMap::new(), ); } @@ -337,7 +337,7 @@ impl RemoteFilesystem { PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(), _ => IndexMap::new(), }; - let _ = HttpDownloader::output_warnings(&*self.io, origin_url, &parsed_map); + let _ = HttpDownloader::output_warnings(&*self.io.borrow(), origin_url, &parsed_map); } if [401_i64, 403].contains(&code) && retry_auth_failure { diff --git a/crates/shirabe/src/util/stream_context_factory.rs b/crates/shirabe/src/util/stream_context_factory.rs index 48949c8..488d807 100644 --- a/crates/shirabe/src/util/stream_context_factory.rs +++ b/crates/shirabe/src/util/stream_context_factory.rs @@ -2,7 +2,6 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::ca_bundle::CaBundle; -use shirabe_external_packages::psr::log::LoggerInterface; use shirabe_php_shim::{ HHVM_VERSION, PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, PhpMixed, RuntimeException, array_replace_recursive, curl_version, extension_loaded, function_exists, @@ -239,7 +238,9 @@ impl StreamContextFactory { pub fn get_tls_defaults( options: &IndexMap<String, PhpMixed>, - logger: Option<&dyn LoggerInterface>, + // `logger` was a PSR LoggerInterface; CaBundle is slated for removal so + // it is now an unused `()` placeholder. + logger: (), ) -> anyhow::Result<IndexMap<String, PhpMixed>, TransportException> { let ciphers = [ "ECDHE-RSA-AES128-GCM-SHA256", diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs index aa81f0f..737e75d 100644 --- a/crates/shirabe/src/util/svn.rs +++ b/crates/shirabe/src/util/svn.rs @@ -13,6 +13,7 @@ use shirabe_php_shim::{ use crate::config::Config; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::util::Platform; use crate::util::ProcessExecutor; @@ -29,7 +30,7 @@ pub struct Svn { /// @var bool pub(crate) has_auth: Option<bool>, /// @var IOInterface - pub(crate) io: Box<dyn IOInterface>, + pub(crate) io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, /// @var string pub(crate) url: String, /// @var bool @@ -50,12 +51,14 @@ impl Svn { pub fn new( url: String, - io: Box<dyn IOInterface>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, config: std::rc::Rc<std::cell::RefCell<Config>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, ) -> Self { let process = process.unwrap_or_else(|| { - std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(&*io))) + std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io.clone(), + )))) }); Self { url, @@ -95,7 +98,7 @@ impl Svn { // Ensure we are allowed to use this URL by config self.config.borrow_mut().prohibit_url_by_config( url, - Some(&*self.io), + Some(&*self.io.borrow()), &indexmap::IndexMap::new(), )?; |
