diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-08 22:14:12 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-08 22:14:12 +0900 |
| commit | f4cad2123b2af0de72bda4ce039e16e74f163f4e (patch) | |
| tree | 21803308c5ff41e23c9d3b117433eea16b4ff663 /crates/shirabe/src/util/process_executor.rs | |
| parent | 0209f63210e5b547b5c6b73367bb80ea86c255ec (diff) | |
| download | php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.tar.gz php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.tar.zst php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.zip | |
feat(php-shim): give ported exceptions PHP's class hierarchy
Ported exceptions were flat structs reached with `downcast_ref`, so
Composer's `catch (\RuntimeException $e)` only matched the exact leaf
type and `get_class($e)` had nothing to report. Each exception now
embeds an instance of the class it extends and travels inside an
`AnyThrowable`; `Catch::catch`/`catch_mut` walk that chain, and
`PhpClass::php_class_name` yields the PHP FQCN.
Dropping the `std::error::Error` impls from the exception types leaves
`AnyThrowable` as the only route into an `anyhow::Error`, so the walk
cannot be bypassed. A `no_exception_downcast` linter catches the
`downcast::<X>()` calls that would now silently answer `None`.
Three sites change behavior as a result: the `TransportException`
exit-code override reaches `MaxFileSizeExceededException`, the
`catch (\LogicException)` in findSimilar() reaches its subclasses, and
rendered exception titles carry the real class name rather than a
guess. `get_class_err()` is no longer a `todo!()`, which re-enables
FilesystemRepositoryTest::testCorruptedRepositoryFile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/util/process_executor.rs')
| -rw-r--r-- | crates/shirabe/src/util/process_executor.rs | 49 |
1 files changed, 18 insertions, 31 deletions
diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs index 463c5d19..b9373b6f 100644 --- a/crates/shirabe/src/util/process_executor.rs +++ b/crates/shirabe/src/util/process_executor.rs @@ -13,6 +13,7 @@ use shirabe_external_packages::symfony::process::Process; use shirabe_external_packages::symfony::process::ProcessMock; use shirabe_external_packages::symfony::process::exception::ProcessSignaledException; use shirabe_external_packages::symfony::process::exception::RuntimeException as SymfonyProcessRuntimeException; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ LogicException, PHP_EOL, PhpMixed, RuntimeException, array_intersect, array_map, escapeshellarg, explode, implode, in_array_strict, is_array, is_dir, is_numeric, is_string, @@ -253,17 +254,13 @@ impl ProcessExecutor { Some(Self::get_timeout() as f64), )?; } else { - return Err(LogicException { - message: "Invalid command type".to_string(), - code: 0, - } - .into()); + return Err(LogicException::new("Invalid command type".to_string()).into()); } if !Platform::is_windows() && tty { // PHP: try { $process->setTty(true); } catch (RuntimeException $e) { /* ignore */ } if let Err(e) = process.set_tty(true) - && e.downcast_ref::<SymfonyProcessRuntimeException>().is_none() + && !e.is_instanceof::<SymfonyProcessRuntimeException>() { return Err(e); } @@ -312,7 +309,7 @@ impl ProcessExecutor { let final_result: anyhow::Result<()> = match result { Ok(()) => Ok(()), Err(e) => { - if let Some(pse) = e.downcast_ref::<ProcessSignaledException>() { + if let Some(pse) = e.catch::<ProcessSignaledException>() { if signal_handler.is_triggered() { // exiting as we were signaled and the child process exited too due to the signal signal_handler.exit_with_last_signal(); @@ -451,21 +448,18 @@ impl ProcessExecutor { // strict-mode mismatch) extends `\RuntimeException`, so PHP call sites that // `catch (\RuntimeException $e)` around a mock-driven git/hg/svn call (e.g. // `GitDriver::supports`) treat a mismatch as an ordinary recoverable failure. Using - // the same `RuntimeException` type here keeps `downcast_ref::<RuntimeException>()` - // checks working the same way against a mismatch. - return Err(RuntimeException { - message: format!( - "Received unexpected command {:?} in \"{}\"{}{}{}Received calls:{}{}", - command, - cwd.unwrap_or(""), - PHP_EOL, - expected, - PHP_EOL, - PHP_EOL, - received - ), - code: 0, - } + // the same `RuntimeException` type here keeps `catch::<RuntimeException>()` checks + // working the same way against a mismatch. + return Err(RuntimeException::new(format!( + "Received unexpected command {:?} in \"{}\"{}{}{}Received calls:{}{}", + command, + cwd.unwrap_or(""), + PHP_EOL, + expected, + PHP_EOL, + PHP_EOL, + received + )) .into()); } @@ -645,10 +639,7 @@ impl ProcessExecutor { Box::pin(async move { if !allow_async { - return Err(LogicException { - message: "You must use the ProcessExecutor instance which is part of a Composer\\Loop instance to be able to run async processes".to_string(), - code: 0, - } + return Err(LogicException::new("You must use the ProcessExecutor instance which is part of a Composer\\Loop instance to be able to run async processes".to_string()) .into()); } @@ -682,11 +673,7 @@ impl ProcessExecutor { Some(Self::get_timeout() as f64), )? } else { - return Err(LogicException { - message: "Invalid command type".to_string(), - code: 0, - } - .into()); + return Err(LogicException::new("Invalid command type".to_string()).into()); }; process.start(None, IndexMap::new())?; |
