aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/installer
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-11 02:39:35 +0900
committernsfisis <nsfisis@gmail.com>2026-06-11 02:39:35 +0900
commit6ae7e0d4b3eaf20e2d2cd3d000cf61ab0c9b6e83 (patch)
treee39261f4aa7314fe8c75142670c76591cdaccb92 /crates/shirabe/src/installer
parent5d3232a80be4b989e89cc7ae4e3642cc5acae030 (diff)
downloadphp-shirabe-6ae7e0d4b3eaf20e2d2cd3d000cf61ab0c9b6e83.tar.gz
php-shirabe-6ae7e0d4b3eaf20e2d2cd3d000cf61ab0c9b6e83.tar.zst
php-shirabe-6ae7e0d4b3eaf20e2d2cd3d000cf61ab0c9b6e83.zip
feat(console): resolve phase-b TODOs in doRun and IO wiring
Wire up ConsoleIO with HelperSet/QuestionHelper, register the ErrorHandler with the IO instance, and fall back to a default output in run(). Replace resolved phase-b TODOs across the console, command, io, factory, installer, dependency_resolver, and util modules; reclassify the remaining blockers (typed Symfony command registry, stdin resource caching) as phase-c. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/installer')
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs31
-rw-r--r--crates/shirabe/src/installer/library_installer.rs3
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs9
3 files changed, 34 insertions, 9 deletions
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 4d5608d..90801ec 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -117,7 +117,14 @@ impl InstallationManager {
for installer in &self.installers {
if installer.supports(&r#type) {
- // TODO(phase-b): cache by cloning Box<dyn InstallerInterface> is non-trivial
+ // 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());
}
@@ -326,7 +333,14 @@ impl InstallationManager {
}
let installer = self.get_installer(&package.get_type())?;
- // TODO(phase-b): closure captures installer + package; needs Rc-shared installer/package
+ // PHP: $cleanupPromises[$index] = function () use ($index, $installer, $type, $package) {
+ // if (null === $package->getInstallationSource()) { return \React\Promise\resolve(null); }
+ // return $installer->cleanup($type, $package); };
+ // TODO(phase-c): the cleanup callable must capture the installer and package and invoke
+ // installer.cleanup(...) returning a React promise. It is a 'static closure stored in
+ // cleanup_promises, so installer/package must be Rc-shared (the installer registry is
+ // not Rc yet, see get_installer) and the promise type must be modelled. Both depend on
+ // the async/React-Promise rework, so a no-op future is stored instead.
let _ = installer;
let op_type_clone = op_type.clone();
let cleanup: Box<
@@ -507,7 +521,12 @@ impl InstallationManager {
.prepare(&op_type, package, initial_package)
.await?;
- // TODO(phase-b): chain the install/update/uninstall step with cleanup_promises[index], repo.write, etc.
+ // PHP: $promise = $promise->then(fn() => $this->{$type}(...))->then($cleanupPromises[$index])
+ // ->then(fn() => $repo->write($devMode, $this->io)); the chained steps run install/
+ // update/uninstall, then cleanup, then persist the repository.
+ // TODO(phase-c): this promise chain (install step -> cleanup_promises[index] ->
+ // repo.write) needs the React\Promise model and the cleanup callables wired (see above);
+ // both stay todo!(), so only prepare() is awaited here.
let _ = cleanup_promises.get(&index);
let event_name_post = match op_type.as_str() {
@@ -518,7 +537,11 @@ impl InstallationManager {
};
if run_scripts && self.event_dispatcher.is_some() {
- // TODO(phase-b): post-exec callback captures &mut dispatcher and operation
+ // PHP appends a post-exec step to the promise chain that dispatches the
+ // POST_PACKAGE_* event via the event dispatcher with repo/all_operations/operation.
+ // TODO(phase-c): the callback captures the event dispatcher (&mut) and the operation
+ // and must outlive the loop body; that requires the dispatcher behind Rc<RefCell>
+ // and the deferred event dispatch to be wired into the promise chain (todo!()).
let _ = event_name_post;
post_exec_callbacks.push(Box::new(|| {
// dispatcher.dispatch_package_event(event_name_post, dev_mode, repo, all_operations, operation);
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index decc1c2..da5533c 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -189,8 +189,7 @@ impl LibraryInstaller {
self.filesystem
.borrow_mut()
.ensure_directory_exists(&self.vendor_dir);
- // TODO(phase-b): realpath returns Option<String>; PHP assigns to vendorDir even when false
- self.vendor_dir = realpath(&self.vendor_dir).unwrap();
+ self.vendor_dir = realpath(&self.vendor_dir).unwrap_or_default();
}
pub(crate) fn get_download_manager(&self) -> &std::rc::Rc<std::cell::RefCell<DownloadManager>> {
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index 23d7098..2644639 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -94,9 +94,12 @@ impl InstallerInterface for PluginInstaller {
.get("plugin-optional")
.map(|v| matches!(v, PhpMixed::Bool(true)))
.unwrap_or(false);
- // TODO(plugin): check if plugin is allowed
- // TODO(phase-b): is_plugin_allowed needs &mut PluginManager but prepare is &self.
- let _ = plugin_optional;
+ self.get_plugin_manager().borrow_mut().is_plugin_allowed(
+ &package.get_name(),
+ false,
+ plugin_optional,
+ true,
+ )?;
}
self.inner.prepare(r#type, package, prev_package).await