diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-23 03:07:15 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-23 15:48:00 +0900 |
| commit | e068a9d644fde6659a88accd55b3f1d0d9d7cf46 (patch) | |
| tree | bb719a70eb8c840957a94a5601df8961055ceb0f /crates/shirabe | |
| parent | 60eb89529c8af2e4477e0bb65ed9e0f2dc7d3dd7 (diff) | |
| download | php-shirabe-e068a9d644fde6659a88accd55b3f1d0d9d7cf46.tar.gz php-shirabe-e068a9d644fde6659a88accd55b3f1d0d9d7cf46.tar.zst php-shirabe-e068a9d644fde6659a88accd55b3f1d0d9d7cf46.zip | |
refactor(promise): rewrite promise bodies to async/await
Mechanically convert promise-returning function bodies to async/await:
resolve() returns the value directly, forwarding calls get .await, and
simple .then chains become await sequences. Also collapse the installer
double-Option (Result<Option<Option<PhpMixed>>> -> Result<Option<PhpMixed>>).
Hard spots that depend on the Loop::wait / job-machine boundary
(accept/reject orchestration, closures capturing &mut self, batch waits)
are left intact and marked with TODO(phase-c-promise) for manual porting.
The crate does not compile yet; traits still need #[async_trait].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe')
26 files changed, 382 insertions, 377 deletions
diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs index 4842b05..ac898d5 100644 --- a/crates/shirabe/src/downloader/archive_downloader.rs +++ b/crates/shirabe/src/downloader/archive_downloader.rs @@ -36,6 +36,7 @@ pub trait ArchiveDownloader { self.cleanup_executed_mut().remove(package.get_name()); self.inner_mut() .prepare(r#type, package, path, prev_package) + .await } async fn cleanup( @@ -49,6 +50,7 @@ pub trait ArchiveDownloader { .insert(package.get_name().to_string(), true); self.inner_mut() .cleanup(r#type, package, path, prev_package) + .await } /// @inheritDoc @@ -122,6 +124,7 @@ pub trait ArchiveDownloader { let _ = file_name; + // TODO(phase-c-promise): rewrite extract().then(onFulfilled/onRejected) + renameRecursively chain as an await sequence let promise = self.extract(package, "", &temporary_dir)?; // TODO(phase-b): the original PHP chains React promise `.then(onFulfilled, onRejected)` diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs index 3100e29..f5201cc 100644 --- a/crates/shirabe/src/downloader/download_manager.rs +++ b/crates/shirabe/src/downloader/download_manager.rs @@ -214,7 +214,7 @@ impl DownloadManager { let source = match array_shift(&mut sources) { Some(s) => s, None => { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } }; if retry_state { @@ -233,12 +233,15 @@ impl DownloadManager { let downloader = match self.get_downloader_for_package(package)? { Some(d) => d, None => { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } }; // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - let result = match downloader.download3(package, &target_dir, prev_package) { + let result = match downloader + .download3(package, &target_dir, prev_package) + .await + { Ok(r) => r, Err(e) => { // PHP closure handleError: rethrow if not RuntimeException or if IrrecoverableDownloadException @@ -271,15 +274,8 @@ impl DownloadManager { }; // PHP: $result->then(static fn ($res) => $res, $handleError); - // TODO(phase-b): chain $handleError as the rejection handler on the promise - let res = result.then( - Some(Box::new(move |res: Option<PhpMixed>| -> Option<PhpMixed> { - res - })), - None, - ); - - return Ok(res); + // The rejection handler ($handleError) is already applied via the try/catch (the Err arm above). + return Ok(result); } } @@ -299,10 +295,12 @@ impl DownloadManager { ) -> Result<Option<PhpMixed>> { let target_dir = self.normalize_target_dir(target_dir); if let Some(downloader) = self.get_downloader_for_package(package)? { - return downloader.prepare(r#type, package, &target_dir, prev_package); + return downloader + .prepare(r#type, package, &target_dir, prev_package) + .await; } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } /// Installs package into target dir. @@ -320,10 +318,10 @@ impl DownloadManager { ) -> Result<Option<PhpMixed>> { let target_dir = self.normalize_target_dir(target_dir); if let Some(downloader) = self.get_downloader_for_package(package)? { - return downloader.install2(package, &target_dir); + return downloader.install2(package, &target_dir).await; } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } /// Updates package from initial to target version. @@ -346,19 +344,26 @@ impl DownloadManager { // no downloaders present means update from metapackage to metapackage, nothing to do if initial_downloader.is_none() && downloader.is_none() { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } // if we have a downloader present before, but not after, the package became a metapackage and its files should be removed if downloader.is_none() { - return initial_downloader.unwrap().remove2(initial, &target_dir); + return initial_downloader + .unwrap() + .remove2(initial, &target_dir) + .await; } let initial_type = self.get_downloader_type(initial_downloader.unwrap()); let target_type = self.get_downloader_type(downloader.unwrap()); if initial_type == target_type { // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - match downloader.unwrap().update(initial, target, &target_dir) { + match downloader + .unwrap() + .update(initial, target, &target_dir) + .await + { Ok(p) => return Ok(p), Err(e) => { // TODO(phase-b): downcast to RuntimeException @@ -383,18 +388,12 @@ impl DownloadManager { // if downloader type changed, or update failed and user asks for reinstall, // we wipe the dir and do a new install instead of updating it - let promise = initial_downloader.unwrap().remove2(initial, &target_dir)?; - - let target_dir_owned = target_dir.clone(); - // TODO(phase-b): capture self and target into the closure; type mismatch with then signature. - let _ = target_dir_owned; - Ok(promise.then( - Some(Box::new(move |res: Option<PhpMixed>| -> Option<PhpMixed> { - let _ = res; - todo!("self.install(target, &target_dir_owned)") - })), - None, - )) + // PHP: return $promise->then(fn () => $this->install($target, $targetDir)); + let _ = initial_downloader + .unwrap() + .remove2(initial, &target_dir) + .await?; + self.install(target, &target_dir).await } /// Removes package from target dir. @@ -409,10 +408,10 @@ impl DownloadManager { ) -> Result<Option<PhpMixed>> { let target_dir = self.normalize_target_dir(target_dir); if let Some(downloader) = self.get_downloader_for_package(package)? { - return downloader.remove2(package, &target_dir); + return downloader.remove2(package, &target_dir).await; } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } /// Cleans up a failed operation @@ -431,10 +430,12 @@ impl DownloadManager { ) -> Result<Option<PhpMixed>> { let target_dir = self.normalize_target_dir(target_dir); if let Some(downloader) = self.get_downloader_for_package(package)? { - return downloader.cleanup(r#type, package, &target_dir, prev_package); + return downloader + .cleanup(r#type, package, &target_dir, prev_package) + .await; } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } /// Determines the install preference of a package diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs index 0920342..452ce8c 100644 --- a/crates/shirabe/src/downloader/file_downloader.rs +++ b/crates/shirabe/src/downloader/file_downloader.rs @@ -199,6 +199,7 @@ impl DownloaderInterface for FileDownloader { .borrow_mut() .ensure_directory_exists(&dir_of_file)?; + // TODO(phase-c-promise): rewrite the accept/reject/retry promise orchestration as an async loop. // TODO(plugin): inline closures rely on captured $accept/$reject/$urls/$retries. In Rust // we'd need a struct holding shared state — left as a phase-b refactor. let _ = (output, &urls, &mut retries, cache_key_generator, &file_name); @@ -218,7 +219,7 @@ impl DownloaderInterface for FileDownloader { _path: &str, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(react_promise_resolve(Some(PhpMixed::Null))) + Ok(Some(PhpMixed::Null)) } /// @inheritDoc @@ -273,7 +274,7 @@ impl DownloaderInterface for FileDownloader { } } - Ok(react_promise_resolve(Some(PhpMixed::Null))) + Ok(Some(PhpMixed::Null)) } /// @inheritDoc @@ -333,7 +334,7 @@ impl DownloaderInterface for FileDownloader { } } - Ok(react_promise_resolve(Some(PhpMixed::Null))) + Ok(Some(PhpMixed::Null)) } /// @inheritDoc @@ -349,10 +350,9 @@ impl DownloaderInterface for FileDownloader { self.get_install_operation_appendix(target, path) )); - let _promise = self.remove(initial, path, false)?; - // TODO(phase-b): chain `.then(|| self.install(target, path, false))` - let _ = (initial, target, path); - todo!("phase-b: chain promise.then(|| self.install(target, path, false))") + // PHP: return $this->remove($initial, $path, false)->then(fn () => $this->install($target, $path, false)); + let _ = self.remove(initial, path, false).await?; + self.install(target, path, false).await } /// @inheritDoc @@ -368,11 +368,20 @@ impl DownloaderInterface for FileDownloader { UninstallOperation::format(package, false) )); } - let _promise = self.filesystem.borrow_mut().remove_directory_async(path)?; + let result = self + .filesystem + .borrow_mut() + .remove_directory_async(path) + .await?; + if !result { + return Err(RuntimeException { + message: format!("Could not completely delete {}, aborting.", path), + code: 0, + } + .into()); + } - // TODO(phase-b): chain `.then(|result| if !result { throw RuntimeException })` - let _ = path; - todo!("phase-b: chain promise.then(|result| {{ if !result {{ throw RuntimeException }} }})") + Ok(None) } } @@ -384,6 +393,8 @@ impl ChangeReportInterface for FileDownloader { package: &dyn PackageInterface, path: &str, ) -> Result<Option<String>> { + // TODO(phase-c-promise): get_local_changes drives promises via http_downloader/process wait(); + // converting requires deciding whether ChangeReportInterface::get_local_changes becomes async. Left as-is. // TODO(phase-b): swap self.io to NullIO and restore — needs a take/swap helper let mut null_io = NullIO::new(); diff --git a/crates/shirabe/src/downloader/fossil_downloader.rs b/crates/shirabe/src/downloader/fossil_downloader.rs index fc3d0e8..25f3a31 100644 --- a/crates/shirabe/src/downloader/fossil_downloader.rs +++ b/crates/shirabe/src/downloader/fossil_downloader.rs @@ -35,7 +35,7 @@ impl FossilDownloader { _url: String, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub(crate) async fn do_install( @@ -95,7 +95,7 @@ impl FossilDownloader { &mut output, )?; - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub(crate) async fn do_update( @@ -147,7 +147,7 @@ impl FossilDownloader { &mut output, )?; - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub fn get_local_changes( diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs index e6bc16b..e43d31b 100644 --- a/crates/shirabe/src/downloader/git_downloader.rs +++ b/crates/shirabe/src/downloader/git_downloader.rs @@ -66,7 +66,7 @@ impl GitDownloader { ) -> Result<Option<PhpMixed>> { // Do not create an extra local cache when repository is already local if Filesystem::is_local_path(url) { - return Ok(promise::resolve(None)); + return Ok(None); } GitUtil::clean_env(&self.inner.process); @@ -129,7 +129,7 @@ impl GitDownloader { .into()); } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) async fn do_install( @@ -282,7 +282,7 @@ impl GitDownloader { let _ = new_ref; } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) async fn do_update( @@ -448,7 +448,7 @@ impl GitDownloader { self.update_origin_url(&path, target.get_source_url().unwrap()); } - Ok(promise::resolve(None)) + Ok(None) } pub fn get_local_changes(&self, _package: &dyn PackageInterface, path: &str) -> Option<String> { @@ -717,23 +717,23 @@ impl GitDownloader { let changes = match self.get_local_changes(package, &path) { Some(c) => c, - None => return Ok(promise::resolve(None)), + None => return Ok(None), }; if !self.inner.io.is_interactive() { let discard_changes = self.inner.config.borrow_mut().get("discard-changes"); if discard_changes.as_bool() == Some(true) { - return self.discard_changes(&path); + return self.discard_changes(&path).await; } if discard_changes.as_string() == Some("stash") { if !update { - return self.inner.clean_changes(package, &path, update); + return self.inner.clean_changes(package, &path, update).await; } - return self.stash_changes(&path); + return self.stash_changes(&path).await; } - return self.inner.clean_changes(package, &path, update); + return self.inner.clean_changes(package, &path, update).await; } let changes: Vec<String> = array_map( @@ -780,7 +780,7 @@ impl GitDownloader { let mut do_help = false; match answer.as_deref() { Some("y") => { - self.discard_changes(&path)?; + self.discard_changes(&path).await?; break 'outer; } Some("s") => { @@ -788,7 +788,7 @@ impl GitDownloader { // goto help; do_help = true; } else { - self.stash_changes(&path)?; + self.stash_changes(&path).await?; break 'outer; } } @@ -847,7 +847,7 @@ impl GitDownloader { } } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) fn reapply_changes(&mut self, path: &str) -> Result<()> { @@ -1254,7 +1254,7 @@ impl GitDownloader { self.has_discarded_changes.insert(path, true); - Ok(promise::resolve(None)) + Ok(None) } /// @phpstan-return PromiseInterface<void|null> @@ -1281,7 +1281,7 @@ impl GitDownloader { self.has_stashed_changes.insert(path, true); - Ok(promise::resolve(None)) + Ok(None) } /// @throws \RuntimeException diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs index a3fbb49..d852a00 100644 --- a/crates/shirabe/src/downloader/gzip_downloader.rs +++ b/crates/shirabe/src/downloader/gzip_downloader.rs @@ -90,12 +90,12 @@ impl GzipDownloader { (), )? == 0 { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } if extension_loaded("zlib") { self.extract_using_ext(file, &target_filepath); - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } let process_error = format!( @@ -111,7 +111,7 @@ impl GzipDownloader { self.extract_using_ext(file, &target_filepath); - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } fn extract_using_ext(&self, file: &str, target_filepath: &str) { @@ -141,7 +141,9 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -151,7 +153,9 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -160,7 +164,7 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -169,7 +173,7 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -178,7 +182,7 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -188,6 +192,8 @@ impl crate::downloader::DownloaderInterface for GzipDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/hg_downloader.rs b/crates/shirabe/src/downloader/hg_downloader.rs index 16cf73c..56f3b6f 100644 --- a/crates/shirabe/src/downloader/hg_downloader.rs +++ b/crates/shirabe/src/downloader/hg_downloader.rs @@ -43,7 +43,7 @@ impl HgDownloader { .into()); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub(crate) async fn do_install( @@ -97,7 +97,7 @@ impl HgDownloader { .into()); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub(crate) async fn do_update( @@ -148,7 +148,7 @@ impl HgDownloader { }; hg_utils.run_command(up_command, url, Some(path)); - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub fn get_local_changes( diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs index 3da4915..f677a50 100644 --- a/crates/shirabe/src/downloader/path_downloader.rs +++ b/crates/shirabe/src/downloader/path_downloader.rs @@ -93,7 +93,7 @@ impl PathDownloader { let real_url = real_url.unwrap(); if realpath(&path).as_deref() == Some(&real_url) { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } if format!( @@ -119,7 +119,7 @@ impl PathDownloader { .into()); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub async fn install( @@ -155,7 +155,7 @@ impl PathDownloader { ); } - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } // Get the transport options with default values @@ -290,7 +290,7 @@ impl PathDownloader { self.inner.io.write_error3("", true, io_interface::NORMAL); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub async fn remove( @@ -339,7 +339,7 @@ impl PathDownloader { .into()); } - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } let url = package.get_dist_url().ok_or_else(|| RuntimeException { @@ -378,10 +378,10 @@ impl PathDownloader { ); } - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } - self.inner.remove(package, &path, output) + self.inner.remove(package, &path, output).await } pub fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> { @@ -549,7 +549,9 @@ impl DownloaderInterface for PathDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -559,7 +561,9 @@ impl DownloaderInterface for PathDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -568,7 +572,7 @@ impl DownloaderInterface for PathDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -577,7 +581,7 @@ impl DownloaderInterface for PathDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -586,7 +590,7 @@ impl DownloaderInterface for PathDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -596,6 +600,8 @@ impl DownloaderInterface for PathDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/perforce_downloader.rs b/crates/shirabe/src/downloader/perforce_downloader.rs index ae433ef..fdb0f0d 100644 --- a/crates/shirabe/src/downloader/perforce_downloader.rs +++ b/crates/shirabe/src/downloader/perforce_downloader.rs @@ -40,7 +40,7 @@ impl PerforceDownloader { _url: String, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } pub async fn do_install( @@ -70,7 +70,7 @@ impl PerforceDownloader { .sync_code_base(label.as_deref()); self.perforce.as_mut().unwrap().cleanup_client_spec(); - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } fn get_label_from_source_reference(&self, source_ref: String) -> Option<String> { @@ -118,7 +118,7 @@ impl PerforceDownloader { path: String, url: String, ) -> Result<Option<PhpMixed>> { - self.do_install(target, path, url) + self.do_install(target, path, url).await } pub fn get_local_changes( diff --git a/crates/shirabe/src/downloader/phar_downloader.rs b/crates/shirabe/src/downloader/phar_downloader.rs index f3fffb9..9cfb18f 100644 --- a/crates/shirabe/src/downloader/phar_downloader.rs +++ b/crates/shirabe/src/downloader/phar_downloader.rs @@ -59,7 +59,7 @@ impl PharDownloader { // https://github.com/koto/phar-util // http://blog.kotowicz.net/2010/08/hardening-php-how-to-securely-include.html - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } } @@ -75,7 +75,9 @@ impl DownloaderInterface for PharDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -85,7 +87,9 @@ impl DownloaderInterface for PharDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -94,7 +98,7 @@ impl DownloaderInterface for PharDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -103,7 +107,7 @@ impl DownloaderInterface for PharDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -112,7 +116,7 @@ impl DownloaderInterface for PharDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -122,6 +126,8 @@ impl DownloaderInterface for PharDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs index 71efc29..c6031d9 100644 --- a/crates/shirabe/src/downloader/rar_downloader.rs +++ b/crates/shirabe/src/downloader/rar_downloader.rs @@ -77,7 +77,7 @@ impl RarDownloader { (), )? == 0 { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } process_error = Some(format!( @@ -139,7 +139,7 @@ impl RarDownloader { rar_archive.close(); - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } } @@ -155,7 +155,9 @@ impl crate::downloader::DownloaderInterface for RarDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -165,7 +167,9 @@ impl crate::downloader::DownloaderInterface for RarDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -174,7 +178,7 @@ impl crate::downloader::DownloaderInterface for RarDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -183,7 +187,7 @@ impl crate::downloader::DownloaderInterface for RarDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -192,7 +196,7 @@ impl crate::downloader::DownloaderInterface for RarDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -202,6 +206,8 @@ impl crate::downloader::DownloaderInterface for RarDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs index 65e3e37..f3d0fef 100644 --- a/crates/shirabe/src/downloader/svn_downloader.rs +++ b/crates/shirabe/src/downloader/svn_downloader.rs @@ -57,7 +57,7 @@ impl SvnDownloader { .into()); } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) async fn do_install( @@ -101,7 +101,7 @@ impl SvnDownloader { Some(path), )?; - Ok(promise::resolve(None)) + Ok(None) } pub(crate) async fn do_update( @@ -152,7 +152,7 @@ impl SvnDownloader { None, )?; - Ok(promise::resolve(None)) + Ok(None) } pub fn get_local_changes(&self, package: &dyn PackageInterface, path: &str) -> Option<String> { @@ -210,7 +210,7 @@ impl SvnDownloader { ) -> anyhow::Result<Option<PhpMixed>> { let changes = self.get_local_changes(package, path); if changes.is_none() { - return Ok(promise::resolve(None)); + return Ok(None); } if !self.inner.io.is_interactive() { @@ -222,10 +222,10 @@ impl SvnDownloader { .as_bool() == Some(true) { - return self.discard_changes(path); + return self.discard_changes(path).await; } - return self.inner.clean_changes(package, path, update); + return self.inner.clean_changes(package, path, update).await; } let changes_str = changes.unwrap(); @@ -273,7 +273,7 @@ impl SvnDownloader { .as_string() { Some("y") => { - self.discard_changes(path)?; + self.discard_changes(path).await?; break; } Some("n") => { @@ -310,7 +310,7 @@ impl SvnDownloader { } } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) fn get_commit_logs( @@ -421,7 +421,7 @@ impl SvnDownloader { .into()); } - Ok(promise::resolve(None)) + Ok(None) } pub(crate) fn has_metadata_repository(&self, path: &str) -> bool { diff --git a/crates/shirabe/src/downloader/tar_downloader.rs b/crates/shirabe/src/downloader/tar_downloader.rs index b65d0e3..c720ccf 100644 --- a/crates/shirabe/src/downloader/tar_downloader.rs +++ b/crates/shirabe/src/downloader/tar_downloader.rs @@ -54,7 +54,7 @@ impl TarDownloader { let archive = PharData::new(file.to_string()); archive.extract_to(path, None, true); - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } } @@ -70,7 +70,9 @@ impl DownloaderInterface for TarDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -80,7 +82,9 @@ impl DownloaderInterface for TarDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -89,7 +93,7 @@ impl DownloaderInterface for TarDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -98,7 +102,7 @@ impl DownloaderInterface for TarDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -107,7 +111,7 @@ impl DownloaderInterface for TarDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -117,6 +121,8 @@ impl DownloaderInterface for TarDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs index 1219ab1..096e93d 100644 --- a/crates/shirabe/src/downloader/vcs_downloader.rs +++ b/crates/shirabe/src/downloader/vcs_downloader.rs @@ -65,7 +65,7 @@ impl VcsDownloaderBase { ) -> Result<Option<PhpMixed>> { // TODO(phase-b): parent::cleanChanges() rechecks getLocalChanges via dynamic dispatch. // Callers in subclasses must do that check themselves (they already have). - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } } @@ -142,7 +142,7 @@ pub trait VcsDownloader: while let Some(url) = array_shift(&mut urls) { // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch let attempt: Result<Option<PhpMixed>> = - self.do_download(package, path, &url, prev_package); + self.do_download(package, path, &url, prev_package).await; match attempt { Ok(promise) => return Ok(promise), Err(e) => { @@ -182,7 +182,7 @@ pub trait VcsDownloader: } } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } async fn prepare( @@ -193,7 +193,8 @@ pub trait VcsDownloader: prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { if r#type == "update" { - self.clean_changes(prev_package.unwrap(), path, true)?; + self.clean_changes(prev_package.unwrap(), path, true) + .await?; self.has_cleaned_changes_mut() .insert(prev_package.unwrap().get_unique_name(), true); } else if r#type == "install" { @@ -201,10 +202,10 @@ pub trait VcsDownloader: .borrow_mut() .empty_directory(path, true)?; } else if r#type == "uninstall" { - self.clean_changes(package, path, false)?; + self.clean_changes(package, path, false).await?; } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } async fn cleanup( @@ -227,7 +228,7 @@ pub trait VcsDownloader: .shift_remove(&prev_package.unwrap().get_unique_name()); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } async fn install( @@ -255,7 +256,7 @@ pub trait VcsDownloader: let mut urls = self.prepare_urls(package.get_source_urls()); while let Some(url) = array_shift(&mut urls) { // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - let attempt: Result<Option<PhpMixed>> = self.do_install(package, path, &url); + let attempt: Result<Option<PhpMixed>> = self.do_install(package, path, &url).await; match attempt { Ok(_) => break, Err(e) => { @@ -295,7 +296,7 @@ pub trait VcsDownloader: } } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } async fn update( @@ -326,7 +327,8 @@ pub trait VcsDownloader: let mut exception: Option<anyhow::Error> = None; while let Some(url) = array_shift(&mut urls) { // TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch - let attempt: Result<Option<PhpMixed>> = self.do_update(initial, target, path, &url); + let attempt: Result<Option<PhpMixed>> = + self.do_update(initial, target, path, &url).await; match attempt { Ok(_) => { exception = None; @@ -404,7 +406,7 @@ pub trait VcsDownloader: } } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } async fn remove( @@ -418,28 +420,20 @@ pub trait VcsDownloader: io_interface::NORMAL, ); - let promise = self + let result = self .filesystem_mut() .borrow_mut() - .remove_directory_async(path)?; + .remove_directory_async(path) + .await?; + if !result { + return Err(RuntimeException { + message: format!("Could not completely delete {}, aborting.", path), + code: 0, + } + .into()); + } - let path = path.to_string(); - // TODO(phase-b): closure return type mismatches PromiseInterface::then signature. - Ok(promise.then( - Some(Box::new( - move |result: Option<PhpMixed>| -> Option<PhpMixed> { - let result_bool = result.as_ref().and_then(|v| v.as_bool()).unwrap_or(false); - if !result_bool { - let _: RuntimeException = RuntimeException { - message: format!("Could not completely delete {}, aborting.", path), - code: 0, - }; - } - None - }, - )), - None, - )) + Ok(None) } fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> { @@ -480,7 +474,7 @@ pub trait VcsDownloader: .into()); } - Ok(shirabe_external_packages::react::promise::resolve(None)) + Ok(None) } /// Reapply previously stashed changes if applicable, only called after an update (regardless if successful or not) diff --git a/crates/shirabe/src/downloader/xz_downloader.rs b/crates/shirabe/src/downloader/xz_downloader.rs index 6cc960e..d15287d 100644 --- a/crates/shirabe/src/downloader/xz_downloader.rs +++ b/crates/shirabe/src/downloader/xz_downloader.rs @@ -64,7 +64,7 @@ impl XzDownloader { (), )? == 0 { - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } let process_error = format!( @@ -89,7 +89,9 @@ impl crate::downloader::DownloaderInterface for XzDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -99,7 +101,9 @@ impl crate::downloader::DownloaderInterface for XzDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -108,7 +112,7 @@ impl crate::downloader::DownloaderInterface for XzDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -117,7 +121,7 @@ impl crate::downloader::DownloaderInterface for XzDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -126,7 +130,7 @@ impl crate::downloader::DownloaderInterface for XzDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -136,6 +140,8 @@ impl crate::downloader::DownloaderInterface for XzDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs index ef18f95..88b3d3a 100644 --- a/crates/shirabe/src/downloader/zip_downloader.rs +++ b/crates/shirabe/src/downloader/zip_downloader.rs @@ -192,7 +192,9 @@ impl ZipDownloader { } } - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn extract_with_system_unzip( @@ -211,7 +213,7 @@ impl ZipDownloader { .as_ref() .map_or(true, |v| v.is_empty()); if unzip_commands_empty { - return self.extract_with_zip_archive(package, file, path); + return self.extract_with_zip_archive(package, file, path).await; } let command_spec = UNZIP_COMMANDS.lock().unwrap().as_ref().unwrap()[0].clone(); @@ -268,6 +270,8 @@ impl ZipDownloader { } } + // TODO(phase-c-promise): execute_async + .then fallback closure captures &mut self/io; + // recursive promise flattening, not a mechanical await chain. // TODO(phase-b): full try_fallback closure deferred — PHP captures `$io`, `$self` // and several locals by reference, conflicting with Rust's borrow checker because // `extract_with_zip_archive` later needs `&mut self`. Restructure once the @@ -350,7 +354,7 @@ impl ZipDownloader { if extract_result { zip_archive.close(); - return Ok(shirabe_external_packages::react::promise::resolve(None)); + return Ok(None); } return Err(RuntimeException { @@ -392,7 +396,7 @@ impl ZipDownloader { file: &str, path: &str, ) -> Result<Option<PhpMixed>> { - self.extract_with_system_unzip(package, file, path) + self.extract_with_system_unzip(package, file, path).await } pub fn get_error_message(&self, retval: i64, file: &str) -> String { @@ -433,7 +437,9 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { prev_package: Option<&dyn PackageInterface>, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.download(package, path, prev_package, output) + self.inner + .download(package, path, prev_package, output) + .await } async fn prepare( @@ -443,7 +449,9 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.prepare(r#type, package, path, prev_package) + self.inner + .prepare(r#type, package, path, prev_package) + .await } async fn install( @@ -452,7 +460,7 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.install(package, path, output) + self.inner.install(package, path, output).await } async fn update( @@ -461,7 +469,7 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { target: &dyn PackageInterface, path: &str, ) -> Result<Option<PhpMixed>> { - self.inner.update(initial, target, path) + self.inner.update(initial, target, path).await } async fn remove( @@ -470,7 +478,7 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { path: &str, output: bool, ) -> Result<Option<PhpMixed>> { - self.inner.remove(package, path, output) + self.inner.remove(package, path, output).await } async fn cleanup( @@ -480,6 +488,8 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { path: &str, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, path, prev_package) + self.inner + .cleanup(r#type, package, path, prev_package) + .await } } diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index 114f1fc..ae0470a 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -285,6 +285,8 @@ impl InstallationManager { download_only: bool, all_operations: Vec<Box<dyn OperationInterface>>, ) -> Result<()> { + // TODO(phase-c-promise): Loop::wait-driven batch orchestration (download/cleanup promises); + // rewrite to await collected futures once the async Loop boundary lands. let mut promises: Vec<Box<dyn PromiseInterface>> = vec![]; for (index, operation) in &operations { @@ -401,6 +403,8 @@ impl InstallationManager { run_scripts: bool, all_operations: &[Box<dyn OperationInterface>], ) -> Result<()> { + // TODO(phase-c-promise): Loop::wait-driven batch orchestration (prepare/install promises); + // rewrite to await collected futures once the async Loop boundary lands. let mut promises: Vec<Box<dyn PromiseInterface>> = vec![]; let mut post_exec_callbacks: Vec<Box<dyn Fn()>> = vec![]; @@ -513,6 +517,7 @@ impl InstallationManager { /// @param array<PromiseInterface<void|null>> $promises fn wait_on_promises(&mut self, promises: Vec<Box<dyn PromiseInterface>>) { + // TODO(phase-c-promise): thin wrapper over Loop::wait (the async boundary owned separately). let mut progress: Option<()> = None; // TODO(phase-b): self.io instanceof ConsoleIO downcast let io_is_console = false; @@ -541,7 +546,7 @@ impl InstallationManager { /// @phpstan-return PromiseInterface<void|null>|null pub async fn download(&mut self, package: &dyn PackageInterface) -> Option<PhpMixed> { let installer = self.get_installer(package.get_type()).ok()?; - let promise = installer.cleanup("install", package, None).ok()?; + let promise = installer.cleanup("install", package, None).await.ok()?; promise } @@ -556,7 +561,7 @@ impl InstallationManager { ) -> Option<PhpMixed> { let package = operation.get_package(); let installer = self.get_installer(package.get_type()).ok()?; - let promise = installer.install(repo, package).ok()?; + let promise = installer.install(repo, package).await.ok()?; self.mark_for_notification(package); promise @@ -578,24 +583,19 @@ impl InstallationManager { let promise = if initial_type == target_type { let installer = self.get_installer(initial_type).ok()?; - let promise = installer.update(repo, initial, target).ok()?; + let promise = installer.update(repo, initial, target).await.ok()?; self.mark_for_notification(target); promise } else { - let promise = self + // PHP: uninstall initial, then install target via the target-type installer. + let _ = self .get_installer(initial_type) .ok()? .uninstall(repo, initial) + .await .ok()?; - let promise = match promise { - Some(p) => p, - None => promise::resolve(None), - }; - - let target_type = target_type.to_string(); - // TODO(phase-b): promise.then(closure capturing self/installer) - let _ = target_type; - Some(promise) + let installer = self.get_installer(target_type).ok()?; + installer.install(repo, target).await.ok()? }; promise @@ -612,7 +612,7 @@ impl InstallationManager { let package = operation.get_package(); let installer = self.get_installer(package.get_type()).ok()?; - installer.uninstall(repo, package).ok()? + installer.uninstall(repo, package).await.ok()? } /// Executes markAliasInstalled operation. @@ -653,6 +653,8 @@ impl InstallationManager { } pub fn notify_installs(&mut self, _io: &dyn IOInterface) { + // TODO(phase-c-promise): collects http_downloader.add() promises and drives them via Loop::wait; + // rewrite to await the collected futures once the async Loop boundary lands. let mut promises: Vec<Box<dyn PromiseInterface>> = vec![]; let result: Result<()> = (|| -> Result<()> { @@ -802,6 +804,8 @@ impl InstallationManager { &mut self, cleanup_promises: &IndexMap<i64, Box<dyn Fn() -> Option<Box<dyn PromiseInterface>>>>, ) { + // TODO(phase-c-promise): runs cleanup closures and drives them via Loop::wait; + // rewrite once the async Loop boundary and async cleanup closures land. let mut promises: Vec<Box<dyn PromiseInterface>> = vec![]; self.loop_.borrow().abort_jobs(); diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index a9a77e9..0629de5 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -137,11 +137,10 @@ impl LibraryInstaller { ) -> Result<Option<PhpMixed>> { let download_path = self.get_install_path(package).unwrap(); - Ok(Some( - self.get_download_manager() - .borrow() - .install(package, &download_path)?, - )) + self.get_download_manager() + .borrow() + .install(package, &download_path) + .await } /// @return PromiseInterface|null @@ -159,19 +158,9 @@ impl LibraryInstaller { if strpos(&initial_download_path, &target_download_path) == Some(0) || strpos(&target_download_path, &initial_download_path) == Some(0) { - let promise = self.remove_code(initial)?; - let promise = match promise { - Some(p) => p, - None => 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)" - ))); + // PHP: return $this->removeCode($initial)->then(fn () => $this->installCode($target)); + let _ = self.remove_code(initial).await?; + return self.install_code(target).await; } self.filesystem @@ -179,11 +168,10 @@ impl LibraryInstaller { .rename(&initial_download_path, &target_download_path); } - Ok(Some(self.get_download_manager().borrow().update( - initial, - target, - &target_download_path, - )?)) + self.get_download_manager() + .borrow() + .update(initial, target, &target_download_path) + .await } /// @return PromiseInterface|null @@ -194,11 +182,10 @@ impl LibraryInstaller { ) -> Result<Option<PhpMixed>> { let download_path = self.get_package_base_path(package); - Ok(Some( - self.get_download_manager() - .borrow() - .remove(package, &download_path)?, - )) + self.get_download_manager() + .borrow() + .remove(package, &download_path) + .await } pub(crate) fn initialize_vendor_dir(&mut self) { @@ -275,11 +262,10 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - Ok(Some(self.get_download_manager().borrow().download( - package, - &download_path, - prev_package, - )?)) + self.get_download_manager() + .borrow() + .download(package, &download_path, prev_package) + .await } async fn prepare( @@ -292,12 +278,10 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - Ok(Some(self.get_download_manager().borrow().prepare( - r#type, - package, - &download_path, - prev_package, - )?)) + self.get_download_manager() + .borrow() + .prepare(r#type, package, &download_path, prev_package) + .await } async fn cleanup( @@ -310,12 +294,10 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); let download_path = self.get_install_path(package).unwrap(); - Ok(Some(self.get_download_manager().borrow().cleanup( - r#type, - package, - &download_path, - prev_package, - )?)) + self.get_download_manager() + .borrow() + .cleanup(r#type, package, &download_path, prev_package) + .await } async fn install( @@ -332,6 +314,7 @@ impl InstallerInterface for LibraryInstaller { self.binary_installer.remove_binaries(package); } + // TODO(phase-c-promise): rewrite install_code().then(installBinaries + repo.addPackage) as an await sequence. let promise = self.install_code(package)?; let promise = match promise { Some(p) => p, @@ -365,6 +348,7 @@ impl InstallerInterface for LibraryInstaller { // self.initialize_vendor_dir(); self.binary_installer.remove_binaries(initial); + // TODO(phase-c-promise): rewrite update_code().then(installBinaries + repo updates) as an await sequence. let promise = self.update_code(initial, target)?; let promise = match promise { Some(p) => p, @@ -393,6 +377,7 @@ impl InstallerInterface for LibraryInstaller { .into()); } + // TODO(phase-c-promise): rewrite remove_code().then(remove_binaries/remove_package/rmdir) as an await sequence. let promise = self.remove_code(package)?; let promise = match promise { Some(p) => p, diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs index f6d6133..23d92f2 100644 --- a/crates/shirabe/src/installer/metapackage_installer.rs +++ b/crates/shirabe/src/installer/metapackage_installer.rs @@ -40,9 +40,7 @@ impl InstallerInterface for MetapackageInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn prepare( @@ -51,9 +49,7 @@ impl InstallerInterface for MetapackageInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn cleanup( @@ -62,9 +58,7 @@ impl InstallerInterface for MetapackageInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn install( @@ -80,9 +74,7 @@ impl InstallerInterface for MetapackageInstaller { repo.add_package(package.clone_package_box()); - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn update( @@ -108,9 +100,7 @@ impl InstallerInterface for MetapackageInstaller { repo.remove_package(initial); repo.add_package(target.clone_package_box()); - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn uninstall( @@ -134,9 +124,7 @@ impl InstallerInterface for MetapackageInstaller { repo.remove_package(package); - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } fn get_install_path(&self, _package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs index 263caca..68e4ef8 100644 --- a/crates/shirabe/src/installer/noop_installer.rs +++ b/crates/shirabe/src/installer/noop_installer.rs @@ -26,9 +26,7 @@ impl InstallerInterface for NoopInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> anyhow::Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn prepare( @@ -37,9 +35,7 @@ impl InstallerInterface for NoopInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> anyhow::Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn cleanup( @@ -48,9 +44,7 @@ impl InstallerInterface for NoopInstaller { _package: &dyn PackageInterface, _prev_package: Option<&dyn PackageInterface>, ) -> anyhow::Result<Option<PhpMixed>> { - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn install( @@ -62,9 +56,7 @@ impl InstallerInterface for NoopInstaller { repo.add_package(package.clone_package_box()); } - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn update( @@ -86,9 +78,7 @@ impl InstallerInterface for NoopInstaller { repo.add_package(target.clone_package_box()); } - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } async fn uninstall( @@ -105,9 +95,7 @@ impl InstallerInterface for NoopInstaller { } repo.remove_package(package); - Ok(Some(shirabe_external_packages::react::promise::resolve( - None, - ))) + Ok(None) } fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs index fd1fc99..4f9918e 100644 --- a/crates/shirabe/src/installer/plugin_installer.rs +++ b/crates/shirabe/src/installer/plugin_installer.rs @@ -96,7 +96,7 @@ impl InstallerInterface for PluginInstaller { let _ = plugin_optional; } - self.inner.prepare(r#type, package, prev_package) + self.inner.prepare(r#type, package, prev_package).await } async fn download( @@ -116,7 +116,7 @@ impl InstallerInterface for PluginInstaller { }.into()); } - self.inner.download(package, prev_package) + self.inner.download(package, prev_package).await } async fn install( @@ -124,22 +124,13 @@ impl InstallerInterface for PluginInstaller { repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface, ) -> Result<Option<PhpMixed>> { - let promise = self.inner.install(repo, package)?; - let promise = match promise { - Some(p) => p, - None => shirabe_external_packages::react::promise::resolve(None), - }; + self.inner.install(repo, package).await?; // TODO(plugin): register package in plugin manager after install, rollback on failure - 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, - ))) + Platform::workaround_filesystem_issues(); + // self.get_plugin_manager().register_package(package, true)?; + // On error: self.rollback_install(e, repo, package)?; + Ok(None) } async fn update( @@ -148,23 +139,14 @@ impl InstallerInterface for PluginInstaller { initial: &dyn PackageInterface, target: &dyn PackageInterface, ) -> Result<Option<PhpMixed>> { - let promise = self.inner.update(repo, initial, target)?; - let promise = match promise { - Some(p) => p, - None => shirabe_external_packages::react::promise::resolve(None), - }; + self.inner.update(repo, initial, target).await?; // TODO(plugin): deactivate initial and register target in plugin manager after update, rollback on failure - 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, - ))) + 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(None) } async fn uninstall( @@ -177,7 +159,7 @@ impl InstallerInterface for PluginInstaller { .borrow_mut() .uninstall_package(package); - self.inner.uninstall(repo, package) + self.inner.uninstall(repo, package).await } async fn cleanup( @@ -186,7 +168,7 @@ impl InstallerInterface for PluginInstaller { package: &dyn PackageInterface, prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { - self.inner.cleanup(r#type, package, prev_package) + self.inner.cleanup(r#type, package, prev_package).await } fn get_install_path(&self, package: &dyn PackageInterface) -> Option<String> { diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs index 1c50a16..034a07a 100644 --- a/crates/shirabe/src/installer/project_installer.rs +++ b/crates/shirabe/src/installer/project_installer.rs @@ -64,7 +64,7 @@ impl InstallerInterface for ProjectInstaller { self.download_manager .borrow() .download(package, install_path, prev_package) - .map(Some) + .await } async fn prepare( @@ -76,7 +76,7 @@ impl InstallerInterface for ProjectInstaller { self.download_manager .borrow() .prepare(r#type, package, &self.install_path, prev_package) - .map(Some) + .await } async fn cleanup( @@ -88,7 +88,7 @@ impl InstallerInterface for ProjectInstaller { self.download_manager .borrow() .cleanup(r#type, package, &self.install_path, prev_package) - .map(Some) + .await } async fn install( @@ -96,11 +96,10 @@ impl InstallerInterface for ProjectInstaller { _repo: &mut dyn InstalledRepositoryInterface, package: &dyn PackageInterface, ) -> anyhow::Result<Option<PhpMixed>> { - Ok(Some( - self.download_manager - .borrow() - .install(package, &self.install_path)?, - )) + self.download_manager + .borrow() + .install(package, &self.install_path) + .await } async fn update( diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs index 51a54e0..79f9d6d 100644 --- a/crates/shirabe/src/package/version/version_guesser.rs +++ b/crates/shirabe/src/package/version/version_guesser.rs @@ -518,6 +518,10 @@ impl VersionGuesser { strnatcasecmp(b, a) }); + // TODO(phase-c-promise): execute_async is now async; the .then continuation captures and mutates shared + // state (last_index, length, version, pretty_version, promises) and cancels sibling promises, while the + // loop is driven by process.wait(). This concurrent job/cancellation machinery needs design before it can + // become an await-based form, so the promise body (including the inner todo!()) is left as-is. let mut promises: Vec< Box<dyn shirabe_external_packages::react::promise::PromiseInterface>, > = vec![]; diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index 7601117..3c5eb31 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -1103,6 +1103,9 @@ impl ComposerRepository { .map_or(false, |c| c.metadata) && (allow_partial_advisories || api_url.is_none()) { + // TODO(phase-c-promise): collects start_cached_async_download().then_boxed() promises and + // drives them via Loop::wait; rewrite to await the collected futures once the async Loop + // boundary lands. let mut promises: Vec<Box<dyn PromiseInterface>> = Vec::new(); let names: Vec<String> = package_constraint_map.keys().cloned().collect(); for name in names { @@ -1848,6 +1851,9 @@ impl ComposerRepository { let mut packages: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new(); let mut names_found: IndexMap<String, bool> = IndexMap::new(); + // TODO(phase-c-promise): collects start_cached_async_download().then_boxed() promises and + // drives them via Loop::wait; rewrite to await the collected futures once the async Loop + // boundary lands. let mut promises: Vec<Box<dyn PromiseInterface>> = Vec::new(); if self.lazy_providers_url.is_none() { @@ -2131,62 +2137,43 @@ impl ComposerRepository { contents_opt = None; } - let promise = self.async_fetch_file(&url, &cache_key, last_modified.as_deref())?; - let url_owned = url.clone(); - let cache_key_owned = cache_key.clone(); - let contents = contents_opt; - // TODO(phase-b): then_boxed expects closure returning Box<dyn PromiseInterface>, - // not anyhow::Result<PhpMixed>; needs structural reshape - Ok(promise.then_boxed( - Some(Box::new( - move |response: PhpMixed| -> Box<dyn PromiseInterface> { - let _result: anyhow::Result<PhpMixed> = (|| -> anyhow::Result<PhpMixed> { - let mut packages_source = - format!("downloaded file ({})", Url::sanitize(url_owned.clone())); + // PHP: return $this->asyncFetchFile(...)->then(function ($response) use (...): array { ... }); + let response = self + .async_fetch_file(&url, &cache_key, last_modified.as_deref()) + .await?; - let response_data = if response.as_bool() == Some(true) { - packages_source = format!( - "cached file ({} originating from {})", - cache_key_owned, - Url::sanitize(url_owned.clone()) - ); - contents - .clone() - .map(|m| { - PhpMixed::Array( - m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ) - }) - .unwrap_or(PhpMixed::Null) - } else { - response - }; + let mut packages_source = format!("downloaded file ({})", Url::sanitize(url.clone())); - let response_arr = response_data.as_array(); - let has_pkg = response_arr - .and_then(|a| a.get("packages")) - .and_then(|v| v.as_array()) - .map_or(false, |a| a.contains_key(&package_name)); - let has_advisories = - response_arr.map_or(false, |a| a.contains_key("security-advisories")); - if !has_pkg && !has_advisories { - return Ok(PhpMixed::List(vec![ - Box::new(PhpMixed::Null), - Box::new(PhpMixed::String(packages_source)), - ])); - } + let response_data = if response.as_bool() == Some(true) { + packages_source = format!( + "cached file ({} originating from {})", + cache_key, + Url::sanitize(url.clone()) + ); + contents_opt + .map(|m| PhpMixed::Array(m.into_iter().map(|(k, v)| (k, Box::new(v))).collect())) + .unwrap_or(PhpMixed::Null) + } else { + response + }; - Ok(PhpMixed::List(vec![ - Box::new(response_data), - Box::new(PhpMixed::String(packages_source)), - ])) - })(); - // TODO(phase-b): return a real PromiseInterface - todo!("return real PromiseInterface") - }, - )), - None, - )) + let response_arr = response_data.as_array(); + let has_pkg = response_arr + .and_then(|a| a.get("packages")) + .and_then(|v| v.as_array()) + .map_or(false, |a| a.contains_key(&package_name)); + let has_advisories = response_arr.map_or(false, |a| a.contains_key("security-advisories")); + if !has_pkg && !has_advisories { + return Ok(PhpMixed::List(vec![ + Box::new(PhpMixed::Null), + Box::new(PhpMixed::String(packages_source)), + ])); + } + + Ok(PhpMixed::List(vec![ + Box::new(response_data), + Box::new(PhpMixed::String(packages_source)), + ])) } /// @param name package name (must be lowercased already) @@ -3293,18 +3280,20 @@ impl ComposerRepository { if self.packagesNotFoundCache.contains_key(filename) { let mut empty: IndexMap<String, PhpMixed> = IndexMap::new(); empty.insert("packages".to_string(), PhpMixed::Array(IndexMap::new())); - return Ok(react_promise_resolve(PhpMixed::Array( + return Ok(PhpMixed::Array( empty.into_iter().map(|(k, v)| (k, Box::new(v))).collect(), - ))); + )); } if self.freshMetadataUrls.contains_key(filename) && last_modified_time.is_some() { // make it look like we got a 304 response - let promise = react_promise_resolve(PhpMixed::Bool(true)); - - return Ok(promise); + return Ok(PhpMixed::Bool(true)); } + // TODO(phase-c-promise): the live fetch path builds accept/reject closures (with raw-pointer + // shared state) and returns http_downloader.add(...).then_with_reject_boxed(accept, reject). + // Rewrite as an async fetch + inline accept/reject handling once the HttpDownloader async + // boundary lands. let mut filename = filename.to_string(); let mut options = self.options.clone(); if let Some(dispatcher) = self.event_dispatcher.as_ref() { diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs index 39b5e6b..1bf4673 100644 --- a/crates/shirabe/src/util/filesystem.rs +++ b/crates/shirabe/src/util/filesystem.rs @@ -135,9 +135,7 @@ impl Filesystem { pub async fn remove_directory_async(&mut self, directory: &str) -> anyhow::Result<bool> { let edge_case_result = self.remove_edge_cases(directory, true)?; if let Some(r) = edge_case_result { - return Ok(shirabe_external_packages::react::promise::resolve(Some( - PhpMixed::Bool(r), - ))); + return Ok(r); } let cmd: Vec<String> = if Platform::is_windows() { @@ -151,6 +149,9 @@ impl Filesystem { vec!["rm".to_string(), "-rf".to_string(), directory.to_string()] }; + // TODO(phase-c-promise): execute_async is now async fn -> Result<Process>; the .then_boxed continuation that + // inspects the process result and flattens into a recursive removeDirectoryPhp fallback needs job-machine + // boundary design before it can become a flat await chain. let promise = self.get_process().execute_async( PhpMixed::List( cmd.iter() diff --git a/crates/shirabe/src/util/sync_helper.rs b/crates/shirabe/src/util/sync_helper.rs index 6cf2467..4c48c5c 100644 --- a/crates/shirabe/src/util/sync_helper.rs +++ b/crates/shirabe/src/util/sync_helper.rs @@ -20,8 +20,8 @@ impl<'a> DownloaderOrManager<'a> { prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { match self { - Self::Interface(d) => d.download3(package, path, prev_package), - Self::Manager(d) => d.borrow().download(package, path, prev_package), + Self::Interface(d) => d.download3(package, path, prev_package).await, + Self::Manager(d) => d.borrow().download(package, path, prev_package).await, } } @@ -33,8 +33,12 @@ impl<'a> DownloaderOrManager<'a> { prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { match self { - Self::Interface(d) => d.prepare(r#type, package, path, prev_package), - Self::Manager(d) => d.borrow().prepare(r#type, package, path, prev_package), + Self::Interface(d) => d.prepare(r#type, package, path, prev_package).await, + Self::Manager(d) => { + d.borrow() + .prepare(r#type, package, path, prev_package) + .await + } } } @@ -44,8 +48,8 @@ impl<'a> DownloaderOrManager<'a> { path: &str, ) -> Result<Option<PhpMixed>> { match self { - Self::Interface(d) => d.install2(package, path), - Self::Manager(d) => d.borrow().install(package, path), + Self::Interface(d) => d.install2(package, path).await, + Self::Manager(d) => d.borrow().install(package, path).await, } } @@ -56,8 +60,8 @@ impl<'a> DownloaderOrManager<'a> { path: &str, ) -> Result<Option<PhpMixed>> { match self { - Self::Interface(d) => d.update(package, prev_package, path), - Self::Manager(d) => d.borrow().update(package, prev_package, path), + Self::Interface(d) => d.update(package, prev_package, path).await, + Self::Manager(d) => d.borrow().update(package, prev_package, path).await, } } @@ -69,8 +73,12 @@ impl<'a> DownloaderOrManager<'a> { prev_package: Option<&dyn PackageInterface>, ) -> Result<Option<PhpMixed>> { match self { - Self::Interface(d) => d.cleanup(r#type, package, path, prev_package), - Self::Manager(d) => d.borrow().cleanup(r#type, package, path, prev_package), + Self::Interface(d) => d.cleanup(r#type, package, path, prev_package).await, + Self::Manager(d) => { + d.borrow() + .cleanup(r#type, package, path, prev_package) + .await + } } } } @@ -78,6 +86,7 @@ impl<'a> DownloaderOrManager<'a> { pub struct SyncHelper; impl SyncHelper { + // TODO(phase-c-promise): synchronous wrapper driving now-async downloader calls via Self::await (loop.wait); needs async/loop boundary design. pub fn download_and_install_package_sync( r#loop: &std::rc::Rc<std::cell::RefCell<Loop>>, downloader: DownloaderOrManager<'_>, @@ -125,6 +134,7 @@ impl SyncHelper { Ok(()) } + // TODO(phase-c-promise): loop-pump synchronous wait over a promise; driving mechanism needs design. pub fn r#await( r#loop: &std::rc::Rc<std::cell::RefCell<Loop>>, promise: Option<Box<dyn PromiseInterface>>, |
