From f4cad2123b2af0de72bda4ce039e16e74f163f4e Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 22:14:12 +0900 Subject: 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::()` 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) --- .../shirabe/src/command/create_project_command.rs | 84 +++++++++------------- 1 file changed, 33 insertions(+), 51 deletions(-) (limited to 'crates/shirabe/src/command/create_project_command.rs') diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index aa1b79a8..267c866e 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -42,6 +42,7 @@ use shirabe_external_packages::symfony::console::command::command::Command; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; use shirabe_external_packages::symfony::finder::Finder; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ DIRECTORY_SEPARATOR, InvalidArgumentException, PhpMixed, RuntimeException, UnexpectedValueException, array_pop, chdir, explode_with_limit, file_exists, getcwd, @@ -311,7 +312,7 @@ impl CreateProjectCommand { } } Err(e) => { - if e.downcast_ref::().is_some() { + if e.is_instanceof::() { io.write_error("Hint: To allow running the config command recommended below before dependencies are installed, run create-project with --no-install."); io.write_error(&format!( "You can then cd into {}, configure allow-plugins, and finally run a composer install to complete the process.", @@ -365,11 +366,7 @@ impl CreateProjectCommand { for dir in &dirs { if !fs.remove_directory(dir)? { had_error = Some( - RuntimeException { - message: format!("Could not remove {}", dir.display()), - code: 0, - } - .into(), + RuntimeException::new(format!("Could not remove {}", dir.display())).into(), ); break; } @@ -485,10 +482,9 @@ impl CreateProjectCommand { ); } if directory.is_empty() { - return Err(UnexpectedValueException { - message: "Got an empty target directory, something went wrong".to_string(), - code: 0, - } + return Err(UnexpectedValueException::new( + "Got an empty target directory, something went wrong".to_string(), + ) .into()); } @@ -513,20 +509,17 @@ impl CreateProjectCommand { if file_exists(&directory) { if !is_dir(&directory) { - return Err(InvalidArgumentException { - message: format!( - "Cannot create project directory at \"{}\", it exists as a file.", - directory - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Cannot create project directory at \"{}\", it exists as a file.", + directory + )) .into()); } if !fs.borrow().is_dir_empty(&directory) { - return Err(InvalidArgumentException { - message: format!("Project directory \"{}\" is not empty.", directory), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Project directory \"{}\" is not empty.", + directory + )) .into()); } } @@ -575,20 +568,17 @@ impl CreateProjectCommand { .unwrap_or_default(); if !STABILITIES.contains_key(stability.as_str()) { - return Err(InvalidArgumentException { - message: format!( - "Invalid stability provided ({}), must be one of: {}", - stability, - implode( - ", ", - &STABILITIES - .keys() - .map(|k| k.to_string()) - .collect::>() - ) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Invalid stability provided ({}), must be one of: {}", + stability, + implode( + ", ", + &STABILITIES + .keys() + .map(|k| k.to_string()) + .collect::>() + ) + )) .into()); } @@ -730,21 +720,14 @@ impl CreateProjectCommand { )? .is_some() { - return Err(InvalidArgumentException { - message: format!( - "{} in a version installable using your PHP version, PHP extensions and Composer version.", - error_message - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "{} in a version installable using your PHP version, PHP extensions and Composer version.", + error_message + )) .into()); } - return Err(InvalidArgumentException { - message: format!("{}.", error_message), - code: 0, - } - .into()); + return Err(InvalidArgumentException::new(format!("{}.", error_message)).into()); } let mut package = package.unwrap(); @@ -936,10 +919,9 @@ impl Command for CreateProjectCommand { { let package = input.borrow().get_argument("package")?; if package.is_null() { - return Err(RuntimeException { - message: "Not enough arguments (missing: \"package\").".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Not enough arguments (missing: \"package\").".to_string(), + ) .into()); } let mut parts = -- cgit v1.3.1-4-g156e