From 901878ee3f2bee6605b02d321cc4c92bc32fd5b0 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 27 Jun 2026 08:44:57 +0900 Subject: refactor(composer): hold managers behind *Interface traits Composer/PartialComposer exposed its RepositoryManager, InstallationManager, EventDispatcher, Locker, DownloadManager, AutoloadGenerator and ArchiveManager as concrete types, but Composer's public setters (setDownloadManager() etc.) let plugins swap in subclasses. Introduce a *Interface trait per manager and store each as Rc> so a replacement is honored. Only Composer's slots and the sinks fed from its accessors become trait objects; managers injected concretely at construction keep their concrete references, matching PHP semantics. Fluent setters on the affected classes now return () and Locker::update_hash is de-generified to a boxed FnOnce so the traits stay object-safe. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/autoload/autoload_generator.rs | 136 ++++++++++++++++- crates/shirabe/src/command/archive_command.rs | 53 +++---- .../shirabe/src/command/base_dependency_command.rs | 2 +- crates/shirabe/src/command/bump_command.rs | 2 +- .../shirabe/src/command/create_project_command.rs | 7 +- crates/shirabe/src/command/diagnose_command.rs | 6 +- .../shirabe/src/command/dump_autoload_command.rs | 2 +- crates/shirabe/src/composer.rs | 102 ++++++++----- crates/shirabe/src/console/application.rs | 2 +- .../src/dependency_resolver/pool_builder.rs | 6 +- crates/shirabe/src/downloader/download_manager.rs | 139 +++++++++++++++-- .../src/event_dispatcher/event_dispatcher.rs | 71 ++++++++- crates/shirabe/src/installer.rs | 44 +++--- .../shirabe/src/installer/installation_manager.rs | 75 ++++++++++ crates/shirabe/src/installer/library_installer.rs | 9 +- crates/shirabe/src/installer/project_installer.rs | 6 +- .../src/package/archiver/archive_manager.rs | 29 +++- crates/shirabe/src/package/locker.rs | 164 ++++++++++++++++++++- crates/shirabe/src/plugin/plugin_manager.rs | 4 +- .../shirabe/src/repository/repository_factory.rs | 9 +- .../shirabe/src/repository/repository_manager.rs | 43 ++++++ crates/shirabe/src/repository/repository_set.rs | 4 +- crates/shirabe/src/util/sync_helper.rs | 4 +- crates/shirabe/tests/composer_test.rs | 22 +-- 24 files changed, 785 insertions(+), 156 deletions(-) diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index 1bdea07..1d0960d 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -21,12 +21,12 @@ use crate::event_dispatcher::EventDispatcher; use crate::filter::platform_requirement_filter::IgnoreAllPlatformRequirementFilter; use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; -use crate::installer::InstallationManager; +use crate::installer::InstallationManagerInterface; use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::json::JsonFile; -use crate::package::Locker; +use crate::package::LockerInterface; use crate::package::PackageInterfaceHandle; use crate::package::RootPackageInterfaceHandle; use crate::repository::InstalledRepositoryInterface; @@ -107,11 +107,11 @@ impl AutoloadGenerator { config: &Config, local_repo: &mut dyn InstalledRepositoryInterface, root_package: RootPackageInterfaceHandle, - installation_manager: &mut InstallationManager, + installation_manager: &mut dyn InstallationManagerInterface, target_dir: &str, scan_psr_packages: bool, suffix: Option, - locker: Option<&mut Locker>, + locker: Option<&mut dyn LockerInterface>, strict_ambiguous: bool, ) -> anyhow::Result { let mut scan_psr_packages = scan_psr_packages; @@ -732,7 +732,7 @@ impl AutoloadGenerator { pub fn build_package_map( &self, - installation_manager: &mut InstallationManager, + installation_manager: &mut dyn InstallationManagerInterface, root_package: RootPackageInterfaceHandle, packages: Vec, ) -> anyhow::Result)>> { @@ -1930,6 +1930,132 @@ impl AutoloadGenerator { } } +// Composer's Composer::setAutoloadGenerator() accepts any AutoloadGenerator subclass, so plugins +// may swap in a replacement. The interface captures the methods reached through Composer's accessor +// and through the `Rc>` references fed from it. +pub trait AutoloadGeneratorInterface: std::fmt::Debug { + fn set_dev_mode(&mut self, dev_mode: bool); + fn set_class_map_authoritative(&mut self, class_map_authoritative: bool); + fn set_apcu(&mut self, apcu: bool, apcu_prefix: Option); + fn set_run_scripts(&mut self, run_scripts: bool); + fn set_dry_run(&mut self, dry_run: bool); + fn set_platform_requirement_filter( + &mut self, + platform_requirement_filter: std::rc::Rc, + ); + #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] + fn dump( + &mut self, + config: &Config, + local_repo: &mut dyn InstalledRepositoryInterface, + root_package: RootPackageInterfaceHandle, + installation_manager: &mut dyn InstallationManagerInterface, + target_dir: &str, + scan_psr_packages: bool, + suffix: Option, + locker: Option<&mut dyn LockerInterface>, + strict_ambiguous: bool, + ) -> anyhow::Result; + fn build_package_map( + &self, + installation_manager: &mut dyn InstallationManagerInterface, + root_package: RootPackageInterfaceHandle, + packages: Vec, + ) -> anyhow::Result)>>; + fn parse_autoloads( + &self, + package_map: Vec<(PackageInterfaceHandle, Option)>, + root_package: RootPackageInterfaceHandle, + filtered_dev_packages: PhpMixed, + ) -> IndexMap; + fn create_loader( + &self, + autoloads: &IndexMap, + vendor_dir: Option, + ) -> ClassLoader; +} + +impl AutoloadGeneratorInterface for AutoloadGenerator { + fn set_dev_mode(&mut self, dev_mode: bool) { + self.set_dev_mode(dev_mode); + } + + fn set_class_map_authoritative(&mut self, class_map_authoritative: bool) { + self.set_class_map_authoritative(class_map_authoritative); + } + + fn set_apcu(&mut self, apcu: bool, apcu_prefix: Option) { + self.set_apcu(apcu, apcu_prefix); + } + + fn set_run_scripts(&mut self, run_scripts: bool) { + self.set_run_scripts(run_scripts); + } + + fn set_dry_run(&mut self, dry_run: bool) { + self.set_dry_run(dry_run); + } + + fn set_platform_requirement_filter( + &mut self, + platform_requirement_filter: std::rc::Rc, + ) { + self.set_platform_requirement_filter(platform_requirement_filter); + } + + #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] + fn dump( + &mut self, + config: &Config, + local_repo: &mut dyn InstalledRepositoryInterface, + root_package: RootPackageInterfaceHandle, + installation_manager: &mut dyn InstallationManagerInterface, + target_dir: &str, + scan_psr_packages: bool, + suffix: Option, + locker: Option<&mut dyn LockerInterface>, + strict_ambiguous: bool, + ) -> anyhow::Result { + self.dump( + config, + local_repo, + root_package, + installation_manager, + target_dir, + scan_psr_packages, + suffix, + locker, + strict_ambiguous, + ) + } + + fn build_package_map( + &self, + installation_manager: &mut dyn InstallationManagerInterface, + root_package: RootPackageInterfaceHandle, + packages: Vec, + ) -> anyhow::Result)>> { + self.build_package_map(installation_manager, root_package, packages) + } + + fn parse_autoloads( + &self, + package_map: Vec<(PackageInterfaceHandle, Option)>, + root_package: RootPackageInterfaceHandle, + filtered_dev_packages: PhpMixed, + ) -> IndexMap { + self.parse_autoloads(package_map, root_package, filtered_dev_packages) + } + + fn create_loader( + &self, + autoloads: &IndexMap, + vendor_dir: Option, + ) -> ClassLoader { + self.create_loader(autoloads, vendor_dir) + } +} + /// Evaluates one of the `autoload_namespaces.php` / `autoload_psr4.php` / `autoload_classmap.php` /// files that `dump` just generated, returning the `return array(...)` payload with every path /// expression resolved to an absolute string. This stands in for PHP's `require $file` (there is no diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index c2e9070..9aae885 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -19,7 +19,7 @@ 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::archiver::ArchiveManagerInterface; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; use crate::plugin::CommandEvent; @@ -222,31 +222,32 @@ impl ArchiveCommand { let mut owned_archive_manager; let composer_archive_manager; let mut composer_archive_manager_ref; - let archive_manager: &mut ArchiveManager = if let Some(composer) = &composer_guard { - composer_archive_manager = composer.get_archive_manager().clone(); - composer_archive_manager_ref = composer_archive_manager.borrow_mut(); - &mut composer_archive_manager_ref - } else { - let factory = Factory; - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))); - let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( - Factory::create_http_downloader(io.clone(), config, indexmap::IndexMap::new())?, - )); - 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), - ))); - owned_archive_manager = - factory.create_archive_manager(&config.borrow(), &download_manager, &loop_)?; - &mut owned_archive_manager - }; + let archive_manager: &mut dyn ArchiveManagerInterface = + if let Some(composer) = &composer_guard { + composer_archive_manager = composer.get_archive_manager().clone(); + composer_archive_manager_ref = composer_archive_manager.borrow_mut(); + &mut *composer_archive_manager_ref + } else { + let factory = Factory; + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None))); + let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( + Factory::create_http_downloader(io.clone(), config, indexmap::IndexMap::new())?, + )); + 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), + ))); + owned_archive_manager = + factory.create_archive_manager(&config.borrow(), &download_manager, &loop_)?; + &mut owned_archive_manager + }; let package: crate::package::CompletePackageInterfaceHandle = if let Some(name) = package_name { diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 8404dec..1c48a06 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -152,7 +152,7 @@ pub trait BaseDependencyCommand: BaseCommand { RepositoryFactory::default_repos( Some(self.get_io()), Some(composer.get_config()), - Some(&mut rm.borrow_mut()), + Some(&mut *rm.borrow_mut()), )? .into_values() .collect(), diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs index f86b29a..3964300 100644 --- a/crates/shirabe/src/command/bump_command.rs +++ b/crates/shirabe/src/command/bump_command.rs @@ -285,7 +285,7 @@ impl BumpCommand { composer .get_locker() .borrow_mut() - .update_hash(&composer_json, None:: _>)?; + .update_hash(&composer_json, None)?; } if dry_run && change_count > 0 { diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index ab45400..abbf37a 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -865,7 +865,7 @@ impl CreateProjectCommand { RepositoryFactory::default_repos( Some(io.clone()), Some(config.clone()), - Some(&mut rm.borrow_mut()), + Some(&mut *rm.borrow_mut()), )? .into_iter() .map(|(_, v)| v) @@ -998,9 +998,8 @@ impl CreateProjectCommand { } let dm = composer.get_download_manager(); - dm.borrow_mut() - .set_prefer_source(prefer_source) - .set_prefer_dist(prefer_dist); + dm.borrow_mut().set_prefer_source(prefer_source); + dm.borrow_mut().set_prefer_dist(prefer_dist); let project_installer = ProjectInstaller::new(&directory, dm.clone(), fs.clone()); let installation_manager = composer.get_installation_manager().clone(); diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index 37009fb..68aec20 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -34,7 +34,7 @@ use crate::io::IOInterfaceImmutable; use crate::io::NullIO; use crate::json::JsonFile; use crate::json::JsonValidationException; -use crate::package::Locker; +use crate::package::LockerInterface; use crate::package::RootPackage; use crate::package::version::VersionParser; use crate::plugin::CommandEvent; @@ -284,7 +284,7 @@ impl Command for DiagnoseCommand { io.write_no_newline("Checking composer.lock: "); let locker = c.get_locker().clone(); let locker = locker.borrow(); - let r = self.check_composer_lock_schema(&locker)?; + let r = self.check_composer_lock_schema(&*locker)?; self.output_result(r); } } @@ -487,7 +487,7 @@ impl DiagnoseCommand { Ok(PhpMixed::Bool(true)) } - fn check_composer_lock_schema(&self, locker: &Locker) -> anyhow::Result { + fn check_composer_lock_schema(&self, locker: &dyn LockerInterface) -> anyhow::Result { let json = locker.get_json_file(); match json.validate_schema(JsonFile::LOCK_SCHEMA, None) { diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index d9e9973..b0bb755 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -269,7 +269,7 @@ impl Command for DumpAutoloadCommand { &config_ref, local_repo, package, - &mut installation_manager_ref, + &mut *installation_manager_ref, "composer", optimize, None, diff --git a/crates/shirabe/src/composer.rs b/crates/shirabe/src/composer.rs index 57b4c6c..d6c41e2 100644 --- a/crates/shirabe/src/composer.rs +++ b/crates/shirabe/src/composer.rs @@ -3,15 +3,15 @@ use shirabe_external_packages::composer::pcre::Preg; -use crate::autoload::AutoloadGenerator; +use crate::autoload::AutoloadGeneratorInterface; use crate::config::Config; -use crate::downloader::DownloadManager; -use crate::event_dispatcher::EventDispatcher; -use crate::installer::InstallationManager; -use crate::package::archiver::ArchiveManager; -use crate::package::{Locker, RootPackageInterfaceHandle}; +use crate::downloader::DownloadManagerInterface; +use crate::event_dispatcher::EventDispatcherInterface; +use crate::installer::InstallationManagerInterface; +use crate::package::archiver::ArchiveManagerInterface; +use crate::package::{LockerInterface, RootPackageInterfaceHandle}; use crate::plugin::PluginManager; -use crate::repository::RepositoryManager; +use crate::repository::RepositoryManagerInterface; use crate::util::r#loop::Loop; // TODO: change this information to Shirabe version. @@ -37,10 +37,10 @@ pub struct PartialComposer { global: bool, package: Option, r#loop: Option>>, - repository_manager: Option>>, - installation_manager: Option>>, + repository_manager: Option>>, + installation_manager: Option>>, config: Option>>, - event_dispatcher: Option>>, + event_dispatcher: Option>>, } impl PartialComposer { @@ -70,34 +70,40 @@ impl PartialComposer { pub fn set_repository_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { self.repository_manager = Some(manager); } - pub fn get_repository_manager(&self) -> std::rc::Rc> { + pub fn get_repository_manager( + &self, + ) -> std::rc::Rc> { self.repository_manager.as_ref().unwrap().clone() } pub fn set_installation_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { self.installation_manager = Some(manager); } - pub fn get_installation_manager(&self) -> std::rc::Rc> { + pub fn get_installation_manager( + &self, + ) -> std::rc::Rc> { self.installation_manager.as_ref().unwrap().clone() } pub fn set_event_dispatcher( &mut self, - event_dispatcher: std::rc::Rc>, + event_dispatcher: std::rc::Rc>, ) { self.event_dispatcher = Some(event_dispatcher); } - pub fn get_event_dispatcher(&self) -> std::rc::Rc> { + pub fn get_event_dispatcher( + &self, + ) -> std::rc::Rc> { self.event_dispatcher.as_ref().unwrap().clone() } @@ -114,12 +120,12 @@ impl PartialComposer { #[derive(Debug)] pub struct Composer { partial: PartialComposer, - locker: Option>>, - download_manager: Option>>, + locker: Option>>, + download_manager: Option>>, // TODO(plugin): plugin_manager is part of the plugin API plugin_manager: Option>>, - autoload_generator: Option>>, - archive_manager: Option>>, + autoload_generator: Option>>, + archive_manager: Option>>, } impl Default for Composer { @@ -140,33 +146,37 @@ impl Composer { } } - pub fn set_locker(&mut self, locker: std::rc::Rc>) { + pub fn set_locker(&mut self, locker: std::rc::Rc>) { self.locker = Some(locker); } - pub fn get_locker(&self) -> std::rc::Rc> { + pub fn get_locker(&self) -> std::rc::Rc> { self.locker.as_ref().unwrap().clone() } pub fn set_download_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { self.download_manager = Some(manager); } - pub fn get_download_manager(&self) -> std::rc::Rc> { + pub fn get_download_manager( + &self, + ) -> std::rc::Rc> { self.download_manager.as_ref().unwrap().clone() } pub fn set_archive_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { self.archive_manager = Some(manager); } - pub fn get_archive_manager(&self) -> std::rc::Rc> { + pub fn get_archive_manager( + &self, + ) -> std::rc::Rc> { self.archive_manager.as_ref().unwrap().clone() } @@ -182,12 +192,14 @@ impl Composer { pub fn set_autoload_generator( &mut self, - autoload_generator: std::rc::Rc>, + autoload_generator: std::rc::Rc>, ) { self.autoload_generator = Some(autoload_generator); } - pub fn get_autoload_generator(&self) -> std::rc::Rc> { + pub fn get_autoload_generator( + &self, + ) -> std::rc::Rc> { self.autoload_generator.as_ref().unwrap().clone() } @@ -225,40 +237,45 @@ impl Composer { pub fn set_repository_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { self.partial.set_repository_manager(manager); } pub fn get_repository_manager( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> { self.partial.get_repository_manager() } pub fn set_installation_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc< + std::cell::RefCell, + >, ) { self.partial.set_installation_manager(manager); } pub fn get_installation_manager( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> { self.partial.get_installation_manager() } pub fn set_event_dispatcher( &mut self, - dispatcher: std::rc::Rc>, + dispatcher: std::rc::Rc< + std::cell::RefCell, + >, ) { self.partial.set_event_dispatcher(dispatcher); } pub fn get_event_dispatcher( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> + { self.partial.get_event_dispatcher() } @@ -366,7 +383,7 @@ impl PartialOrFullComposer { pub fn set_repository_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc>, ) { match self { Self::Full(full) => full.set_repository_manager(manager), @@ -376,7 +393,7 @@ impl PartialOrFullComposer { pub fn get_repository_manager( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> { match self { Self::Full(full) => full.get_repository_manager(), Self::Partial(partial) => partial.get_repository_manager(), @@ -385,7 +402,9 @@ impl PartialOrFullComposer { pub fn set_installation_manager( &mut self, - manager: std::rc::Rc>, + manager: std::rc::Rc< + std::cell::RefCell, + >, ) { match self { Self::Full(full) => full.set_installation_manager(manager), @@ -395,7 +414,7 @@ impl PartialOrFullComposer { pub fn get_installation_manager( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> { match self { Self::Full(full) => full.get_installation_manager(), Self::Partial(partial) => partial.get_installation_manager(), @@ -404,7 +423,9 @@ impl PartialOrFullComposer { pub fn set_event_dispatcher( &mut self, - dispatcher: std::rc::Rc>, + dispatcher: std::rc::Rc< + std::cell::RefCell, + >, ) { match self { Self::Full(full) => full.set_event_dispatcher(dispatcher), @@ -414,7 +435,8 @@ impl PartialOrFullComposer { pub fn get_event_dispatcher( &self, - ) -> std::rc::Rc> { + ) -> std::rc::Rc> + { match self { Self::Full(full) => full.get_event_dispatcher(), Self::Partial(partial) => partial.get_event_dispatcher(), diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index de1ecfc..f3e4292 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -2383,7 +2383,7 @@ impl ApplicationHandle { let installation_manager = composer.get_installation_manager(); let package_map = generator.build_package_map( - &mut installation_manager.borrow_mut(), + &mut *installation_manager.borrow_mut(), root_package.clone(), vec![], )?; diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index e2c5b11..843818f 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -19,7 +19,7 @@ use crate::dependency_resolver::Pool; use crate::dependency_resolver::PoolOptimizer; use crate::dependency_resolver::Request; use crate::dependency_resolver::SecurityAdvisoryPoolFilter; -use crate::event_dispatcher::EventDispatcher; +use crate::event_dispatcher::EventDispatcherInterface; use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; use crate::package::AliasPackageHandle; @@ -40,7 +40,7 @@ pub struct PoolBuilder { root_aliases: IndexMap>>, root_references: IndexMap, temporary_constraints: IndexMap, - event_dispatcher: Option>>, + event_dispatcher: Option>>, pool_optimizer: Option, io: std::rc::Rc>, alias_map: IndexMap>, @@ -85,7 +85,7 @@ impl PoolBuilder { root_aliases: IndexMap>>, root_references: IndexMap, io: std::rc::Rc>, - event_dispatcher: Option>>, + event_dispatcher: Option>>, pool_optimizer: Option, temporary_constraints: IndexMap, security_advisory_pool_filter: Option, diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs index 183813d..a5a83a6 100644 --- a/crates/shirabe/src/downloader/download_manager.rs +++ b/crates/shirabe/src/downloader/download_manager.rs @@ -60,28 +60,22 @@ impl DownloadManager { /// Makes downloader prefer source installation over the dist. /// /// @param bool $preferSource prefer downloading from source - pub fn set_prefer_source(&mut self, prefer_source: bool) -> &mut Self { + pub fn set_prefer_source(&mut self, prefer_source: bool) { self.prefer_source = prefer_source; - - self } /// Makes downloader prefer dist installation over the source. /// /// @param bool $preferDist prefer downloading from dist - pub fn set_prefer_dist(&mut self, prefer_dist: bool) -> &mut Self { + pub fn set_prefer_dist(&mut self, prefer_dist: bool) { self.prefer_dist = prefer_dist; - - self } /// Sets fine tuned preference settings for package level source/dist selection. /// /// @param array $preferences array of preferences by package patterns - pub fn set_preferences(&mut self, preferences: IndexMap) -> &mut Self { + pub fn set_preferences(&mut self, preferences: IndexMap) { self.package_preferences = preferences; - - self } /// Sets installer downloader for a specific installation type. @@ -92,11 +86,9 @@ impl DownloadManager { &mut self, r#type: &str, downloader: std::rc::Rc>, - ) -> &mut Self { + ) { let r#type = strtolower(r#type); self.downloaders.insert(r#type, downloader); - - self } /// Returns downloader for a specific installation type. @@ -551,3 +543,126 @@ impl DownloadManager { rtrim(dir, Some("\\/")) } } + +// Composer's Composer::setDownloadManager() accepts any DownloadManager subclass, so plugins may +// swap in a replacement. The interface captures the methods reached through Composer's accessor and +// through the `Rc>` references fed from it. +#[async_trait::async_trait(?Send)] +pub trait DownloadManagerInterface: std::fmt::Debug { + fn set_prefer_source(&mut self, prefer_source: bool); + fn set_prefer_dist(&mut self, prefer_dist: bool); + fn get_downloader_for_package( + &self, + package: PackageInterfaceHandle, + ) -> Result>>>; + async fn download( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result>; + async fn prepare( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result>; + async fn install( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> Result>; + async fn update( + &self, + initial: PackageInterfaceHandle, + target: PackageInterfaceHandle, + target_dir: &str, + ) -> Result>; + async fn remove( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> Result>; + async fn cleanup( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result>; +} + +#[async_trait::async_trait(?Send)] +impl DownloadManagerInterface for DownloadManager { + fn set_prefer_source(&mut self, prefer_source: bool) { + self.set_prefer_source(prefer_source); + } + + fn set_prefer_dist(&mut self, prefer_dist: bool) { + self.set_prefer_dist(prefer_dist); + } + + fn get_downloader_for_package( + &self, + package: PackageInterfaceHandle, + ) -> Result>>> { + self.get_downloader_for_package(package) + } + + async fn download( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result> { + self.download(package, target_dir, prev_package).await + } + + async fn prepare( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result> { + self.prepare(r#type, package, target_dir, prev_package) + .await + } + + async fn install( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> Result> { + self.install(package, target_dir).await + } + + async fn update( + &self, + initial: PackageInterfaceHandle, + target: PackageInterfaceHandle, + target_dir: &str, + ) -> Result> { + self.update(initial, target, target_dir).await + } + + async fn remove( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> Result> { + self.remove(package, target_dir).await + } + + async fn cleanup( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option, + ) -> Result> { + self.cleanup(r#type, package, target_dir, prev_package) + .await + } +} diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs index f74b4a2..091160e 100644 --- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs +++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs @@ -125,10 +125,8 @@ impl EventDispatcher { } /// Set whether script handlers are active or not - pub fn set_run_scripts(&mut self, run_scripts: bool) -> &mut Self { + pub fn set_run_scripts(&mut self, run_scripts: bool) { self.run_scripts = run_scripts; - - self } /// Dispatch an event @@ -1193,7 +1191,7 @@ impl EventDispatcher { let installation_manager = composer.get_installation_manager(); let package_map = generator.build_package_map( - &mut installation_manager.borrow_mut(), + &mut *installation_manager.borrow_mut(), package.clone(), packages, )?; @@ -1245,3 +1243,68 @@ impl EventDispatcher { } } } + +// Composer's PartialComposer::setEventDispatcher() accepts any EventDispatcher subclass, so plugins +// may swap in a replacement. The interface captures the methods reached through Composer's accessor +// and through the `Rc>` references fed from it. +pub trait EventDispatcherInterface: std::fmt::Debug { + fn dispatch( + &mut self, + event_name: Option<&str>, + event: Option<&mut dyn EventInterface>, + ) -> anyhow::Result; + fn dispatch_script( + &mut self, + event_name: &str, + dev_mode: bool, + additional_args: Vec, + flags: IndexMap, + ) -> anyhow::Result; + fn dispatch_installer_event( + &mut self, + event_name: &str, + dev_mode: bool, + execute_operations: bool, + transaction: Transaction, + ) -> anyhow::Result; + fn add_listener(&mut self, event_name: &str, listener: Callable, priority: i64); + fn has_event_listeners(&mut self, event: &dyn EventInterface) -> bool; +} + +impl EventDispatcherInterface for EventDispatcher { + fn dispatch( + &mut self, + event_name: Option<&str>, + event: Option<&mut dyn EventInterface>, + ) -> anyhow::Result { + self.dispatch(event_name, event) + } + + fn dispatch_script( + &mut self, + event_name: &str, + dev_mode: bool, + additional_args: Vec, + flags: IndexMap, + ) -> anyhow::Result { + self.dispatch_script(event_name, dev_mode, additional_args, flags) + } + + fn dispatch_installer_event( + &mut self, + event_name: &str, + dev_mode: bool, + execute_operations: bool, + transaction: Transaction, + ) -> anyhow::Result { + self.dispatch_installer_event(event_name, dev_mode, execute_operations, transaction) + } + + fn add_listener(&mut self, event_name: &str, listener: Callable, priority: i64) { + self.add_listener(event_name, listener, priority); + } + + fn has_event_listeners(&mut self, event: &dyn EventInterface) -> bool { + self.has_event_listeners(event) + } +} diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs index 2590b49..f051563 100644 --- a/crates/shirabe/src/installer.rs +++ b/crates/shirabe/src/installer.rs @@ -42,7 +42,7 @@ use shirabe_semver; use crate::advisory::AuditConfig; use crate::advisory::Auditor; -use crate::autoload::AutoloadGenerator; +use crate::autoload::AutoloadGeneratorInterface; use crate::composer::PartialComposerHandle; use crate::config::Config; use crate::console::GithubActionError; @@ -56,9 +56,9 @@ use crate::dependency_resolver::SecurityAdvisoryPoolFilter; use crate::dependency_resolver::Solver; use crate::dependency_resolver::UpdateAllowTransitiveDeps; use crate::dependency_resolver::operation::OperationInterface; -use crate::downloader::DownloadManager; +use crate::downloader::DownloadManagerInterface; use crate::downloader::TransportException; -use crate::event_dispatcher::EventDispatcher; +use crate::event_dispatcher::EventDispatcherInterface; use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFilter; use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; @@ -67,7 +67,7 @@ use crate::io::IOInterfaceImmutable; use crate::package::AliasPackageHandle; use crate::package::CompleteAliasPackageHandle; use crate::package::Link; -use crate::package::Locker; +use crate::package::LockerInterface; use crate::package::PackageInterfaceHandle; use crate::package::RootPackageInterfaceHandle; use crate::package::base_package; @@ -83,7 +83,7 @@ use crate::repository::InstalledRepository; use crate::repository::PlatformRepository; use crate::repository::PlatformRepositoryHandle; use crate::repository::RepositoryInterface; -use crate::repository::RepositoryManager; +use crate::repository::RepositoryManagerInterface; use crate::repository::RepositorySet; use crate::repository::RootPackageRepository; use crate::script::ScriptEvents; @@ -98,12 +98,13 @@ pub struct Installer { pub(crate) package: RootPackageInterfaceHandle, // TODO can we get rid of the below and just use the package itself? pub(crate) fixed_root_package: RootPackageInterfaceHandle, - pub(crate) download_manager: std::rc::Rc>, - pub(crate) repository_manager: std::rc::Rc>, - pub(crate) locker: std::rc::Rc>, - pub(crate) installation_manager: std::rc::Rc>, - pub(crate) event_dispatcher: std::rc::Rc>, - pub(crate) autoload_generator: std::rc::Rc>, + pub(crate) download_manager: std::rc::Rc>, + pub(crate) repository_manager: std::rc::Rc>, + pub(crate) locker: std::rc::Rc>, + pub(crate) installation_manager: + std::rc::Rc>, + pub(crate) event_dispatcher: std::rc::Rc>, + pub(crate) autoload_generator: std::rc::Rc>, pub(crate) prefer_source: bool, pub(crate) prefer_dist: bool, pub(crate) optimize_autoloader: bool, @@ -155,12 +156,12 @@ impl Installer { io: std::rc::Rc>, config: std::rc::Rc>, package: RootPackageInterfaceHandle, - download_manager: std::rc::Rc>, - repository_manager: std::rc::Rc>, - locker: std::rc::Rc>, - installation_manager: std::rc::Rc>, - event_dispatcher: std::rc::Rc>, - autoload_generator: std::rc::Rc>, + download_manager: std::rc::Rc>, + repository_manager: std::rc::Rc>, + locker: std::rc::Rc>, + installation_manager: std::rc::Rc>, + event_dispatcher: std::rc::Rc>, + autoload_generator: std::rc::Rc>, ) -> Self { let suggested_packages_reporter = std::rc::Rc::new(std::cell::RefCell::new( SuggestedPackagesReporter::new(io.clone()), @@ -249,7 +250,7 @@ impl Installer { self.write_lock = false; self.dump_autoloader = false; let repository_manager = self.repository_manager.clone(); - self.mock_local_repositories(&mut repository_manager.borrow_mut())?; + self.mock_local_repositories(&mut *repository_manager.borrow_mut())?; } if self.download_only { @@ -409,7 +410,7 @@ impl Installer { .as_installed_repository_interface_mut() .unwrap(), self.package.clone(), - &mut self.installation_manager.borrow_mut(), + &mut *self.installation_manager.borrow_mut(), "composer", self.optimize_autoloader, None, @@ -1651,7 +1652,10 @@ impl Installer { /// Replace local repositories with InstalledArrayRepository instances /// /// This is to prevent any accidental modification of the existing repos on disk - fn mock_local_repositories(&self, rm: &mut RepositoryManager) -> anyhow::Result<()> { + fn mock_local_repositories( + &self, + rm: &mut dyn RepositoryManagerInterface, + ) -> anyhow::Result<()> { let mut packages: IndexMap = IndexMap::new(); for package in rm.get_local_repository().get_packages()? { packages.insert(package.to_string(), PackageInterfaceHandle::dup(&package)); diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index ac5f8e4..bee14cc 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -791,3 +791,78 @@ impl InstallationManager { } } } + +// Composer's PartialComposer::setInstallationManager() accepts any InstallationManager subclass, so +// plugins may swap in a replacement. The interface captures the methods reached through Composer's +// accessor and through the `&mut dyn InstallationManagerInterface` references fed from it. +pub trait InstallationManagerInterface: std::fmt::Debug { + fn add_installer(&mut self, installer: Box); + fn remove_installer(&mut self, installer: &dyn InstallerInterface); + fn disable_plugins(&mut self); + fn is_package_installed( + &mut self, + repo: &dyn InstalledRepositoryInterface, + package: PackageInterfaceHandle, + ) -> Result; + fn ensure_binaries_presence(&mut self, package: PackageInterfaceHandle); + fn execute( + &mut self, + repo: &mut dyn InstalledRepositoryInterface, + operations: Vec>, + dev_mode: bool, + run_scripts: bool, + download_only: bool, + ) -> Result<()>; + fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option; + fn set_output_progress(&mut self, output_progress: bool); + fn notify_installs(&mut self, io: std::rc::Rc>); +} + +impl InstallationManagerInterface for InstallationManager { + fn add_installer(&mut self, installer: Box) { + self.add_installer(installer); + } + + fn remove_installer(&mut self, installer: &dyn InstallerInterface) { + self.remove_installer(installer); + } + + fn disable_plugins(&mut self) { + self.disable_plugins(); + } + + fn is_package_installed( + &mut self, + repo: &dyn InstalledRepositoryInterface, + package: PackageInterfaceHandle, + ) -> Result { + self.is_package_installed(repo, package) + } + + fn ensure_binaries_presence(&mut self, package: PackageInterfaceHandle) { + self.ensure_binaries_presence(package); + } + + fn execute( + &mut self, + repo: &mut dyn InstalledRepositoryInterface, + operations: Vec>, + dev_mode: bool, + run_scripts: bool, + download_only: bool, + ) -> Result<()> { + self.execute(repo, operations, dev_mode, run_scripts, download_only) + } + + fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option { + self.get_install_path(package) + } + + fn set_output_progress(&mut self, output_progress: bool) { + self.set_output_progress(output_progress); + } + + fn notify_installs(&mut self, io: std::rc::Rc>) { + self.notify_installs(io); + } +} diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index e2c6c0c..e1c3aec 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -8,7 +8,7 @@ use shirabe_php_shim::{ }; use crate::composer::PartialComposerWeakHandle; -use crate::downloader::DownloadManager; +use crate::downloader::DownloadManagerInterface; use crate::installer::BinaryInstaller; use crate::installer::BinaryPresenceInterface; use crate::installer::InstallerInterface; @@ -24,7 +24,8 @@ use crate::util::Silencer; pub struct LibraryInstaller { pub(crate) composer: PartialComposerWeakHandle, pub(crate) vendor_dir: String, - pub(crate) download_manager: Option>>, + pub(crate) download_manager: + Option>>, pub(crate) io: std::rc::Rc>, pub(crate) r#type: Option, pub(crate) filesystem: std::rc::Rc>, @@ -192,7 +193,9 @@ impl LibraryInstaller { self.vendor_dir = realpath(&self.vendor_dir).unwrap_or_default(); } - pub(crate) fn get_download_manager(&self) -> &std::rc::Rc> { + pub(crate) fn get_download_manager( + &self, + ) -> &std::rc::Rc> { // PHP: assert($this->downloadManager instanceof DownloadManager, new \LogicException(...)) assert!( self.download_manager.is_some(), diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs index 351fd35..83ffbb2 100644 --- a/crates/shirabe/src/installer/project_installer.rs +++ b/crates/shirabe/src/installer/project_installer.rs @@ -1,6 +1,6 @@ //! ref: composer/src/Composer/Installer/ProjectInstaller.php -use crate::downloader::DownloadManager; +use crate::downloader::DownloadManagerInterface; use crate::installer::InstallerInterface; use crate::package::PackageInterfaceHandle; use crate::repository::InstalledRepositoryInterface; @@ -10,14 +10,14 @@ use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; #[derive(Debug)] pub struct ProjectInstaller { install_path: String, - download_manager: std::rc::Rc>, + download_manager: std::rc::Rc>, filesystem: std::rc::Rc>, } impl ProjectInstaller { pub fn new( install_path: &str, - dm: std::rc::Rc>, + dm: std::rc::Rc>, fs: std::rc::Rc>, ) -> Self { let install_path = format!("{}/", install_path.replace('\\', "/").trim_end_matches('/')); diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs index 139eb9a..be911d6 100644 --- a/crates/shirabe/src/package/archiver/archive_manager.rs +++ b/crates/shirabe/src/package/archiver/archive_manager.rs @@ -49,9 +49,8 @@ impl ArchiveManager { self.archivers.push(archiver); } - pub fn set_overwrite_files(&mut self, overwrite_files: bool) -> &mut Self { + pub fn set_overwrite_files(&mut self, overwrite_files: bool) { self.overwrite_files = overwrite_files; - self } pub fn get_package_filename_parts( @@ -314,3 +313,29 @@ impl ArchiveManager { formats } } + +// Composer's Composer::setArchiveManager() accepts any ArchiveManager subclass, so plugins may +// swap in a replacement. The interface captures the methods reached through Composer's accessor. +pub trait ArchiveManagerInterface: std::fmt::Debug { + fn archive( + &mut self, + package: CompletePackageInterfaceHandle, + format: String, + target_dir: String, + file_name: Option, + ignore_filters: bool, + ) -> anyhow::Result; +} + +impl ArchiveManagerInterface for ArchiveManager { + fn archive( + &mut self, + package: CompletePackageInterfaceHandle, + format: String, + target_dir: String, + file_name: Option, + ignore_filters: bool, + ) -> anyhow::Result { + self.archive(package, format, target_dir, file_name, ignore_filters) + } +} diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index 3a87061..df5e1de 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -642,14 +642,13 @@ impl Locker { } /// Updates the lock file's hash in-place from a given composer.json's JsonFile - pub fn update_hash( + pub fn update_hash( &mut self, composer_json: &JsonFile, - data_processor: Option, - ) -> Result<()> - where - F: FnOnce(IndexMap) -> IndexMap, - { + data_processor: Option< + Box) -> IndexMap>, + >, + ) -> Result<()> { let contents = file_get_contents(composer_json.get_path()); let contents = match contents { Some(s) => s, @@ -1002,6 +1001,159 @@ impl Locker { } } +// Composer's Composer::setLocker() accepts any Locker subclass, so plugins may swap in a +// replacement. The interface captures the methods reached through Composer's accessor and through +// the `&mut dyn LockerInterface` / `&dyn LockerInterface` references fed from it. +pub trait LockerInterface: std::fmt::Debug { + fn get_json_file(&self) -> &JsonFile; + fn is_locked(&mut self) -> bool; + fn is_fresh(&mut self) -> Result; + fn get_locked_repository(&mut self, with_dev_reqs: bool) -> Result; + fn get_dev_package_names(&mut self) -> Result>; + fn get_platform_requirements(&mut self, with_dev_reqs: bool) -> Result>; + fn get_minimum_stability(&mut self) -> Result; + fn get_stability_flags(&mut self) -> Result>; + fn get_prefer_stable(&mut self) -> Result>; + fn get_prefer_lowest(&mut self) -> Result>; + fn get_platform_overrides(&mut self) -> Result>; + fn get_aliases(&mut self) -> Result>>; + fn get_plugin_api(&mut self) -> Result; + fn get_lock_data(&mut self) -> Result>; + #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] + fn set_lock_data( + &mut self, + packages: Vec, + dev_packages: Option>, + platform_reqs: IndexMap, + platform_dev_reqs: IndexMap, + aliases: Vec>, + minimum_stability: &str, + stability_flags: IndexMap, + prefer_stable: bool, + prefer_lowest: bool, + platform_overrides: IndexMap, + write: bool, + ) -> Result; + fn update_hash( + &mut self, + composer_json: &JsonFile, + data_processor: Option< + Box) -> IndexMap>, + >, + ) -> Result<()>; + fn get_missing_requirement_info( + &mut self, + package: RootPackageInterfaceHandle, + include_dev: bool, + ) -> Result>; +} + +impl LockerInterface for Locker { + fn get_json_file(&self) -> &JsonFile { + self.get_json_file() + } + + fn is_locked(&mut self) -> bool { + self.is_locked() + } + + fn is_fresh(&mut self) -> Result { + self.is_fresh() + } + + fn get_locked_repository(&mut self, with_dev_reqs: bool) -> Result { + self.get_locked_repository(with_dev_reqs) + } + + fn get_dev_package_names(&mut self) -> Result> { + self.get_dev_package_names() + } + + fn get_platform_requirements(&mut self, with_dev_reqs: bool) -> Result> { + self.get_platform_requirements(with_dev_reqs) + } + + fn get_minimum_stability(&mut self) -> Result { + self.get_minimum_stability() + } + + fn get_stability_flags(&mut self) -> Result> { + self.get_stability_flags() + } + + fn get_prefer_stable(&mut self) -> Result> { + self.get_prefer_stable() + } + + fn get_prefer_lowest(&mut self) -> Result> { + self.get_prefer_lowest() + } + + fn get_platform_overrides(&mut self) -> Result> { + self.get_platform_overrides() + } + + fn get_aliases(&mut self) -> Result>> { + self.get_aliases() + } + + fn get_plugin_api(&mut self) -> Result { + self.get_plugin_api() + } + + fn get_lock_data(&mut self) -> Result> { + self.get_lock_data() + } + + #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] + fn set_lock_data( + &mut self, + packages: Vec, + dev_packages: Option>, + platform_reqs: IndexMap, + platform_dev_reqs: IndexMap, + aliases: Vec>, + minimum_stability: &str, + stability_flags: IndexMap, + prefer_stable: bool, + prefer_lowest: bool, + platform_overrides: IndexMap, + write: bool, + ) -> Result { + self.set_lock_data( + packages, + dev_packages, + platform_reqs, + platform_dev_reqs, + aliases, + minimum_stability, + stability_flags, + prefer_stable, + prefer_lowest, + platform_overrides, + write, + ) + } + + fn update_hash( + &mut self, + composer_json: &JsonFile, + data_processor: Option< + Box) -> IndexMap>, + >, + ) -> Result<()> { + self.update_hash(composer_json, data_processor) + } + + fn get_missing_requirement_info( + &mut self, + package: RootPackageInterfaceHandle, + include_dev: bool, + ) -> Result> { + self.get_missing_requirement_info(package, include_dev) + } +} + struct SetEntry { repo: LockArrayRepositoryHandle, method: String, diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index c1cd38e..a949c03 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -19,7 +19,7 @@ use crate::factory::DisablePlugins; use crate::installer::InstallerInterface; use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; -use crate::package::Locker; +use crate::package::LockerInterface; use crate::package::PackageInterfaceHandle; use crate::package::RootPackageInterfaceHandle; use crate::package::base_package::{self}; @@ -738,7 +738,7 @@ impl PluginManager { fn parse_allowed_plugins( allow_plugins_config: PhpMixed, - mut locker: Option<&mut Locker>, + mut locker: Option<&mut dyn LockerInterface>, ) -> Option> { // PHP: [] === $allowPluginsConfig && $locker !== null && $locker->isLocked() && version_compare($locker->getPluginApi(), '2.2.0', '<') let is_empty_array = allow_plugins_config diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs index db8185e..bc08589 100644 --- a/crates/shirabe/src/repository/repository_factory.rs +++ b/crates/shirabe/src/repository/repository_factory.rs @@ -15,6 +15,7 @@ use crate::json::JsonFile; use crate::repository::FilesystemRepository; use crate::repository::RepositoryInterfaceHandle; use crate::repository::RepositoryManager; +use crate::repository::RepositoryManagerInterface; use crate::util::HttpDownloader; use crate::util::ProcessExecutor; @@ -98,7 +99,7 @@ impl RepositoryFactory { config: &std::rc::Rc>, repository: &str, allow_filesystem: bool, - rm: Option<&mut RepositoryManager>, + rm: Option<&mut dyn RepositoryManagerInterface>, ) -> anyhow::Result { let repo_config = Self::config_from_string(io.clone(), config, repository, allow_filesystem)?; @@ -109,7 +110,7 @@ impl RepositoryFactory { io: std::rc::Rc>, config: &std::rc::Rc>, repo_config: IndexMap, - rm: Option<&mut RepositoryManager>, + rm: Option<&mut dyn RepositoryManagerInterface>, ) -> anyhow::Result { let mut owned_rm; let rm = if let Some(rm) = rm { @@ -134,7 +135,7 @@ impl RepositoryFactory { pub fn default_repos( io: Option>>, config: Option>>, - rm: Option<&mut RepositoryManager>, + rm: Option<&mut dyn RepositoryManagerInterface>, ) -> anyhow::Result> { let config = match config { Some(c) => c, @@ -235,7 +236,7 @@ impl RepositoryFactory { } fn create_repos( - rm: &mut RepositoryManager, + rm: &mut dyn RepositoryManagerInterface, repo_configs: Vec, ) -> anyhow::Result> { let mut repo_map: IndexMap = IndexMap::new(); diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs index 247236c..41f4e7d 100644 --- a/crates/shirabe/src/repository/repository_manager.rs +++ b/crates/shirabe/src/repository/repository_manager.rs @@ -196,3 +196,46 @@ impl RepositoryManager { self.local_repository.clone().unwrap() } } + +// Composer's PartialComposer::setRepositoryManager() accepts any RepositoryManager subclass, so +// plugins may swap in a replacement. The interface captures the methods reached through Composer's +// accessor and through the `&mut RepositoryManager` parameters fed from it. +pub trait RepositoryManagerInterface: std::fmt::Debug { + fn get_local_repository(&self) -> RepositoryInterfaceHandle; + fn get_repositories(&self) -> &Vec; + fn create_repository( + &self, + r#type: &str, + config: IndexMap, + name: Option<&str>, + ) -> anyhow::Result; + fn add_repository(&mut self, repository: RepositoryInterfaceHandle); + fn set_local_repository(&mut self, repository: RepositoryInterfaceHandle); +} + +impl RepositoryManagerInterface for RepositoryManager { + fn get_local_repository(&self) -> RepositoryInterfaceHandle { + self.get_local_repository() + } + + fn get_repositories(&self) -> &Vec { + self.get_repositories() + } + + fn create_repository( + &self, + r#type: &str, + config: IndexMap, + name: Option<&str>, + ) -> anyhow::Result { + self.create_repository(r#type, config, name) + } + + fn add_repository(&mut self, repository: RepositoryInterfaceHandle) { + self.add_repository(repository); + } + + fn set_local_repository(&mut self, repository: RepositoryInterfaceHandle) { + self.set_local_repository(repository); + } +} diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs index ba6373a..393639f 100644 --- a/crates/shirabe/src/repository/repository_set.rs +++ b/crates/shirabe/src/repository/repository_set.rs @@ -15,7 +15,7 @@ use crate::dependency_resolver::PoolOptimizer; use crate::dependency_resolver::Request; use crate::dependency_resolver::SecurityAdvisoryPoolFilter; use crate::downloader::TransportException; -use crate::event_dispatcher::EventDispatcher; +use crate::event_dispatcher::EventDispatcherInterface; use crate::io::IOInterface; use crate::io::NullIO; use crate::package::AliasPackageHandle; @@ -451,7 +451,7 @@ impl RepositorySet { &mut self, request: &mut Request, io: std::rc::Rc>, - event_dispatcher: Option>>, + event_dispatcher: Option>>, pool_optimizer: Option, ignored_types: Vec, allowed_types: Option>, diff --git a/crates/shirabe/src/util/sync_helper.rs b/crates/shirabe/src/util/sync_helper.rs index 6220f5c..8988795 100644 --- a/crates/shirabe/src/util/sync_helper.rs +++ b/crates/shirabe/src/util/sync_helper.rs @@ -1,6 +1,6 @@ //! ref: composer/src/Composer/Util/SyncHelper.php -use crate::downloader::DownloadManager; +use crate::downloader::DownloadManagerInterface; use crate::downloader::DownloaderInterface; use crate::package::PackageInterfaceHandle; use crate::util::r#loop::Loop; @@ -9,7 +9,7 @@ use shirabe_php_shim::PhpMixed; pub enum DownloaderOrManager<'a> { Interface(&'a std::rc::Rc>), - Manager(&'a std::rc::Rc>), + Manager(&'a std::rc::Rc>), } impl<'a> DownloaderOrManager<'a> { diff --git a/crates/shirabe/tests/composer_test.rs b/crates/shirabe/tests/composer_test.rs index 908e813..bffe4ce 100644 --- a/crates/shirabe/tests/composer_test.rs +++ b/crates/shirabe/tests/composer_test.rs @@ -7,12 +7,15 @@ use indexmap::IndexMap; use shirabe::composer::Composer; use shirabe::config::Config; use shirabe::downloader::DownloadManager; +use shirabe::downloader::DownloadManagerInterface; use shirabe::installer::InstallationManager; +use shirabe::installer::InstallationManagerInterface; use shirabe::io::IOInterface; use shirabe::io::null_io::NullIO; use shirabe::json::JsonFile; -use shirabe::package::{Locker, RootPackageHandle, RootPackageInterfaceHandle}; +use shirabe::package::{Locker, LockerInterface, RootPackageHandle, RootPackageInterfaceHandle}; use shirabe::repository::RepositoryManager; +use shirabe::repository::RepositoryManagerInterface; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe::util::process_executor::ProcessExecutor; @@ -60,7 +63,7 @@ fn test_set_get_locker() { let io = null_io(); let json_file = JsonFile::new("composer.lock".to_string(), None, None).unwrap(); let process = Rc::new(RefCell::new(ProcessExecutor::new(Some(io.clone())))); - let locker = Rc::new(RefCell::new(Locker::new( + let locker: Rc> = Rc::new(RefCell::new(Locker::new( io.clone(), json_file, installation_manager(&io), @@ -77,13 +80,9 @@ fn test_set_get_repository_manager() { let mut composer = Composer::new(); let io = null_io(); let config = Rc::new(RefCell::new(Config::new(false, None))); - let manager = Rc::new(RefCell::new(RepositoryManager::new( - io.clone(), - config, - http_downloader(&io), - None, - None, - ))); + let manager: Rc> = Rc::new(RefCell::new( + RepositoryManager::new(io.clone(), config, http_downloader(&io), None, None), + )); composer.set_repository_manager(manager.clone()); assert!(Rc::ptr_eq(&composer.get_repository_manager(), &manager)); @@ -92,7 +91,8 @@ fn test_set_get_repository_manager() { #[test] fn test_set_get_download_manager() { let mut composer = Composer::new(); - let manager = Rc::new(RefCell::new(DownloadManager::new(null_io(), false, None))); + let manager: Rc> = + Rc::new(RefCell::new(DownloadManager::new(null_io(), false, None))); composer.set_download_manager(manager.clone()); assert!(Rc::ptr_eq(&composer.get_download_manager(), &manager)); @@ -102,7 +102,7 @@ fn test_set_get_download_manager() { fn test_set_get_installation_manager() { let mut composer = Composer::new(); let io = null_io(); - let manager = installation_manager(&io); + let manager: Rc> = installation_manager(&io); composer.set_installation_manager(manager.clone()); assert!(Rc::ptr_eq(&composer.get_installation_manager(), &manager)); -- cgit v1.3.1