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/package/locker.rs | 164 +++++++++++++++++++++++++++++++++-- 1 file changed, 158 insertions(+), 6 deletions(-) (limited to 'crates/shirabe/src/package/locker.rs') 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, -- cgit v1.3.1