diff options
Diffstat (limited to 'crates/shirabe/src/installer')
6 files changed, 175 insertions, 165 deletions
diff --git a/crates/shirabe/src/installer/binary_installer.rs b/crates/shirabe/src/installer/binary_installer.rs index c7b880b..aea0a7c 100644 --- a/crates/shirabe/src/installer/binary_installer.rs +++ b/crates/shirabe/src/installer/binary_installer.rs @@ -252,7 +252,7 @@ impl BinaryInstaller { let bin_path = self .filesystem .borrow_mut() - .find_shortest_path(link, bin, false); + .find_shortest_path(link, bin, false, false); let caller = Self::determine_binary_caller(bin); // if the target is a php file, we run the unixy proxy file @@ -288,7 +288,7 @@ impl BinaryInstaller { let bin_path = self .filesystem .borrow_mut() - .find_shortest_path(link, bin, false); + .find_shortest_path(link, bin, false, false); let bin_dir = ProcessExecutor::escape(&dirname(&bin_path)); let bin_file = basename(&bin_path); @@ -312,7 +312,7 @@ impl BinaryInstaller { let bin_path_exported = self .filesystem .borrow() - .find_shortest_path_code(link, bin, false, true); + .find_shortest_path_code(link, bin, false, true, false); let mut stream_proxy_code = String::new(); let mut stream_hint = String::new(); let mut globals_code = format!("$GLOBALS['_composer_bin_dir'] = __DIR__;\n",); @@ -329,6 +329,7 @@ impl BinaryInstaller { &format!("{}/autoload.php", vendor_dir_real), false, true, + false, ), )); } diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index 3789729..3b342a9 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -42,7 +42,7 @@ pub struct InstallationManager { notifiable_packages: IndexMap<String, Vec<Box<dyn PackageInterface>>>, loop_: std::rc::Rc<std::cell::RefCell<Loop>>, io: Box<dyn IOInterface>, - event_dispatcher: Option<EventDispatcher>, + event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, output_progress: bool, } @@ -50,7 +50,7 @@ impl InstallationManager { pub fn new( loop_: std::rc::Rc<std::cell::RefCell<Loop>>, io: Box<dyn IOInterface>, - event_dispatcher: Option<EventDispatcher>, + event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, ) -> Self { Self { installers: vec![], @@ -85,7 +85,7 @@ impl InstallationManager { let _ = installer; let key: Option<usize> = None; if let Some(k) = key { - array_splice(&mut self.installers, k as i64, Some(1), None); + array_splice(&mut self.installers, k as i64, Some(1), vec![]); self.cache = IndexMap::new(); } } @@ -109,18 +109,18 @@ impl InstallationManager { /// @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<&dyn InstallerInterface> { + 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(&r#type).unwrap().as_ref()); + return Ok(self.cache.get_mut(&r#type).unwrap().as_mut()); } for installer in &self.installers { if installer.supports(&r#type) { // TODO(phase-b): cache by cloning Box<dyn InstallerInterface> is non-trivial self.cache.insert(r#type.clone(), installer.clone_box()); - return Ok(self.cache.get(&r#type).unwrap().as_ref()); + return Ok(self.cache.get_mut(&r#type).unwrap().as_mut()); } } @@ -187,9 +187,9 @@ impl InstallationManager { let signal_handler = SignalHandler::create( vec![ - SignalHandler::SIGINT, - SignalHandler::SIGTERM, - SignalHandler::SIGHUP, + SignalHandler::SIGINT.to_string(), + SignalHandler::SIGTERM.to_string(), + SignalHandler::SIGHUP.to_string(), ], // TODO(phase-b): closure captures &mut self via &mut cleanup_promises Box::new(move |signal: String, handler: &SignalHandler| { @@ -331,7 +331,7 @@ impl InstallationManager { if op_type != "uninstall" { let installer = self.get_installer(package.get_type())?; let promise = installer.download(package, initial_package); - if let Some(p) = promise { + if let Ok(Some(p)) = promise { promises.push(p); } } @@ -447,8 +447,6 @@ impl InstallationManager { initial_package = None; } - let installer = self.get_installer(package.get_type())?; - let event_name = match op_type.as_str() { "install" => PackageEvents::PRE_PACKAGE_INSTALL, "update" => PackageEvents::PRE_PACKAGE_UPDATE, @@ -457,25 +455,26 @@ impl InstallationManager { }; if run_scripts && self.event_dispatcher.is_some() { - self.event_dispatcher - .as_mut() - .unwrap() - .dispatch_package_event( - event_name, - dev_mode, - repo, - all_operations, - operation.as_ref(), - ); + // TODO(phase-b): dispatch_package_event takes Box<dyn RepositoryInterface>/Vec<Box<...>> + // but we hold &mut dyn here. Needs structural rework (likely shared Rc on repo and ops). + let _ = ( + event_name, + dev_mode, + &repo, + &all_operations, + operation.as_ref(), + ); } let _dispatcher = self.event_dispatcher.as_ref(); let _io = self.io.as_ref(); + let installer = self.get_installer(package.get_type())?; let promise = installer.prepare(&op_type, package, initial_package); let promise = match promise { - Some(p) => p, - None => promise::resolve(None), + Ok(Some(p)) => p, + Ok(None) => promise::resolve(None), + Err(e) => return Err(e), }; // TODO(phase-b): chain `.then(cb1).then(cb2)` with cleanup_promises[index], repo.write, etc. @@ -527,7 +526,8 @@ impl InstallationManager { // TODO(phase-b): progress = self.io.get_progress_bar(); progress = Some(()); } - let _ = self.loop_.borrow_mut().wait(promises, progress); + // TODO(phase-b): pass actual ProgressBar when self.io.get_progress_bar() is implemented + let _ = self.loop_.borrow_mut().wait(promises, None); if progress.is_some() { // progress.clear(); // ProgressBar in non-decorated output does not output a final line-break and clear() does nothing @@ -545,7 +545,7 @@ impl InstallationManager { package: &dyn PackageInterface, ) -> Option<Box<dyn PromiseInterface>> { let installer = self.get_installer(package.get_type()).ok()?; - let promise = installer.cleanup("install", package, None); + let promise = installer.cleanup("install", package, None).ok()?; promise } @@ -560,7 +560,7 @@ impl InstallationManager { ) -> Option<Box<dyn PromiseInterface>> { let package = operation.get_package(); let installer = self.get_installer(package.get_type()).ok()?; - let promise = installer.install(repo, package); + let promise = installer.install(repo, package).ok()?; self.mark_for_notification(package); promise @@ -582,14 +582,15 @@ impl InstallationManager { let promise = if initial_type == target_type { let installer = self.get_installer(initial_type).ok()?; - let promise = installer.update(repo, initial, target); + let promise = installer.update(repo, initial, target).ok()?; self.mark_for_notification(target); promise } else { let promise = self .get_installer(initial_type) .ok()? - .uninstall(repo, initial); + .uninstall(repo, initial) + .ok()?; let promise = match promise { Some(p) => p, None => promise::resolve(None), @@ -615,7 +616,7 @@ impl InstallationManager { let package = operation.get_package(); let installer = self.get_installer(package.get_type()).ok()?; - installer.uninstall(repo, package) + installer.uninstall(repo, package).ok()? } /// Executes markAliasInstalled operation. @@ -684,9 +685,13 @@ impl InstallationManager { "Content-type: application/x-www-form-urlencoded".to_string(), ))]), ); + let params_vec: Vec<(&str, &str)> = params + .iter() + .map(|(k, v)| (k.as_str(), v.as_str())) + .collect(); http.insert( "content".to_string(), - PhpMixed::String(http_build_query(¶ms, "", Some("&"))), + PhpMixed::String(http_build_query(¶ms_vec, "", "&")), ); http.insert("timeout".to_string(), PhpMixed::Int(3)); opts.insert( @@ -696,12 +701,13 @@ impl InstallationManager { ), ); - promises.push(self.loop_.borrow().get_http_downloader().borrow_mut().add( - &url, - &PhpMixed::Array( - opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ), - )?); + promises.push( + self.loop_ + .borrow() + .get_http_downloader() + .borrow_mut() + .add(&url, opts)?, + ); } continue; @@ -767,10 +773,13 @@ impl InstallationManager { PhpMixed::Array(http.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), ); - promises.push(self.loop_.borrow().get_http_downloader().borrow_mut().add( - repo_url, - &PhpMixed::Array(opts.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), - )?); + promises.push( + self.loop_ + .borrow() + .get_http_downloader() + .borrow_mut() + .add(repo_url, opts)?, + ); } let _ = self.loop_.borrow_mut().wait(promises, None); diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index 0bc87f9..fc79bdd 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -44,8 +44,9 @@ impl LibraryInstaller { binary_installer: Option<BinaryInstaller>, ) -> Self { // PHP: $this->downloadManager = $composer instanceof Composer ? $composer->getDownloadManager() : null; - let download_manager = - if let Some(full_composer) = composer.as_any().downcast_ref::<Composer>() { + // TODO(phase-b): PartialComposer cannot downcast to Composer in this Rust port. + let download_manager: Option<std::rc::Rc<std::cell::RefCell<DownloadManager>>> = + if let Some(_full_composer) = composer.as_any().downcast_ref::<Composer>() { // TODO(phase-b): clone or borrow the DownloadManager from the full Composer Some(todo!("composer.get_download_manager() as DownloadManager")) } else { @@ -55,8 +56,12 @@ impl LibraryInstaller { let filesystem = filesystem .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None)))); let vendor_dir = rtrim( - // TODO(phase-b): composer.get_config().borrow_mut().get("vendor-dir") returns a PhpMixed/String - &composer.get_config().borrow_mut().get("vendor-dir"), + // TODO(phase-b): Config::get returns PhpMixed; coerce to String via get_str. + &composer + .get_config() + .borrow_mut() + .get_str("vendor-dir") + .unwrap_or_default(), Some("/"), ); let binary_installer = binary_installer.unwrap_or_else(|| { @@ -64,13 +69,22 @@ impl LibraryInstaller { // TODO(phase-b): pass io by reference/clone todo!("io reference"), rtrim( - &composer.get_config().borrow_mut().get("bin-dir"), + &composer + .get_config() + .borrow_mut() + .get_str("bin-dir") + .unwrap_or_default(), Some("/"), ), - composer.get_config().borrow_mut().get("bin-compat"), + // TODO(phase-b): Config::get returns PhpMixed; coerce to String via get_str. + composer + .get_config() + .borrow_mut() + .get_str("bin-compat") + .unwrap_or_default(), // TODO(phase-b): pass filesystem reference todo!("filesystem reference"), - vendor_dir.clone(), + Some(vendor_dir.clone()), ) }); @@ -86,12 +100,10 @@ impl LibraryInstaller { } /// Make sure binaries are installed for a given package. - pub fn ensure_binaries_presence(&self, package: &dyn PackageInterface) { - self.binary_installer.install_binaries( - package, - &self.get_install_path(package).unwrap(), - false, - ); + pub fn ensure_binaries_presence(&mut self, package: &dyn PackageInterface) { + let install_path = self.get_install_path(package).unwrap(); + self.binary_installer + .install_binaries(package, &install_path, false); } /// Returns the base path of the package without target-dir path @@ -104,7 +116,7 @@ impl LibraryInstaller { if let Some(target_dir) = target_dir { if !target_dir.is_empty() { - return Preg::replace( + let replaced = Preg::replace( &format!( "{{/*{}/?$}}", preg_quote(&target_dir, None).replace('/', "/+") @@ -112,6 +124,7 @@ impl LibraryInstaller { "", &install_path, ); + return replaced.unwrap_or(install_path); } } @@ -126,9 +139,11 @@ impl LibraryInstaller { ) -> Result<Option<Box<dyn PromiseInterface>>> { let download_path = self.get_install_path(package).unwrap(); - self.get_download_manager() - .borrow() - .install(package, &download_path) + Ok(Some( + self.get_download_manager() + .borrow() + .install(package, &download_path)?, + )) } /// @return PromiseInterface|null @@ -152,17 +167,13 @@ impl LibraryInstaller { None => shirabe_external_packages::react::promise::resolve(None), }; - return Ok(Some(promise.then(Box::new( - move || -> Result<Box<dyn PromiseInterface>> { - // TODO(phase-b): capture target/self into the closure - let promise = self.install_code(target)?; - if let Some(promise) = promise { - return Ok(promise); - } - - Ok(shirabe_external_packages::react::promise::resolve(None)) - }, - )))); + // TODO(phase-b): promise.then expects Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>> + // arguments. Translating the original PHP closure (which captures &self and target) + // requires restructuring; tracked separately. + let _ = promise; + return Ok(Some(todo!( + "promise.then(...) chain to install_code(target)" + ))); } self.filesystem @@ -170,9 +181,11 @@ impl LibraryInstaller { .rename(&initial_download_path, &target_download_path); } - self.get_download_manager() - .borrow() - .update(initial, target, &target_download_path) + Ok(Some(self.get_download_manager().borrow().update( + initial, + target, + &target_download_path, + )?)) } /// @return PromiseInterface|null @@ -183,9 +196,11 @@ impl LibraryInstaller { ) -> Result<Option<Box<dyn PromiseInterface>>> { let download_path = self.get_package_base_path(package); - self.get_download_manager() - .borrow() - .remove(package, &download_path) + Ok(Some( + self.get_download_manager() + .borrow() + .remove(package, &download_path)?, + )) } pub(crate) fn initialize_vendor_dir(&mut self) { @@ -262,9 +277,11 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - self.get_download_manager() - .borrow() - .download(package, &download_path, prev_package) + Ok(Some(self.get_download_manager().borrow().download( + package, + &download_path, + prev_package, + )?)) } fn prepare( @@ -277,9 +294,12 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - self.get_download_manager() - .borrow() - .prepare(r#type, package, &download_path, prev_package) + Ok(Some(self.get_download_manager().borrow().prepare( + r#type, + package, + &download_path, + prev_package, + )?)) } fn cleanup( @@ -292,9 +312,12 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - self.get_download_manager() - .borrow() - .cleanup(r#type, package, &download_path, prev_package) + Ok(Some(self.get_download_manager().borrow().cleanup( + r#type, + package, + &download_path, + prev_package, + )?)) } fn install( @@ -317,17 +340,13 @@ impl InstallerInterface for LibraryInstaller { None => shirabe_external_packages::react::promise::resolve(None), }; - let binary_installer = &self.binary_installer; - let install_path = self.get_install_path(package).unwrap(); - - // TODO(phase-b): capture binary_installer/install_path/package/repo into the closure - Ok(Some(promise.then(Box::new(move || -> Result<()> { - binary_installer.install_binaries(package, &install_path, true); - if !repo.has_package(package) { - repo.add_package(package.clone_package_box())?; - } - Ok(()) - })))) + // TODO(phase-b): promise.then expects Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>> + // arguments. The original PHP closure captures &mut self/binary_installer/repo/package; + // restructuring required. + let _ = promise; + Ok(Some(todo!( + "promise.then(...) chain to install_binaries + repo.add_package" + ))) } fn update( @@ -354,18 +373,13 @@ impl InstallerInterface for LibraryInstaller { None => shirabe_external_packages::react::promise::resolve(None), }; - let binary_installer = &self.binary_installer; - let install_path = self.get_install_path(target).unwrap(); - - // TODO(phase-b): capture binary_installer/install_path/target/initial/repo into the closure - Ok(Some(promise.then(Box::new(move || -> Result<()> { - binary_installer.install_binaries(target, &install_path, true); - repo.remove_package(initial)?; - if !repo.has_package(target) { - repo.add_package(target.clone_package_box())?; - } - Ok(()) - })))) + // TODO(phase-b): promise.then expects Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>> + // arguments. Closure captures &mut self/binary_installer/repo/initial/target; + // restructuring required. + let _ = promise; + Ok(Some(todo!( + "promise.then(...) chain to install_binaries + repo updates" + ))) } fn uninstall( @@ -387,28 +401,13 @@ impl InstallerInterface for LibraryInstaller { None => shirabe_external_packages::react::promise::resolve(None), }; - let binary_installer = &self.binary_installer; - let download_path = self.get_package_base_path(package); - let filesystem = &self.filesystem; - - // TODO(phase-b): capture binary_installer/filesystem/download_path/package/repo into the closure - Ok(Some(promise.then(Box::new(move || -> Result<()> { - binary_installer.remove_binaries(package); - repo.remove_package(package)?; - - if strpos(package.get_name(), "/").is_some() { - let package_vendor_dir = shirabe_php_shim::dirname(&download_path); - if shirabe_php_shim::is_dir(&package_vendor_dir) - && filesystem.borrow().is_dir_empty(&package_vendor_dir) - { - Silencer::call(|| { - rmdir(&package_vendor_dir); - Ok(()) - })?; - } - } - Ok(()) - })))) + // TODO(phase-b): promise.then expects Option<Box<dyn FnOnce(Option<PhpMixed>) -> Option<PhpMixed>>> + // arguments. Closure captures binary_installer/filesystem/download_path/package/repo; + // restructuring required. + let _ = promise; + Ok(Some(todo!( + "promise.then(...) chain to remove_binaries/remove_package/rmdir" + ))) } fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String> { @@ -439,7 +438,10 @@ impl InstallerInterface for LibraryInstaller { } impl BinaryPresenceInterface for LibraryInstaller { - fn ensure_binaries_presence(&self, package: &dyn PackageInterface) { - LibraryInstaller::ensure_binaries_presence(self, package) + fn ensure_binaries_presence(&self, _package: &dyn PackageInterface) { + // TODO(phase-b): trait takes &self but LibraryInstaller::ensure_binaries_presence + // requires &mut self due to BinaryInstaller::install_binaries(&mut self, ...). + // Revisit the trait or use interior mutability. + todo!() } } diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs index bb04947..2ec8cb9 100644 --- a/crates/shirabe/src/installer/plugin_installer.rs +++ b/crates/shirabe/src/installer/plugin_installer.rs @@ -57,19 +57,8 @@ impl PluginInstaller { } fn get_plugin_manager(&self) -> &PluginManager { - // TODO(plugin): assert self.inner.composer is fully loaded Composer instance - assert!( - self.inner.composer.is_full_composer(), - "{}", - LogicException { - message: - "PluginInstaller should be initialized with a fully loaded Composer instance." - .to_string(), - code: 0, - } - ); - // TODO(plugin): return plugin manager from composer - self.inner.composer.get_plugin_manager() + // TODO(plugin): PartialComposer does not expose PluginManager; revisit when wiring plugin support + todo!("PartialComposer.get_plugin_manager") } fn get_plugin_manager_mut(&mut self) -> &mut PluginManager { @@ -106,8 +95,8 @@ impl InstallerInterface for PluginInstaller { .map(|v| matches!(v, PhpMixed::Bool(true))) .unwrap_or(false); // TODO(plugin): check if plugin is allowed - self.get_plugin_manager() - .is_plugin_allowed(package.get_name(), false, plugin_optional); + // TODO(phase-b): is_plugin_allowed needs &mut PluginManager but prepare is &self. + let _ = plugin_optional; } self.inner.prepare(r#type, package, prev_package) @@ -145,12 +134,15 @@ impl InstallerInterface for PluginInstaller { }; // TODO(plugin): register package in plugin manager after install, rollback on failure - Ok(Some(promise.then(Box::new(move || -> Result<()> { - Platform::workaround_filesystem_issues(); - // self.get_plugin_manager().register_package(package, true)?; - // On error: self.rollback_install(e, repo, package)?; - Ok(()) - })))) + Ok(Some(promise.then( + Some(Box::new(move |_v| -> Option<PhpMixed> { + Platform::workaround_filesystem_issues(); + // self.get_plugin_manager().register_package(package, true)?; + // On error: self.rollback_install(e, repo, package)?; + None + })), + None, + ))) } fn update( @@ -166,13 +158,16 @@ impl InstallerInterface for PluginInstaller { }; // TODO(plugin): deactivate initial and register target in plugin manager after update, rollback on failure - Ok(Some(promise.then(Box::new(move || -> Result<()> { - Platform::workaround_filesystem_issues(); - // self.get_plugin_manager().deactivate_package(initial); - // self.get_plugin_manager().register_package(target, true)?; - // On error: self.rollback_install(e, repo, target)?; - Ok(()) - })))) + Ok(Some(promise.then( + Some(Box::new(move |_v| -> Option<PhpMixed> { + Platform::workaround_filesystem_issues(); + // self.get_plugin_manager().deactivate_package(initial); + // self.get_plugin_manager().register_package(target, true)?; + // On error: self.rollback_install(e, repo, target)?; + None + })), + None, + ))) } fn uninstall( diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs index 906e2c0..6f794af 100644 --- a/crates/shirabe/src/installer/project_installer.rs +++ b/crates/shirabe/src/installer/project_installer.rs @@ -97,9 +97,11 @@ impl InstallerInterface for ProjectInstaller { _repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface, ) -> anyhow::Result<Option<Box<dyn PromiseInterface>>> { - self.download_manager - .borrow() - .install(package, &self.install_path) + Ok(Some( + self.download_manager + .borrow() + .install(package, &self.install_path)?, + )) } fn update( diff --git a/crates/shirabe/src/installer/suggested_packages_reporter.rs b/crates/shirabe/src/installer/suggested_packages_reporter.rs index c820009..8248f5f 100644 --- a/crates/shirabe/src/installer/suggested_packages_reporter.rs +++ b/crates/shirabe/src/installer/suggested_packages_reporter.rs @@ -196,5 +196,6 @@ impl SuggestedPackagesReporter { fn remove_control_characters(&self, string: &str) -> String { Preg::replace("/[[:cntrl:]]/", "", &string.replace('\n', " ")) + .unwrap_or_else(|_| string.replace('\n', " ")) } } |
