aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-20 03:44:17 +0900
committernsfisis <nsfisis@gmail.com>2026-06-20 13:22:55 +0900
commitfc39608de4226286c34aed0c0803edd15a8623a8 (patch)
treeb166d1cdbc603538e34a0ec0bb68b13da7795195 /crates/shirabe/src
parent1afc35c977eb443967fc768d67057a28b56ba15b (diff)
downloadphp-shirabe-fc39608de4226286c34aed0c0803edd15a8623a8.tar.gz
php-shirabe-fc39608de4226286c34aed0c0803edd15a8623a8.tar.zst
php-shirabe-fc39608de4226286c34aed0c0803edd15a8623a8.zip
refactor(installation-manager): cache installers by index, drop clone_box
InstallationManager::get_installer cached a per-type installer via a Rust-only `clone_box` stub (`todo!()`). PHP caches the SAME instance held in `$this->installers`. Store the index into `installers` instead, reproducing that sharing without cloning; both `add_installer` and `remove_installer` clear the cache, so indices never dangle. With the only caller gone, `InstallerInterface::clone_box` is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs55
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs4
2 files changed, 14 insertions, 45 deletions
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 6f8b65c..4d813a5 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -33,11 +33,12 @@ use crate::util::r#loop::Loop;
/// Package operation manager.
#[derive(Debug)]
pub struct InstallationManager {
- /// @var list<InstallerInterface>
installers: Vec<Box<dyn InstallerInterface>>,
- /// @var array<string, InstallerInterface>
- cache: IndexMap<String, Box<dyn InstallerInterface>>,
- /// @var array<string, array<PackageInterface>>
+ /// Maps a package type to the index of its installer in `installers`. PHP caches the installer
+ /// instance itself; here we store an index instead to avoid sharing ownership of the boxed
+ /// installer. The index never dangles because both `add_installer` and `remove_installer`
+ /// clear the cache whenever `installers` changes.
+ cache: IndexMap<String, usize>,
notifiable_packages: IndexMap<String, Vec<PackageInterfaceHandle>>,
loop_: std::rc::Rc<std::cell::RefCell<Loop>>,
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
@@ -68,16 +69,12 @@ impl InstallationManager {
}
/// Adds installer
- ///
- /// @param InstallerInterface $installer installer instance
pub fn add_installer(&mut self, installer: Box<dyn InstallerInterface>) {
array_unshift(&mut self.installers, installer);
self.cache = IndexMap::new();
}
/// Removes installer
- ///
- /// @param InstallerInterface $installer installer instance
pub fn remove_installer(&mut self, installer: &dyn InstallerInterface) {
let target = installer as *const dyn InstallerInterface as *const ();
let key = self
@@ -104,30 +101,20 @@ impl InstallationManager {
}
/// Returns installer for a specific package type.
- ///
- /// @param string $type package type
- ///
- /// @throws \InvalidArgumentException if installer for provided type is not registered
pub fn get_installer(&mut self, r#type: &str) -> Result<&mut dyn InstallerInterface> {
let r#type = strtolower(r#type);
- if self.cache.contains_key(&r#type) {
- return Ok(self.cache.get_mut(&r#type).unwrap().as_mut());
+ if let Some(&index) = self.cache.get(&r#type) {
+ return Ok(self.installers[index].as_mut());
}
- for installer in &self.installers {
- if installer.supports(&r#type) {
- // PHP: return $this->cache[$type] = $installer; — the cache holds the SAME
- // installer instance as $this->installers.
- // TODO(phase-c): faithfully sharing the instance requires storing installers as
- // Rc<RefCell<dyn InstallerInterface>> (so the cache clones the Rc, not the value).
- // That refactor is blocked because get_installer's result is used across `.await`
- // points in download/install/update/uninstall, where a RefCell borrow cannot be
- // held; it is coupled to the async/React-Promise execution rework this file's
- // execute() path depends on. clone_box (itself a todo!()) is the placeholder.
- self.cache.insert(r#type.clone(), installer.clone_box());
- return Ok(self.cache.get_mut(&r#type).unwrap().as_mut());
- }
+ let index = self
+ .installers
+ .iter()
+ .position(|installer| installer.supports(&r#type));
+ if let Some(index) = index {
+ self.cache.insert(r#type.clone(), index);
+ return Ok(self.installers[index].as_mut());
}
Err(InvalidArgumentException {
@@ -419,9 +406,6 @@ impl InstallationManager {
Ok(())
}
- /// @param OperationInterface[] $operations List of operations to execute in this batch
- /// @param OperationInterface[] $allOperations Complete list of operations to be executed in the install job, used for event listeners
- /// @phpstan-param array<callable(): ?PromiseInterface<void|null>> $cleanupPromises
async fn execute_batch(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
@@ -557,8 +541,6 @@ impl InstallationManager {
}
/// Executes download operation.
- ///
- /// @phpstan-return PromiseInterface<void|null>|null
pub async fn download(&mut self, package: PackageInterfaceHandle) -> Option<PhpMixed> {
let installer = self.get_installer(&package.get_type()).ok()?;
@@ -566,8 +548,6 @@ impl InstallationManager {
}
/// Executes install operation.
- ///
- /// @phpstan-return PromiseInterface<void|null>|null
pub async fn install(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
@@ -583,8 +563,6 @@ impl InstallationManager {
}
/// Executes update operation.
- ///
- /// @phpstan-return PromiseInterface<void|null>|null
pub async fn update(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
@@ -615,8 +593,6 @@ impl InstallationManager {
}
/// Uninstalls package.
- ///
- /// @phpstan-return PromiseInterface<void|null>|null
pub async fn uninstall(
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
@@ -654,8 +630,6 @@ impl InstallationManager {
}
/// Returns the installation path of a package
- ///
- /// @return string|null absolute path to install to, which does not end with a slash, or null if the package does not have anything installed on disk
pub fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String> {
let installer = self.get_installer(&package.get_type()).ok()?;
@@ -803,7 +777,6 @@ impl InstallationManager {
}
}
- /// @phpstan-param array<callable(): ?PromiseInterface<void|null>> $cleanupPromises
async fn run_cleanup(
&mut self,
cleanup_promises: &IndexMap<
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index 6df00c6..ff7708f 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -64,8 +64,4 @@ pub trait InstallerInterface: std::fmt::Debug {
fn as_plugin_installer_mut(&mut self) -> Option<&mut PluginInstaller> {
None
}
-
- fn clone_box(&self) -> Box<dyn InstallerInterface> {
- todo!()
- }
}