diff options
Diffstat (limited to 'crates/shirabe')
162 files changed, 2252 insertions, 3372 deletions
diff --git a/crates/shirabe/src/advisory/audit_config.rs b/crates/shirabe/src/advisory/audit_config.rs index 71730317..fc1cbedb 100644 --- a/crates/shirabe/src/advisory/audit_config.rs +++ b/crates/shirabe/src/advisory/audit_config.rs @@ -103,13 +103,10 @@ impl AuditConfig { .map(|s| s.to_string()); if !["audit", "block", "all"].contains(&apply.as_str()) { - return Err(InvalidArgumentException { - message: format!( - "Invalid 'apply' value for '{}': {}. Expected 'audit', 'block', or 'all'.", - key, apply - ), - code: 0, - }.into()); + return Err(InvalidArgumentException::new(format!( + "Invalid 'apply' value for '{}': {}. Expected 'audit', 'block', or 'all'.", + key, apply + )).into()); } (key.clone(), apply, reason) diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs index d20c9453..d661cf90 100644 --- a/crates/shirabe/src/advisory/auditor.rs +++ b/crates/shirabe/src/advisory/auditor.rs @@ -422,13 +422,10 @@ impl Auditor { .map(|buffer_io| &buffer_io.inner) }); if io_as_console.is_none() { - return Err(InvalidArgumentException { - message: format!( - "Cannot use table format with {}", - get_class(&PhpMixed::Null) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Cannot use table format with {}", + get_class(&PhpMixed::Null) + )) .into()); } self.output_advisories_table(io_as_console.unwrap(), advisories)?; @@ -441,11 +438,9 @@ impl Auditor { Ok(()) } Self::FORMAT_SUMMARY => Ok(()), - _ => Err(InvalidArgumentException { - message: format!("Invalid format \"{}\".", format), - code: 0, + _ => { + Err(InvalidArgumentException::new(format!("Invalid format \"{}\".", format)).into()) } - .into()), } } @@ -597,13 +592,10 @@ impl Auditor { .map(|buffer_io| &buffer_io.inner) }); if io_as_console.is_none() { - return Err(InvalidArgumentException { - message: format!( - "Cannot use table format with {}", - get_class(&PhpMixed::Null) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Cannot use table format with {}", + get_class(&PhpMixed::Null) + )) .into()); } diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index aacbfc0f..4eae319d 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -815,19 +815,13 @@ return array( { let name = package.get_name(); let _ = package.get_target_dir(); - return Err(InvalidArgumentException { - message: format!("PSR-4 autoloading is incompatible with the target-dir property, remove the target-dir in package '{}'.", name), - code: 0, - } + return Err(InvalidArgumentException::new(format!("PSR-4 autoloading is incompatible with the target-dir property, remove the target-dir in package '{}'.", name)) .into()); } if let Some(psr4) = autoload.get("psr-4").and_then(|v| v.as_array()) { for (namespace, _dirs) in psr4 { if !namespace.is_empty() && !namespace.ends_with('\\') { - return Err(InvalidArgumentException { - message: format!("psr-4 namespaces must end with a namespace separator, '{}' does not, use '{}\\'.", namespace, namespace), - code: 0, - } + return Err(InvalidArgumentException::new(format!("psr-4 namespaces must end with a namespace separator, '{}' does not, use '{}\\'.", namespace, namespace)) .into()); } } diff --git a/crates/shirabe/src/autoload/class_loader.rs b/crates/shirabe/src/autoload/class_loader.rs index 89758269..47b6ee4c 100644 --- a/crates/shirabe/src/autoload/class_loader.rs +++ b/crates/shirabe/src/autoload/class_loader.rs @@ -175,11 +175,9 @@ impl ClassLoader { // Register directories for a new namespace. let length = strlen(prefix); if "\\" != &prefix[(length as usize - 1)..(length as usize)] { - return Err(InvalidArgumentException { - message: "A non-empty PSR-4 prefix must end with a namespace separator." - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "A non-empty PSR-4 prefix must end with a namespace separator.".to_string(), + ) .into()); } let first = prefix.chars().next().unwrap_or('\0').to_string(); @@ -226,11 +224,9 @@ impl ClassLoader { } else { let length = strlen(prefix); if "\\" != &prefix[(length as usize - 1)..(length as usize)] { - return Err(InvalidArgumentException { - message: "A non-empty PSR-4 prefix must end with a namespace separator." - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "A non-empty PSR-4 prefix must end with a namespace separator.".to_string(), + ) .into()); } let first = prefix.chars().next().unwrap_or('\0').to_string(); diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs index 354e559e..a9f56eac 100644 --- a/crates/shirabe/src/cache.rs +++ b/crates/shirabe/src/cache.rs @@ -180,7 +180,7 @@ impl Cache { self.io.write_error3( &format!( "<warning>Failed to write into cache: {}</warning>", - e.message + e.get_message() ), true, crate::io::DEBUG, @@ -190,7 +190,7 @@ impl Cache { php_regex!( r"{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}" ), - &e.message, + e.get_message(), Some(&mut m), ) { // Remove partial file. diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index e749d13f..1c428c58 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -303,13 +303,10 @@ impl ArchiveCommand { }; let Some(complete) = package.as_complete() else { - return Err(LogicException { - message: format!( - "Expected a CompletePackageInterface instance but found {}", - get_debug_type(&shirabe_php_shim::PhpMixed::Null) - ), - code: 0, - } + return Err(LogicException::new(format!( + "Expected a CompletePackageInterface instance but found {}", + get_debug_type(&shirabe_php_shim::PhpMixed::Null) + )) .into()); }; diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs index 0eee0036..268984d6 100644 --- a/crates/shirabe/src/command/audit_command.rs +++ b/crates/shirabe/src/command/audit_command.rs @@ -60,10 +60,7 @@ impl AuditCommand { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); if !locker.is_locked() { - return Err(UnexpectedValueException { - message: "Valid composer.json and composer.lock files are required to run this command with --locked".to_string(), - code: 0, - }.into()); + return Err(UnexpectedValueException::new("Valid composer.json and composer.lock files are required to run this command with --locked".to_string()).into()); } let locked_repo = locker.get_locked_repository( !input @@ -228,19 +225,16 @@ impl Command for AuditCommand { .collect::<Vec<_>>(), ) { - return Err(InvalidArgumentException { - message: format!( - "--abandoned must be one of {}.", - implode( - ", ", - &Auditor::ABANDONEDS - .iter() - .map(|s| s.to_string()) - .collect::<Vec<_>>() - ) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "--abandoned must be one of {}.", + implode( + ", ", + &Auditor::ABANDONEDS + .iter() + .map(|s| s.to_string()) + .collect::<Vec<_>>() + ) + )) .into()); } diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 7a66862e..a1de9783 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -294,10 +294,7 @@ impl BaseCommand for BaseCommandData { if self.composer.borrow().is_none() { let application = self.get_application(); let Some(application) = application else { - return Err(RuntimeException { - message: "Could not create a Composer\\Composer instance, you must inject one if this command is not used with a Composer\\Console\\Application instance".to_string(), - code: 0, - } + return Err(RuntimeException::new("Could not create a Composer\\Composer instance, you must inject one if this command is not used with a Composer\\Console\\Application instance".to_string()) .into()); }; let composer = { @@ -455,11 +452,9 @@ impl BaseCommand for BaseCommandData { .as_bool() .unwrap_or(false) { - return Err(InvalidArgumentException { - message: "--prefer-source can not be used together with --prefer-install" - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "--prefer-source can not be used together with --prefer-install".to_string(), + ) .into()); } if input @@ -468,11 +463,9 @@ impl BaseCommand for BaseCommandData { .as_bool() .unwrap_or(false) { - return Err(InvalidArgumentException { - message: "--prefer-dist can not be used together with --prefer-install" - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "--prefer-dist can not be used together with --prefer-install".to_string(), + ) .into()); } let prefer_install = input.borrow().get_option("prefer-install")?; @@ -492,13 +485,10 @@ impl BaseCommand for BaseCommandData { prefer_source = false; } other => { - return Err(UnexpectedValueException { - message: format!( - "--prefer-install accepts one of \"dist\", \"source\" or \"auto\", got {}", - other - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "--prefer-install accepts one of \"dist\", \"source\" or \"auto\", got {}", + other + )) .into()); } } @@ -551,12 +541,8 @@ impl BaseCommand for BaseCommandData { if !input.borrow().has_option("ignore-platform-reqs") || !input.borrow().has_option("ignore-platform-req") { - return Err(LogicException { - message: - "Calling getPlatformRequirementFilter from a command which does not define the --ignore-platform-req[s] flags is not permitted." - .to_string(), - code: 0, - } + return Err(LogicException::new("Calling getPlatformRequirementFilter from a command which does not define the --ignore-platform-req[s] flags is not permitted." + .to_string()) .into()); } @@ -585,14 +571,11 @@ impl BaseCommand for BaseCommandData { let requirements = self.normalize_requirements(requirements)?; for requirement in requirements { if !requirement.contains_key("version") { - return Err(UnexpectedValueException { - message: format!( - "Option {} is missing a version constraint, use e.g. {}:^1.0", - requirement.get("name").map(|s| s.as_str()).unwrap_or(""), - requirement.get("name").map(|s| s.as_str()).unwrap_or(""), - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Option {} is missing a version constraint, use e.g. {}:^1.0", + requirement.get("name").map(|s| s.as_str()).unwrap_or(""), + requirement.get("name").map(|s| s.as_str()).unwrap_or(""), + )) .into()); } requires.insert( @@ -648,13 +631,10 @@ impl BaseCommand for BaseCommandData { opt_name: &str, ) -> anyhow::Result<String> { if !input.borrow().has_option(opt_name) { - return Err(LogicException { - message: format!( - "This should not be called on a Command which has no {} option defined.", - opt_name - ), - code: 0, - } + return Err(LogicException::new(format!( + "This should not be called on a Command which has no {} option defined.", + opt_name + )) .into()); } @@ -664,14 +644,11 @@ impl BaseCommand for BaseCommandData { .map(|s| PhpMixed::String(s.to_string())) .collect(); if !in_array_strict(val.clone(), &formats) { - return Err(InvalidArgumentException { - message: format!( - "--{} must be one of {}.", - opt_name, - Auditor::FORMATS.join(", ") - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "--{} must be one of {}.", + opt_name, + Auditor::FORMATS.join(", ") + )) .into()); } diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 46cc654f..e46cac8c 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -63,12 +63,11 @@ pub trait BaseDependencyCommand: BaseCommand { let mut locker = locker.borrow_mut(); if !locker.is_locked() { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: - "A valid composer.lock file is required to run this command with --locked" - .to_string(), - code: 0, - })); + return Err(UnexpectedValueException::new( + "A valid composer.lock file is required to run this command with --locked" + .to_string(), + ) + .into()); } repos.push(locker.get_locked_repository(true)?.into()); @@ -134,10 +133,11 @@ pub trait BaseDependencyCommand: BaseCommand { let packages = installed_repo.find_packages_with_replacers_and_providers(&needle, None)?; if packages.is_empty() { - return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("Could not find package \"{}\" in your project", needle), - code: 0, - })); + return Err(InvalidArgumentException::new(format!( + "Could not find package \"{}\" in your project", + needle + )) + .into()); } let matched_package = installed_repo.find_package( diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs index a4518b0b..51a779ef 100644 --- a/crates/shirabe/src/command/bump_command.rs +++ b/crates/shirabe/src/command/bump_command.rs @@ -305,10 +305,10 @@ impl BumpCommand { let contents = match file_get_contents(json.get_path()) { Some(c) => c, None => { - return Err(shirabe_php_shim::RuntimeException { - message: format!("Unable to read {} contents.", json.get_path()), - code: 0, - } + return Err(shirabe_php_shim::RuntimeException::new(format!( + "Unable to read {} contents.", + json.get_path() + )) .into()); } }; @@ -325,10 +325,10 @@ impl BumpCommand { match file_put_contents(json.get_path(), manipulator.get_contents().as_bytes()) { Some(_) => Ok(true), - None => Err(shirabe_php_shim::RuntimeException { - message: format!("Unable to write new {} contents.", json.get_path()), - code: 0, - } + None => Err(shirabe_php_shim::RuntimeException::new(format!( + "Unable to write new {} contents.", + json.get_path() + )) .into()), } } diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 2727f36b..3b976ba2 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -86,11 +86,10 @@ impl ConfigCommand { ) -> anyhow::Result<()> { let (validator, normalizer) = callbacks; if 1 != values.len() { - return Err(RuntimeException { - message: "You can only pass one value. Example: shirabe config process-timeout 300" + return Err(RuntimeException::new( + "You can only pass one value. Example: shirabe config process-timeout 300" .to_string(), - code: 0, - } + ) .into()); } @@ -101,10 +100,11 @@ impl ConfigCommand { } else { String::new() }; - return Err(RuntimeException { - message: format!("\"{}\" is an invalid value{}", values[0].clone(), suffix), - code: 0, - } + return Err(RuntimeException::new(format!( + "\"{}\" is an invalid value{}", + values[0].clone(), + suffix + )) .into()); } @@ -160,14 +160,11 @@ impl ConfigCommand { } else { String::new() }; - return Err(RuntimeException { - message: format!( - "{} is an invalid value{}", - PhpMixed::from(json_encode(&values_mixed).ok()), - suffix - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "{} is an invalid value{}", + PhpMixed::from(json_encode(&values_mixed).ok()), + suffix + )) .into()); } @@ -679,10 +676,9 @@ impl Command for ConfigCommand { .unwrap_or_default(); if !setting_values.is_empty() && input.borrow().get_option("unset")?.as_bool() == Some(true) { - return Err(RuntimeException { - message: "You can not combine a setting value with --unset".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "You can not combine a setting value with --unset".to_string(), + ) .into()); } @@ -731,10 +727,10 @@ impl Command for ConfigCommand { { Some(v) => v.clone(), None => { - return Err(InvalidArgumentException { - message: format!("There is no {} repository defined", repo_key), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "There is no {} repository defined", + repo_key + )) .into()); } }; @@ -766,11 +762,9 @@ impl Command for ConfigCommand { } if !r#match { - return Err(RuntimeException { - message: format!("{} is not defined.", setting_key), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("{} is not defined.", setting_key)).into(), + ); } value = cursor; @@ -847,11 +841,9 @@ impl Command for ConfigCommand { value = v.clone(); source = "defaults".to_string(); } else { - return Err(RuntimeException { - message: format!("{} is not defined", setting_key), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("{} is not defined", setting_key)).into(), + ); } let value_str = if is_array(&value) || is_object(&value) || is_bool(&value) { @@ -961,13 +953,10 @@ impl Command for ConfigCommand { .as_bool() .unwrap_or(false) { - return Err(RuntimeException { - message: format!( - "Invalid value for {}. Should be one of: auto, source, or dist", - setting_key - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for {}. Should be one of: auto, source, or dist", + setting_key + )) .into()); } @@ -998,10 +987,10 @@ impl Command for ConfigCommand { } if !boolean_validator(&PhpMixed::String(values[0].clone())) { - return Err(RuntimeException { - message: format!("\"{}\" is an invalid value", values[0].clone()), - code: 0, - } + return Err(RuntimeException::new(format!( + "\"{}\" is an invalid value", + values[0].clone() + )) .into()); } @@ -1025,10 +1014,7 @@ impl Command for ConfigCommand { || multi_props.contains_key(&setting_key) || strpos(&setting_key, "extra.") == Some(0)) { - return Err(InvalidArgumentException { - message: format!("The {} property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json", setting_key), - code: 0, - } + return Err(InvalidArgumentException::new(format!("The {} property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json", setting_key)) .into()); } if input.borrow().get_option("unset")?.as_bool() == Some(true) @@ -1122,10 +1108,7 @@ impl Command for ConfigCommand { } } - return Err(RuntimeException { - message: "You must pass the type and a url. Example: shirabe config repositories.foo vcs https://bar.com".to_string(), - code: 0, - } + return Err(RuntimeException::new("You must pass the type and a url. Example: shirabe config repositories.foo vcs https://bar.com".to_string()) .into()); } @@ -1308,10 +1291,10 @@ impl Command for ConfigCommand { if input.borrow().get_option("json")?.as_bool() == Some(true) { value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; if !is_array(&value) { - return Err(RuntimeException { - message: format!("Expected an array or object for {}", setting_key), - code: 0, - } + return Err(RuntimeException::new(format!( + "Expected an array or object for {}", + setting_key + )) .into()); } } @@ -1349,10 +1332,10 @@ impl Command for ConfigCommand { } value = PhpMixed::Array(merged); } else { - return Err(RuntimeException { - message: format!("Cannot merge array and object for {}", setting_key), - code: 0, - } + return Err(RuntimeException::new(format!( + "Cannot merge array and object for {}", + setting_key + )) .into()); } } @@ -1394,13 +1377,10 @@ impl Command for ConfigCommand { let key = format!("{}.{}", matches[1], matches[2]); if matches[1] == "bitbucket-oauth" { if 2 != values.len() { - return Err(RuntimeException { - message: format!( - "Expected two arguments (consumer-key, consumer-secret), got {}", - values.len() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Expected two arguments (consumer-key, consumer-secret), got {}", + values.len() + )) .into()); } self.config_source @@ -1441,10 +1421,9 @@ impl Command for ConfigCommand { "github-oauth" | "gitlab-oauth" | "gitlab-token" | "bearer" ) { if 1 != values.len() { - return Err(RuntimeException { - message: "Too many arguments, expected only one token".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Too many arguments, expected only one token".to_string(), + ) .into()); } self.config_source @@ -1459,13 +1438,10 @@ impl Command for ConfigCommand { .add_config_setting(&key, PhpMixed::String(values[0].clone())); } else if matches[1] == "http-basic" { if 2 != values.len() { - return Err(RuntimeException { - message: format!( - "Expected two arguments (username, password), got {}", - values.len() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Expected two arguments (username, password), got {}", + values.len() + )) .into()); } self.config_source @@ -1483,10 +1459,9 @@ impl Command for ConfigCommand { .add_config_setting(&key, PhpMixed::Array(obj)); } else if matches[1] == "custom-headers" { if values.is_empty() { - return Err(RuntimeException { - message: "Expected at least one argument (header), got none".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Expected at least one argument (header), got none".to_string(), + ) .into()); } @@ -1494,12 +1469,10 @@ impl Command for ConfigCommand { let mut formatted_headers: Vec<PhpMixed> = vec![]; for header in &values { if !is_string(&PhpMixed::String(header.clone())) { - return Err(RuntimeException { - message: - "Headers must be strings in \"Header-Name: Header-Value\" format" - .to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Headers must be strings in \"Header-Name: Header-Value\" format" + .to_string(), + ) .into()); } @@ -1510,13 +1483,10 @@ impl Command for ConfigCommand { header, Some(&mut header_parts), ) { - return Err(RuntimeException { - message: format!( - "Header \"{}\" is not in \"Header-Name: Header-Value\" format", - header - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Header \"{}\" is not in \"Header-Name: Header-Value\" format", + header + )) .into()); } @@ -1535,13 +1505,10 @@ impl Command for ConfigCommand { .add_config_setting(&key, PhpMixed::List(formatted_headers)); } else if matches[1] == "forgejo-token" { if 2 != values.len() { - return Err(RuntimeException { - message: format!( - "Expected two arguments (username, access token), got {}", - values.len() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Expected two arguments (username, access token), got {}", + values.len() + )) .into()); } self.config_source @@ -1604,13 +1571,10 @@ impl Command for ConfigCommand { return Ok(0); } - Err(InvalidArgumentException { - message: format!( - "Setting {} does not exist or is not supported by this command", - setting_key - ), - code: 0, - } + Err(InvalidArgumentException::new(format!( + "Setting {} does not exist or is not supported by this command", + setting_key + )) .into()) } 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::<PluginBlockedException>().is_some() { + if e.is_instanceof::<PluginBlockedException>() { io.write_error("<error>Hint: To allow running the config command recommended below before dependencies are installed, run create-project with --no-install.</error>"); io.write_error(&format!( "<error>You can then cd into {}, configure allow-plugins, and finally run a composer install to complete the process.</error>", @@ -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::<Vec<_>>() - ) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Invalid stability provided ({}), must be one of: {}", + stability, + implode( + ", ", + &STABILITIES + .keys() + .map(|k| k.to_string()) + .collect::<Vec<_>>() + ) + )) .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 = diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index 72b58dea..857e517d 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -38,11 +38,12 @@ 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::process::ExecutableFinder; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ - CmpOp, InvalidArgumentException, PHP_EOL, PhpMixed, disk_free_space, file_exists, - filter_var_boolean, get_class_err, hash, impl_php_class, implode, is_array, is_string, - php_regex, rtrim, str_contains, str_replace, str_starts_with, strpos, strstr, strstr3, - strtolower, trim, version_compare, + AnyThrowable, CmpOp, InvalidArgumentException, PHP_EOL, PhpClass as _, PhpMixed, + disk_free_space, file_exists, filter_var_boolean, hash, impl_php_class, implode, is_array, + is_string, php_regex, rtrim, str_contains, str_replace, str_starts_with, strpos, strstr, + strstr3, strtolower, trim, version_compare, }; #[derive(Debug)] @@ -106,7 +107,7 @@ impl DiagnoseCommand { match json.validate_schema(JsonFile::LOCK_SCHEMA, None) { Ok(_) => {} Err(e) => { - if let Some(jve) = e.downcast_ref::<JsonValidationException>() { + if let Some(jve) = e.catch::<JsonValidationException>() { let mut output = String::new(); for error in jve.get_errors() { output.push_str(&format!("<error>{}</error>{}", error, PHP_EOL)); @@ -191,7 +192,7 @@ impl DiagnoseCommand { ) { Ok(_) => {} Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { let hints = HttpDownloader::get_exception_hints(&e).unwrap_or_default(); if !hints.is_empty() { for hint in hints { @@ -202,7 +203,7 @@ impl DiagnoseCommand { result_list.push(PhpMixed::String(format!( "<error>[{}] {}</error>", std::any::type_name_of_val(te), - te.message + te.get_message() ))); } else { return Err(e); @@ -249,7 +250,7 @@ impl DiagnoseCommand { { Ok(_) => {} Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { let hints = HttpDownloader::get_exception_hints(&e).unwrap_or_default(); if !hints.is_empty() { for hint in hints { @@ -260,7 +261,7 @@ impl DiagnoseCommand { result_list.push(PhpMixed::String(format!( "<error>[{}] {}</error>", std::any::type_name_of_val(te), - te.message + te.get_message() ))); } else { return Err(e); @@ -394,7 +395,7 @@ impl DiagnoseCommand { ))) } Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_code() == 401 { return Ok(PhpMixed::String(format!( @@ -404,7 +405,9 @@ impl DiagnoseCommand { } Ok(PhpMixed::String(format!( "<error>[{}] {}</error>", - get_class_err(&e), + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), e ))) } @@ -540,13 +543,16 @@ impl DiagnoseCommand { Ok(Err(e)) => { return Ok(PhpMixed::String(format!( "<error>[{}] {}</error>", - "UnexpectedValueException", e.message + "UnexpectedValueException", + e.get_message() ))); } Err(e) => { return Ok(PhpMixed::String(format!( "<error>[{}] {}</error>", - get_class_err(&e), + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), e ))); } @@ -960,13 +966,10 @@ impl DiagnoseCommand { PHP_EOL, PHP_EOL ), other => { - return Err(InvalidArgumentException { - message: format!( - "DiagnoseCommand: Unknown error type \"{}\". Please report at https://github.com/composer/composer/issues/new.", - other, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "DiagnoseCommand: Unknown error type \"{}\". Please report at https://github.com/composer/composer/issues/new.", + other, + )) .into()); } }; @@ -1042,13 +1045,10 @@ impl DiagnoseCommand { PHP_EOL ), other => { - return Err(InvalidArgumentException { - message: format!( - "DiagnoseCommand: Unknown warning type \"{}\". Please report at https://github.com/composer/composer/issues/new.", - other, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "DiagnoseCommand: Unknown warning type \"{}\". Please report at https://github.com/composer/composer/issues/new.", + other, + )) .into()); } }; @@ -1409,13 +1409,19 @@ impl Command for DiagnoseCommand { Ok(()) })(); if let Err(e) = proxy_check_result { - if let Some(_te) = e.downcast_ref::<TransportException>() { + if let Some(_te) = e.catch::<TransportException>() { io.write_no_newline("Checking HTTP proxy: "); let status = self.check_connectivity_and_composer_network_http_enablement(); self.output_result(if is_string(&status) { status } else { - PhpMixed::String(format!("<error>[{}] {}</error>", get_class_err(&e), e)) + PhpMixed::String(format!( + "<error>[{}] {}</error>", + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), + e + )) }); } else { return Err(e); @@ -1455,20 +1461,24 @@ impl Command for DiagnoseCommand { } } Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { if te.get_code() == 401 { self.output_result(PhpMixed::String("<comment>The oauth token for github.com seems invalid, run \"composer config --global --unset github-oauth.github.com\" to remove it</comment>".to_string())); } else { self.output_result(PhpMixed::String(format!( "<error>[{}] {}</error>", - get_class_err(&e), + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), e ))); } } else { self.output_result(PhpMixed::String(format!( "<error>[{}] {}</error>", - get_class_err(&e), + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), e ))); } diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index 8aad0705..f9be4cff 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -149,10 +149,7 @@ impl Command for DumpAutoloadCommand { && !optimize && !authoritative { - return Err(InvalidArgumentException { - message: "--strict-psr mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new("--strict-psr mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string()) .into()); } if input @@ -163,10 +160,7 @@ impl Command for DumpAutoloadCommand { && !optimize && !authoritative { - return Err(InvalidArgumentException { - message: "--strict-ambiguous mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new("--strict-ambiguous mode only works with optimized autoloader, use --optimize or --classmap-authoritative if you want a strict return value.".to_string()) .into()); } @@ -208,12 +202,10 @@ impl Command for DumpAutoloadCommand { .as_bool() .unwrap_or(false) { - return Err(InvalidArgumentException { - message: - "You can not use both --no-dev and --dev as they conflict with each other." - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "You can not use both --no-dev and --dev as they conflict with each other." + .to_string(), + ) .into()); } composer diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs index d05b6c58..a67c234e 100644 --- a/crates/shirabe/src/command/exec_command.rs +++ b/crates/shirabe/src/command/exec_command.rs @@ -174,13 +174,10 @@ impl Command for ExecCommand { .as_string() .unwrap_or("") .to_string(); - return Err(RuntimeException { - message: format!( - "No binaries found in composer.json or in bin-dir ({})", - bin_dir - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "No binaries found in composer.json or in bin-dir ({})", + bin_dir + )) .into()); } @@ -222,9 +219,11 @@ impl Command for ExecCommand { if let Some(ref iwd) = initial_working_directory && getcwd().as_deref() != Some(iwd.as_str()) { - chdir(iwd).map_err(|e| RuntimeException { - message: format!("Could not switch back to working directory \"{}\"", iwd), - code: 0, + chdir(iwd).map_err(|e| { + RuntimeException::new(format!( + "Could not switch back to working directory \"{}\"", + iwd + )) })?; } diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs index 79037e55..78b6f3e6 100644 --- a/crates/shirabe/src/command/global_command.rs +++ b/crates/shirabe/src/command/global_command.rs @@ -61,11 +61,10 @@ impl GlobalCommand { } else if let Some(completion_input) = input_any.downcast_ref::<CompletionInput>() { Ok(completion_input.to_string()) } else { - Err(LogicException { - message: "Expected an Input instance that is stringable".to_string(), - code: 0, - } - .into()) + Err( + LogicException::new("Expected an Input instance that is stringable".to_string()) + .into(), + ) } } @@ -85,17 +84,14 @@ impl GlobalCommand { let mut fs = Filesystem::new(None); fs.ensure_directory_exists(&home)?; if !Path::new(&home).is_dir() { - return Err(RuntimeException { - message: "Could not create home directory".to_string(), - code: 0, - } - .into()); + return Err( + RuntimeException::new("Could not create home directory".to_string()).into(), + ); } } - chdir(&home).map_err(|_e| RuntimeException { - message: format!("Could not switch to home directory \"{}\"", home), - code: 0, + chdir(&home).map_err(|_e| { + RuntimeException::new(format!("Could not switch to home directory \"{}\"", home)) })?; if !quiet { diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index 5168b2ba..7b4012ce 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -25,6 +25,7 @@ use shirabe_external_packages::symfony::console::helper::FormatBlockMessages; use shirabe_external_packages::symfony::console::input::ArrayInput; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ FILE_IGNORE_NEW_LINES, InvalidArgumentException, PHP_EOL, PHP_SERVER, PhpMixed, array_flip_strings, array_intersect_key, array_map, basename, empty, explode, file, @@ -99,11 +100,9 @@ impl InitCommand { if let Some(ref email) = email && !self.is_valid_email(email) { - return Err(InvalidArgumentException { - message: format!("Invalid email \"{}\"", email), - code: 0, - } - .into()); + return Err( + InvalidArgumentException::new(format!("Invalid email \"{}\"", email)).into(), + ); } let mut result: IndexMap<String, Option<String>> = IndexMap::new(); @@ -121,11 +120,8 @@ impl InitCommand { return Ok(result); } - Err(InvalidArgumentException { - message: "Invalid author string. Must be in the formats: Jane Doe or John Smith <john@example.com>" - .to_string(), - code: 0, - } + Err(InvalidArgumentException::new("Invalid author string. Must be in the formats: Jane Doe or John Smith <john@example.com>" + .to_string()) .into()) } @@ -517,13 +513,10 @@ impl Command for InitCommand { .unwrap_or(""), ) { - return Err(InvalidArgumentException { - message: format!( - "The package name {} is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+", - options.get("name").and_then(|v| v.as_string()).unwrap_or("") - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The package name {} is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+", + options.get("name").and_then(|v| v.as_string()).unwrap_or("") + )) .into()); } @@ -678,7 +671,7 @@ impl Command for InitCommand { let validate_result = file_obj.validate_schema(JsonFile::LAX_SCHEMA, None); if let Err(e) = validate_result { // try to downcast to JsonValidationException - if let Some(json_err) = e.downcast_ref::<JsonValidationException>() { + if let Some(json_err) = e.catch::<JsonValidationException>() { io.write_error3( "<error>Schema validation error, aborting</error>", true, @@ -926,13 +919,10 @@ impl Command for InitCommand { php_regex!(r"{^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9](([_.]|-{1,2})?[a-z0-9]+)*$}D"), value.as_string().unwrap_or(""), ) { - return Err(InvalidArgumentException { - message: format!( - "The package name {} is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+", - value.as_string().unwrap_or("") - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The package name {} is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+", + value.as_string().unwrap_or("") + )) .into()); } @@ -1031,20 +1021,17 @@ impl Command for InitCommand { } if !base_package::STABILITIES.contains_key(value.as_string().unwrap_or("")) { - return Err(InvalidArgumentException { - message: format!( - "Invalid minimum stability \"{}\". Must be empty or one of: {}", - value.as_string().unwrap_or(""), - implode( - ", ", - &base_package::STABILITIES - .keys() - .map(|k| k.to_string()) - .collect::<Vec<_>>() - ) - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Invalid minimum stability \"{}\". Must be empty or one of: {}", + value.as_string().unwrap_or(""), + implode( + ", ", + &base_package::STABILITIES + .keys() + .map(|k| k.to_string()) + .collect::<Vec<_>>() + ) + )) .into()); } @@ -1107,13 +1094,10 @@ impl Command for InitCommand { && !spdx.validate(license.as_string().unwrap_or("")) && license.as_string() != Some("proprietary") { - return Err(InvalidArgumentException { - message: format!( + return Err(InvalidArgumentException::new(format!( "Invalid license provided: {}. Only SPDX license identifiers (https://spdx.org/licenses/) or \"proprietary\" are accepted.", license.as_string().unwrap_or("") - ), - code: 0, - } + )) .into()); } input.borrow_mut().set_option("license", license); @@ -1246,13 +1230,10 @@ impl Command for InitCommand { if !Preg::is_match(php_regex!(r"{^[^/][A-Za-z0-9\-_/]+/$}"), &value_or_default) { - return Err(InvalidArgumentException { - message: format!( - "The src folder name \"{}\" is invalid. Please add a relative path with tailing forward slash. [A-Za-z0-9_-/]+/", - value_or_default, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The src folder name \"{}\" is invalid. Please add a relative path with tailing forward slash. [A-Za-z0-9_-/]+/", + value_or_default, + )) .into()); } diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs index 41e9ba90..1b2ec6b6 100644 --- a/crates/shirabe/src/command/licenses_command.rs +++ b/crates/shirabe/src/command/licenses_command.rs @@ -128,10 +128,7 @@ impl Command for LicensesCommand { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); if !locker.is_locked() { - return Err(UnexpectedValueException { - message: "Valid composer.json and composer.lock files are required to run this command with --locked".to_string(), - code: 0, - }.into()); + return Err(UnexpectedValueException::new("Valid composer.json and composer.lock files are required to run this command with --locked".to_string()).into()); } let no_dev = input .borrow() @@ -314,13 +311,10 @@ impl Command for LicensesCommand { ); } _ => { - return Err(RuntimeException { - message: format!( - "Unsupported format \"{}\". See help for supported formats.", - format - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Unsupported format \"{}\". See help for supported formats.", + format + )) .into()); } } diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index db7db488..17332083 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -22,6 +22,7 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ Exception, InvalidArgumentException, LogicException, PHP_EOL, PhpMixed, array_keys, array_slice, asort, explode, file_get_contents, implode, in_array_strict, is_array, is_file, @@ -359,11 +360,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { )); } - Err(Exception { - message: "Not a valid selection".to_string(), - code: 0, - } - .into()) + Err(Exception::new("Not a valid selection".to_string()).into()) }, ); @@ -541,17 +538,14 @@ pub trait PackageDiscoveryTrait: BaseCommand { ShowWarnings::Always, )?; if let Some(candidate) = candidate { - return Err(InvalidArgumentException { - message: format!( - "Package {} has requirements incompatible with your PHP version, PHP extensions and Composer version{}", - name, - self.get_platform_exception_details( - candidate, - platform_repo, - )?, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} has requirements incompatible with your PHP version, PHP extensions and Composer version{}", + name, + self.get_platform_exception_details( + candidate, + platform_repo, + )?, + )) .into()); } } @@ -577,34 +571,28 @@ pub trait PackageDiscoveryTrait: BaseCommand { ShowWarnings::Always, )?; if let Some(all_repos_package) = all_repos_package { - return Err(InvalidArgumentException { - message: format!( - "Package {} exists in {} and {} which has a higher repository priority. The packages from the higher priority repository do not match your minimum-stability and are therefore not installable. That repository is canonical so the lower priority repo's packages are not installable. See https://getcomposer.org/repoprio for details and assistance.", - name, - all_repos_package - .get_repository() - .map(|r| r.get_repo_name()) - .transpose()? - .unwrap_or_default(), - package - .get_repository() - .map(|r| r.get_repo_name()) - .transpose()? - .unwrap_or_default(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} exists in {} and {} which has a higher repository priority. The packages from the higher priority repository do not match your minimum-stability and are therefore not installable. That repository is canonical so the lower priority repo's packages are not installable. See https://getcomposer.org/repoprio for details and assistance.", + name, + all_repos_package + .get_repository() + .map(|r| r.get_repo_name()) + .transpose()? + .unwrap_or_default(), + package + .get_repository() + .map(|r| r.get_repo_name()) + .transpose()? + .unwrap_or_default(), + )) .into()); } - return Err(InvalidArgumentException { - message: format!( - "Could not find a version of package {} matching your minimum-stability ({}). Require it with an explicit version constraint allowing its desired stability.", - name, - effective_minimum_stability, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Could not find a version of package {} matching your minimum-stability ({}). Require it with an explicit version constraint allowing its desired stability.", + name, + effective_minimum_stability, + )) .into()); } // Check whether the PHP version was the problem for all versions @@ -639,18 +627,15 @@ pub trait PackageDiscoveryTrait: BaseCommand { ); } - return Err(InvalidArgumentException { - message: format!( - "Could not find package {} in any version matching your PHP version, PHP extensions and Composer version{}{}", - name, - self.get_platform_exception_details( - candidate, - platform_repo, - )?, - additional, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Could not find package {} in any version matching your PHP version, PHP extensions and Composer version{}{}", + name, + self.get_platform_exception_details( + candidate, + platform_repo, + )?, + additional, + )) .into()); } } @@ -665,13 +650,10 @@ pub trait PackageDiscoveryTrait: BaseCommand { .map(|s| PhpMixed::String(s.clone())) .collect::<Vec<_>>(), ) { - return Err(InvalidArgumentException { - message: format!( - "Could not find package {}. It was however found via repository search, which indicates a consistency issue with the repository.", - name, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Could not find package {}. It was however found via repository search, which indicates a consistency issue with the repository.", + name, + )) .into()); } @@ -704,30 +686,24 @@ pub trait PackageDiscoveryTrait: BaseCommand { } } - return Err(InvalidArgumentException { - message: format!( - "Could not find package {}.\n\nDid you mean {}?\n {}", - name, - if similar.len() > 1 { - "one of these" - } else { - "this" - }, - implode("\n ", &similar), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Could not find package {}.\n\nDid you mean {}?\n {}", + name, + if similar.len() > 1 { + "one of these" + } else { + "this" + }, + implode("\n ", &similar), + )) .into()); } - return Err(InvalidArgumentException { - message: format!( - "Could not find a matching version of package {}. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability ({}).", - name, - effective_minimum_stability, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Could not find a matching version of package {}. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability ({}).", + name, + effective_minimum_stability, + )) .into()); } @@ -745,11 +721,9 @@ pub trait PackageDiscoveryTrait: BaseCommand { fn find_similar(&self, package: &str) -> anyhow::Result<Vec<String>> { let results: Vec<SearchResult> = match (|| -> anyhow::Result<Vec<SearchResult>> { if self.get_repos_mut().is_none() { - return Err(LogicException { - message: "findSimilar was called before $this->repos was initialized" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "findSimilar was called before $this->repos was initialized".to_string(), + ) .into()); } self.get_repos_mut() @@ -760,7 +734,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Ok(r) => r, Err(e) => { // PHP: if ($e instanceof \LogicException) throw $e; - if e.downcast_ref::<LogicException>().is_some() { + if e.is_instanceof::<LogicException>() { return Err(e); } diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index b16b6c92..f9753f2a 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -98,12 +98,10 @@ impl Command for ReinstallCommand { if type_count > 0 { if packages_count > 0 { - return Err(InvalidArgumentException { - message: - "You cannot specify package names and filter by type at the same time." - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "You cannot specify package names and filter by type at the same time." + .to_string(), + ) .into()); } let filter_types: Vec<String> = type_option @@ -122,11 +120,9 @@ impl Command for ReinstallCommand { } } else { if packages_count == 0 { - return Err(InvalidArgumentException { - message: "You must pass one or more package names to be reinstalled." - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "You must pass one or more package names to be reinstalled.".to_string(), + ) .into()); } let patterns: Vec<String> = packages_arg diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index 77deaf14..e89e9535 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -193,12 +193,10 @@ impl Command for RemoveCommand { .as_bool() .unwrap_or(false) { - return Err(anyhow::anyhow!(InvalidArgumentException( - shirabe_php_shim::InvalidArgumentException { - message: "Not enough arguments (missing: \"packages\").".to_string(), - code: 0, - } - ))); + return Err(InvalidArgumentException::new( + "Not enough arguments (missing: \"packages\").".to_string(), + ) + .into()); } let mut packages: Vec<String> = input @@ -224,12 +222,11 @@ impl Command for RemoveCommand { let locker = composer.get_locker().clone(); let mut locker = locker.borrow_mut(); if !locker.is_locked() { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: - "A valid composer.lock file is required to run this command with --unused" - .to_string(), - code: 0, - })); + return Err(UnexpectedValueException::new( + "A valid composer.lock file is required to run this command with --unused" + .to_string(), + ) + .into()); } } diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index ab2cf2d4..47fd2d28 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -362,26 +362,20 @@ impl Command for RepositoryCommand { } "add" => { if name.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "You must pass a repository name. Example: composer repo add foo vcs https://example.org".to_string(), - code: 0, - })); + return Err(RuntimeException::new("You must pass a repository name. Example: composer repo add foo vcs https://example.org".to_string()).into()); } if arg1.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "You must pass the type and a url, or a JSON string.".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "You must pass the type and a url, or a JSON string.".to_string(), + ) + .into()); } let arg1_str = arg1.as_deref().unwrap(); let repo_config: PhpMixed = if Preg::is_match(php_regex!(r"{^\s*\{}"), arg1_str) { JsonFile::parse_json(Some(arg1_str), None)? } else { if arg2.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "You must pass the type and a url. Example: composer repo add foo vcs https://example.org".to_string(), - code: 0, - })); + return Err(RuntimeException::new("You must pass the type and a url. Example: composer repo add foo vcs https://example.org".to_string()).into()); } let mut m = IndexMap::new(); m.insert("type".to_string(), PhpMixed::String(arg1_str.to_string())); @@ -400,19 +394,19 @@ impl Command for RepositoryCommand { .as_string() .map(|s| s.to_string()); if before.is_some() && after.is_some() { - return Err(anyhow::anyhow!(RuntimeException { - message: "You can not combine --before and --after".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "You can not combine --before and --after".to_string(), + ) + .into()); } if before.is_some() || after.is_some() { if matches!(repo_config, PhpMixed::Bool(false)) { - return Err(anyhow::anyhow!(RuntimeException { - message: "Cannot use --before/--after with boolean repository values" + return Err(RuntimeException::new( + "Cannot use --before/--after with boolean repository values" .to_string(), - code: 0, - })); + ) + .into()); } let reference_name = before.as_deref().or(after.as_deref()).unwrap(); let offset: i64 = if after.is_some() { 1 } else { 0 }; @@ -443,10 +437,10 @@ impl Command for RepositoryCommand { } "remove" | "rm" | "delete" => { if name.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "You must pass the repository name to remove.".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "You must pass the repository name to remove.".to_string(), + ) + .into()); } let name_str = name.as_deref().unwrap(); self.config_source @@ -465,10 +459,10 @@ impl Command for RepositoryCommand { } "set-url" | "seturl" => { if name.is_none() || arg1.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "Usage: composer repo set-url <name> <new-url>".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "Usage: composer repo set-url <name> <new-url>".to_string(), + ) + .into()); } self.config_source .borrow_mut() @@ -479,10 +473,10 @@ impl Command for RepositoryCommand { } "get-url" | "geturl" => { if name.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "Usage: composer repo get-url <name>".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "Usage: composer repo get-url <name>".to_string(), + ) + .into()); } let name_str = name.as_deref().unwrap(); if let Some(repo) = repos.get(name_str) @@ -493,10 +487,11 @@ impl Command for RepositoryCommand { self.get_io().write(url); return Ok(0); } - return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("The {} repository does not have a URL", name_str), - code: 0, - })); + return Err(InvalidArgumentException::new(format!( + "The {} repository does not have a URL", + name_str + )) + .into()); } for (_key, val) in &repos { if let PhpMixed::Array(ref repo_map) = *val @@ -508,23 +503,25 @@ impl Command for RepositoryCommand { self.get_io().write(url); return Ok(0); } - return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("The {} repository does not have a URL", name_str), - code: 0, - })); + return Err(InvalidArgumentException::new(format!( + "The {} repository does not have a URL", + name_str + )) + .into()); } } - Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("There is no {} repository defined", name_str), - code: 0, - })) + Err(InvalidArgumentException::new(format!( + "There is no {} repository defined", + name_str + )) + .into()) } "disable" => { if name.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "Usage: composer repo disable packagist.org".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "Usage: composer repo disable packagist.org".to_string(), + ) + .into()); } let name_str = name.as_deref().unwrap(); if ["packagist", "packagist.org"].contains(&name_str) { @@ -540,17 +537,14 @@ impl Command for RepositoryCommand { .add_repository("packagist.org", PhpMixed::Bool(false), append); return Ok(0); } - Err(anyhow::anyhow!(RuntimeException { - message: "Only packagist.org can be enabled/disabled using this command. Use add/remove for other repositories.".to_string(), - code: 0, - })) + Err(RuntimeException::new("Only packagist.org can be enabled/disabled using this command. Use add/remove for other repositories.".to_string()).into()) } "enable" => { if name.is_none() { - return Err(anyhow::anyhow!(RuntimeException { - message: "Usage: composer repo enable packagist.org".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "Usage: composer repo enable packagist.org".to_string(), + ) + .into()); } let name_str = name.as_deref().unwrap(); if ["packagist", "packagist.org"].contains(&name_str) { @@ -561,19 +555,16 @@ impl Command for RepositoryCommand { .remove_repository("packagist.org"); return Ok(0); } - Err(anyhow::anyhow!(RuntimeException { - message: "Only packagist.org can be enabled/disabled using this command." - .to_string(), - code: 0, - })) + Err(RuntimeException::new( + "Only packagist.org can be enabled/disabled using this command.".to_string(), + ) + .into()) } - _ => Err(anyhow::anyhow!(InvalidArgumentException { - message: format!( - "Unknown action \"{}\". Use list, add, remove, set-url, get-url, enable, disable", - action - ), - code: 0, - })), + _ => Err(InvalidArgumentException::new(format!( + "Unknown action \"{}\". Use list, add, remove, set-url, get-url, enable, disable", + action + )) + .into()), } } diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index 85e53a37..84b4a6e4 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -977,13 +977,10 @@ impl Command for RequireCommand { if self.newly_created.get() { self.revert_composer_file(); - return Err(RuntimeException { - message: format!( - "No composer.json present in the current directory ({}), this may be the cause of the following exception.", - self.file.borrow() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "No composer.json present in the current directory ({}), this may be the cause of the following exception.", + self.file.borrow() + )) .into()); } diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs index fe020de3..be50c4be 100644 --- a/crates/shirabe/src/command/run_script_command.rs +++ b/crates/shirabe/src/command/run_script_command.rs @@ -16,6 +16,7 @@ use shirabe_external_packages::symfony::console::exception::CommandNotFoundExcep use shirabe_external_packages::symfony::console::exception::namespace_not_found_exception::NamespaceNotFoundException; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{InvalidArgumentException, RuntimeException}; use shirabe_php_shim::{PhpMixed, impl_php_class}; @@ -102,8 +103,8 @@ impl RunScriptCommand { match application.borrow_mut().find(&name) { Ok(cmd) => description = cmd.borrow().get_description(), Err(e) - if e.downcast_ref::<CommandNotFoundException>().is_some() - || e.downcast_ref::<NamespaceNotFoundException>().is_some() => {} + if e.is_instanceof::<CommandNotFoundException>() + || e.is_instanceof::<NamespaceNotFoundException>() => {} Err(e) => return Err(e), } } @@ -255,10 +256,9 @@ impl Command for RunScriptCommand { let script = match input.borrow().get_argument("script")?.as_string() { None => { - return Err(RuntimeException { - message: "Missing required argument \"script\"".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Missing required argument \"script\"".to_string(), + ) .into()); } Some(s) => s.to_string(), @@ -267,10 +267,10 @@ impl Command for RunScriptCommand { if !self.script_events.contains(&script.as_str()) { let const_name = script.to_uppercase().replace('-', "_"); if ScriptEvents::is_defined(&const_name) { - return Err(InvalidArgumentException { - message: format!("Script \"{}\" cannot be run with this command", script), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Script \"{}\" cannot be run with this command", + script + )) .into()); } } @@ -299,10 +299,10 @@ impl Command for RunScriptCommand { ); let has_listeners = dispatcher.borrow_mut().has_event_listeners(&event); if !has_listeners { - return Err(InvalidArgumentException { - message: format!("Script \"{}\" is not defined in this package", script), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Script \"{}\" is not defined in this package", + script + )) .into()); } @@ -320,12 +320,10 @@ impl Command for RunScriptCommand { if let Some(timeout_val) = input.borrow().get_option("timeout")?.as_string() { let timeout_str = timeout_val.to_string(); if !timeout_str.chars().all(|c| c.is_ascii_digit()) { - return Err(RuntimeException { - message: - "Timeout value must be numeric and positive if defined, or 0 for forever" - .to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Timeout value must be numeric and positive if defined, or 0 for forever" + .to_string(), + ) .into()); } let timeout: i64 = timeout_str.parse().unwrap_or(0); diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs index e6a13545..e3faf3a6 100644 --- a/crates/shirabe/src/command/script_alias_command.rs +++ b/crates/shirabe/src/command/script_alias_command.rs @@ -36,12 +36,10 @@ impl ScriptAliasCommand { for alias in &aliases { if !is_string(&PhpMixed::String(alias.clone())) { - return Err(InvalidArgumentException { - message: - r#""scripts-aliases" element array values should contain only strings"# - .to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + r#""scripts-aliases" element array values should contain only strings"# + .to_string(), + ) .into()); } } @@ -123,10 +121,9 @@ impl Command for ScriptAliasCommand { // TODO(phase-c): InputInterface has_to_string/get_class_name not modeled in Rust // TODO remove for Symfony 6+ as it is then in the interface if false { - return Err(LogicException { - message: "Expected an Input instance that is stringable".to_string(), - code: 0, - } + return Err(LogicException::new( + "Expected an Input instance that is stringable".to_string(), + ) .into()); } diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs index 4bf75c8b..51432ed9 100644 --- a/crates/shirabe/src/command/search_command.rs +++ b/crates/shirabe/src/command/search_command.rs @@ -181,10 +181,9 @@ impl Command for SearchCommand { .as_bool() .unwrap_or(false) { - return Err(InvalidArgumentException { - message: "--only-name and --only-vendor cannot be used together".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "--only-name and --only-vendor cannot be used together".to_string(), + ) .into()); } mode = repository_interface::SEARCH_NAME; diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index aa63d8e7..6c693e62 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -359,14 +359,11 @@ impl ShowCommand { if let Some(ref mp) = matched_package && mp.as_complete().is_none() { - return Err(LogicException { - message: format!( - "ShowCommand::getPackage can only work with CompletePackageInterface, but got {}", - shirabe_php_shim::get_class(&PhpMixed::Null) - ), - code: 0, - } - .into()); + return Err(LogicException::new(format!( + "ShowCommand::getPackage can only work with CompletePackageInterface, but got {}", + shirabe_php_shim::get_class(&PhpMixed::Null) + )) + .into()); } let matched_package = matched_package.and_then(|mp| mp.as_complete()); @@ -1817,10 +1814,9 @@ impl Command for ShowCommand { .as_string() .is_some() { - return Err(InvalidArgumentException { - message: "You cannot use --self together with a package name".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "You cannot use --self together with a package name".to_string(), + ) .into()); } installed_repo = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ @@ -1931,10 +1927,7 @@ impl Command for ShowCommand { .borrow_mut() .is_locked() { - return Err(UnexpectedValueException { - message: "A valid composer.json and composer.lock files is required to run this command with --locked".to_string(), - code: 0, - } + return Err(UnexpectedValueException::new("A valid composer.json and composer.lock files is required to run this command with --locked".to_string()) .into()); } let composer_ref = crate::composer::composer_full(composer.as_ref().unwrap()); @@ -2090,14 +2083,11 @@ impl Command for ShowCommand { .collect::<Vec<_>>(), ) { - return Err(InvalidArgumentException { - message: format!( - "Package \"{}\" is installed but not a direct dependent of the root package.", - pkg.get_name() - ), - code: 0, - } - .into()); + return Err(InvalidArgumentException::new(format!( + "Package \"{}\" is installed but not a direct dependent of the root package.", + pkg.get_name() + )) + .into()); } if matched_package.is_none() { @@ -2123,10 +2113,10 @@ impl Command for ShowCommand { hint.push_str(", try using --available (-a) to show all available packages"); } - return Err(InvalidArgumentException { - message: format!("Package \"{}\" not found{}.", pf, hint), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package \"{}\" not found{}.", + pf, hint + )) .into()); } single_package = matched_package; diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index c928552a..02f32526 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -72,10 +72,9 @@ impl UpdateCommand { packages: Vec<String>, ) -> anyhow::Result<Vec<String>> { if !input.borrow().is_interactive() { - return Err(InvalidArgumentException { - message: "--interactive cannot be used in non-interactive terminals.".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "--interactive cannot be used in non-interactive terminals.".to_string(), + ) .into()); } @@ -165,10 +164,9 @@ impl UpdateCommand { } if autocompleter_values.is_empty() { - return Err(RuntimeException { - message: "Could not find any package with new versions available".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Could not find any package with new versions available".to_string(), + ) .into()); } @@ -210,11 +208,7 @@ impl UpdateCommand { return Ok(packages); } - Err(RuntimeException { - message: "Installation aborted.".to_string(), - code: 0, - } - .into()) + Err(RuntimeException::new("Installation aborted.".to_string()).into()) } fn create_version_selector( @@ -450,10 +444,9 @@ impl Command for UpdateCommand { .unwrap_or(false) { if !composer.get_locker().borrow_mut().is_locked() { - return Err(InvalidArgumentException { - message: "patch-only can only be used with a lock file present".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "patch-only can only be used with a lock file present".to_string(), + ) .into()); } for package in composer diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs index 154fbd05..300b9bfa 100644 --- a/crates/shirabe/src/config.rs +++ b/crates/shirabe/src/config.rs @@ -658,10 +658,10 @@ impl Config { &raw, Some(&mut matches), ) { - return Err(RuntimeException { - message: format!("Could not parse the value of '{}': {}", key, raw), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse the value of '{}': {}", + key, raw + )) .into()); } let mut size = matches @@ -735,13 +735,10 @@ impl Config { PhpMixed::String("symlink".to_string()), ], ) { - return Err(RuntimeException { - message: format!( - "Invalid value for 'bin-compat': {}. Expected auto, full or proxy", - value - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for 'bin-compat': {}. Expected auto, full or proxy", + value + )) .into()); } @@ -760,13 +757,10 @@ impl Config { if !matches!(env, PhpMixed::Bool(false)) { let env_str = env.as_string().unwrap_or("").to_string(); if !matches!(env_str.as_str(), "stash" | "true" | "false" | "1" | "0") { - return Err(RuntimeException { - message: format!( - "Invalid value for COMPOSER_DISCARD_CHANGES: {}. Expected 1, 0, true, false or stash", - env_str - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for COMPOSER_DISCARD_CHANGES: {}. Expected 1, 0, true, false or stash", + env_str + )) .into()); } if env_str == "stash" { @@ -782,13 +776,10 @@ impl Config { let val = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); let allowed = matches!(&val, PhpMixed::Bool(_)) || val.as_string() == Some("stash"); if !allowed { - return Err(RuntimeException { - message: format!( - "Invalid value for 'discard-changes': {:?}. Expected true, false or stash", - val - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for 'discard-changes': {:?}. Expected true, false or stash", + val + )) .into()); } @@ -824,10 +815,7 @@ impl Config { } let first = protos.first().cloned(); if first.as_deref() == Some("http") { - return Err(RuntimeException { - message: "The http protocol for github is not available anymore, update your config's github-protocols to use \"https\", \"git\" or \"ssh\"".to_string(), - code: 0, - } + return Err(RuntimeException::new("The http protocol for github is not available anymore, update your config's github-protocols to use \"https\", \"git\" or \"ssh\"".to_string()) .into()); } @@ -860,14 +848,11 @@ impl Config { .map(|s| PhpMixed::String(s.clone())) .collect::<Vec<_>>(), ) { - return Err(RuntimeException { - message: format!( - "Invalid value for COMPOSER_AUDIT_ABANDONED: {}. Expected one of {}.", - abandoned_env_str, - implode(", ", &valid_choices), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for COMPOSER_AUDIT_ABANDONED: {}. Expected one of {}.", + abandoned_env_str, + implode(", ", &valid_choices), + )) .into()); } if let PhpMixed::Array(ref mut m) = result { @@ -880,13 +865,10 @@ impl Config { if !matches!(block_abandoned_env, PhpMixed::Bool(false)) { let env_str = block_abandoned_env.as_string().unwrap_or("").to_string(); if !matches!(env_str.as_str(), "0" | "1") { - return Err(RuntimeException { - message: format!( - "Invalid value for COMPOSER_SECURITY_BLOCKING_ABANDONED: {}. Expected 0 or 1.", - env_str - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid value for COMPOSER_SECURITY_BLOCKING_ABANDONED: {}. Expected 0 or 1.", + env_str + )) .into()); } if let PhpMixed::Array(ref mut m) = result { diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs index 53e3722b..0257ee31 100644 --- a/crates/shirabe/src/config/json_config_source.rs +++ b/crates/shirabe/src/config/json_config_source.rs @@ -7,6 +7,7 @@ use crate::json::JsonValidationException; use crate::util::Filesystem; use crate::util::Silencer; use indexmap::IndexMap; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PHP_EOL, PhpMixed, RuntimeException, chmod, explode, file_get_contents, file_put_contents, implode, is_writable, @@ -32,24 +33,18 @@ impl JsonConfigSource { let contents; if self.file.borrow().exists() { if !is_writable(self.file.borrow().get_path()) { - return Err(RuntimeException { - message: format!( - "The file \"{}\" is not writable.", - self.file.borrow().get_path(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The file \"{}\" is not writable.", + self.file.borrow().get_path(), + )) .into()); } if !Filesystem::is_readable(self.file.borrow().get_path()) { - return Err(RuntimeException { - message: format!( - "The file \"{}\" is not readable.", - self.file.borrow().get_path(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The file \"{}\" is not readable.", + self.file.borrow().get_path(), + )) .into()); } @@ -148,19 +143,16 @@ impl JsonConfigSource { { Ok(_) => {} Err(e) => { - let Some(jve) = e.downcast_ref::<JsonValidationException>() else { + let Some(jve) = e.catch::<JsonValidationException>() else { return Err(e); }; // restore contents to the original state file_put_contents(self.file.borrow().get_path(), contents.as_bytes()); - return Err(RuntimeException { - message: format!( - "Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json). {}{}", - PHP_EOL, - implode(PHP_EOL, jve.get_errors()), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json). {}{}", + PHP_EOL, + implode(PHP_EOL, jve.get_errors()), + )) .into()); } } @@ -397,13 +389,10 @@ impl ConfigSourceInterface for JsonConfigSource { } } let Some(index_to_insert) = index_to_insert else { - return Err(RuntimeException { - message: format!( - "The referenced repository \"{}\" does not exist.", - reference_name, - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The referenced repository \"{}\" does not exist.", + reference_name, + )) .into()); }; diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index 96f80d9b..16a67b72 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -93,6 +93,7 @@ use shirabe_external_packages::symfony::console::style::style_interface::StyleIn use shirabe_external_packages::symfony::console::style::symfony_style::SymfonyStyle; use shirabe_external_packages::symfony::console::terminal::Terminal; use shirabe_external_packages::symfony::process::exception::ProcessTimedOutException; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ LogicException as ShimLogicException, PHP_VERSION, PHP_VERSION_ID, PhpMixed, RuntimeException, bin2hex, chdir, date_default_timezone_get, date_default_timezone_set, defined, dirname, @@ -232,13 +233,10 @@ impl Application { if let Some(ref wd) = working_dir && !is_dir(wd) { - return Err(RuntimeException { - message: format!( - "Invalid working directory specified, {} does not exist.", - wd - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid working directory specified, {} does not exist.", + wd + )) .into()); } @@ -250,8 +248,8 @@ impl Application { exception: &anyhow::Error, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) { - let is_logic_or_error = exception.downcast_ref::<ShimLogicException>().is_some(); - if is_logic_or_error + if (exception.is_class::<ShimLogicException>() + || exception.is_instanceof::<shirabe_php_shim::Error>()) && output.borrow().get_verbosity() < output_interface::VERBOSITY_VERBOSE { output @@ -308,7 +306,7 @@ impl Application { } let message = exception.to_string(); - if exception.downcast_ref::<TransportException>().is_some() + if exception.is_instanceof::<TransportException>() && str_contains(&message, "Unable to use a proxy") { io.write_error3( @@ -320,7 +318,7 @@ impl Application { } if Platform::is_windows() - && exception.downcast_ref::<TransportException>().is_some() + && exception.is_instanceof::<TransportException>() && str_contains(&message, "unable to get local issuer certificate") { let avast_detect = glob("C:\\Program Files\\Avast*"); @@ -359,10 +357,7 @@ impl Application { io.write_error3("<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>", true, io_interface::QUIET); } - if exception - .downcast_ref::<ProcessTimedOutException>() - .is_some() - { + if exception.is_instanceof::<ProcessTimedOutException>() { io.write_error3( "<error>The following exception is caused by a process timeout</error>", true, @@ -376,9 +371,7 @@ impl Application { && !self.io.is_interactive() { io.write_error3("<error>Plugins have been disabled automatically as you are running as root, this may be the cause of the following exception. See also https://getcomposer.org/root</error>", true, io_interface::QUIET); - } else if exception - .downcast_ref::<CommandNotFoundException>() - .is_some() + } else if exception.is_instanceof::<CommandNotFoundException>() && self.get_disable_plugins_by_default() { io.write_error3("<error>Plugins have been disabled, which may be why some commands are missing, unless you made a typo</error>", true, io_interface::QUIET); @@ -416,9 +409,7 @@ impl Application { match Factory::create(io_for_factory, None, disable_plugins_enum, disable_scripts) { Ok(c) => self.composer = Some(c.upcast()), Err(e) => { - if e.downcast_ref::<shirabe_php_shim::InvalidArgumentException>() - .is_some() - { + if e.is_instanceof::<shirabe_php_shim::InvalidArgumentException>() { if required { self.io.write_error(&e.to_string()); if self.are_exceptions_caught() { @@ -429,11 +420,11 @@ impl Application { } return Err(e); } - } else if e.downcast_ref::<JsonValidationException>().is_some() - || e.downcast_ref::<RuntimeException>().is_some() + } else if e.is_instanceof::<JsonValidationException>() + || e.is_instanceof::<RuntimeException>() // PHP's `catch (RuntimeException)` also catches subclasses; // NoSslException is the one Factory::create raises. - || e.downcast_ref::<NoSslException>().is_some() + || e.is_instanceof::<NoSslException>() { if required { return Err(e); @@ -523,7 +514,7 @@ impl Application { Ok(()) => {} Err(e) => { // Errors must be ignored, full binding/validation happens later when the command is known. - if !is_exception_interface(&e) { + if !throwable_is_exception_interface(e.as_ref()) { return Err(e); } } @@ -712,10 +703,7 @@ impl Application { pub fn get_signal_registry(&self) -> anyhow::Result<&SignalRegistry> { match &self.signal_registry { - None => Err(ConsoleRuntimeException(shirabe_php_shim::RuntimeException { - message: "Signals are not supported. Make sure that the `pcntl` extension is installed and that \"pcntl_*\" functions are not disabled by your php.ini's \"disable_functions\" directive.".to_string(), - code: 0, - }) + None => Err(ConsoleRuntimeException::new("Signals are not supported. Make sure that the `pcntl` extension is installed and that \"pcntl_*\" functions are not disabled by your php.ini's \"disable_functions\" directive.".to_string()) .into()), Some(signal_registry) => Ok(signal_registry), } @@ -988,17 +976,12 @@ impl Application { message.push_str(&shirabe_php_shim::implode("\n ", &alternatives)); } - return Err(NamespaceNotFoundException(CommandNotFoundException::new( - message, - alternatives, - 0, - )) - .into()); + return Err(NamespaceNotFoundException::new(message, alternatives, 0).into()); } let exact = namespaces.iter().any(|n| n == namespace); if namespaces.len() > 1 && !exact { - return Err(NamespaceNotFoundException(CommandNotFoundException::new( + return Err(NamespaceNotFoundException::new( format!( "The namespace \"{}\" is ambiguous.\nDid you mean one of these?\n{}.", namespace, @@ -1006,7 +989,7 @@ impl Application { ), namespaces, 0, - )) + ) .into()); } @@ -1936,13 +1919,10 @@ impl ApplicationHandle { command.borrow().get_definition(); if command.borrow().get_name().is_none() { - return Err(ConsoleLogicException(shirabe_php_shim::LogicException { - message: format!( - "The command defined in \"{}\" cannot have an empty name.", - shirabe_php_shim::get_debug_type_obj(&command), - ), - code: 0, - }) + return Err(ConsoleLogicException::new(format!( + "The command defined in \"{}\" cannot have an empty name.", + shirabe_php_shim::get_debug_type_obj(&command), + )) .into()); } @@ -2057,7 +2037,7 @@ impl ApplicationHandle { command_name = cmd.borrow().get_name(); } Err(e) => { - if e.downcast_ref::<CommandNotFoundException>().is_some() { + if e.is_instanceof::<CommandNotFoundException>() { // we'll check command validity again later after plugins are loaded command_name = None; } @@ -2223,9 +2203,9 @@ impl ApplicationHandle { })() { Ok(_) => {} Err(e) => { - if e.downcast_ref::<NoSslException>().is_some() { + if e.is_instanceof::<NoSslException>() { // suppress these as they are not relevant at this point - } else if let Some(pe) = e.downcast_ref::<ParsingException>() { + } else if let Some(pe) = e.catch::<ParsingException>() { let details = pe.get_details(); let file = realpath(Factory::get_composer_file().unwrap_or_default()); @@ -2233,7 +2213,7 @@ impl ApplicationHandle { let line = details.line; let mut ghe = GithubActionError::new(io.clone()); - ghe.emit(&pe.message, file.as_deref(), line); + ghe.emit(pe.get_message(), file.as_deref(), line); return Err(e); } else { @@ -2542,7 +2522,7 @@ impl ApplicationHandle { let outcome = match result_outcome { Ok(r) => Ok(r), - Err(e) => { + Err(mut e) => { // PHP's `exit` bypasses parent::doRun()'s catch entirely; re-raise it untouched so // the GitHub Actions annotation and error hints below are skipped. if e.downcast_ref::<shirabe_php_shim::ExitException>() @@ -2550,7 +2530,7 @@ impl ApplicationHandle { { return Err(e); } - if let Some(see) = e.downcast_ref::<ScriptExecutionException>() { + if let Some(see) = e.catch::<ScriptExecutionException>() { if application.borrow().get_disable_plugins_by_default() && application.borrow().is_running_as_root() && !io.is_interactive() @@ -2572,15 +2552,9 @@ impl ApplicationHandle { // override TransportException's code for the purpose of parent::run() using it as process exit code // as http error codes are all beyond the 255 range of permitted exit codes - // TODO(phase-c): PHP's `instanceof TransportException` also matches the subclass - // MaxFileSizeExceededException, which is a newtype here and is not matched by this downcast. - let e = match e.downcast::<TransportException>() { - Ok(mut e) => { - e.code = Installer::ERROR_TRANSPORT_EXCEPTION; - anyhow::Error::new(e) - } - Err(e) => e, - }; + if let Some(te) = e.catch_mut::<TransportException>() { + te.set_code(Installer::ERROR_TRANSPORT_EXCEPTION); + } Err(e) } @@ -2691,9 +2665,7 @@ impl ApplicationHandle { // $exitCode = $e->getCode(); // is_numeric($exitCode) ? max(1, (int) $exitCode) : 1 - // TODO(phase-c): anyhow::Error has no PHP-style getCode(); the exit code derived - // from the exception's `code` field needs the downcast strategy decided. - let exit_code = shirabe_php_shim::php_exception_get_code(&e); + let exit_code = throwable_get_code(e.as_ref()) as i32; if shirabe_php_shim::is_numeric_string(&exit_code.to_string()) { if exit_code <= 0 { 1 } else { exit_code } } else { @@ -2735,7 +2707,7 @@ impl ApplicationHandle { Ok(()) => {} Err(e) => { // Errors must be ignored, full binding/validation happens later when the command is known. - if !is_exception_interface(&e) { + if !throwable_is_exception_interface(e.as_ref()) { return Err(e); } } @@ -2814,8 +2786,9 @@ impl ApplicationHandle { Err(e) => { // if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) // || 1 !== count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) - let alternatives: Option<Vec<String>> = downcast_command_not_found(&e) - .filter(|_| !is_namespace_not_found(&e)) + let alternatives: Option<Vec<String>> = e + .catch::<CommandNotFoundException>() + .filter(|_| !e.is_instanceof::<NamespaceNotFoundException>()) .map(|cnf| cnf.get_alternatives().clone()); let single_alternative = match &alternatives { @@ -2907,10 +2880,7 @@ impl ApplicationHandle { if !command_signals.is_empty() { if application.borrow().signal_registry.is_none() { - return Err(ConsoleRuntimeException(shirabe_php_shim::RuntimeException { - message: "Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that \"pcntl_*\" functions are not disabled by your php.ini's \"disable_functions\" directive.".to_string(), - code: 0, - }) + return Err(ConsoleRuntimeException::new("Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that \"pcntl_*\" functions are not disabled by your php.ini's \"disable_functions\" directive.".to_string()) .into()); } @@ -2994,133 +2964,35 @@ impl BaseApplication for Application { } } -/// Helper mirroring PHP's `$e instanceof ExceptionInterface`. -fn is_exception_interface(e: &anyhow::Error) -> bool { - // anyhow::Error stores concrete error types; enumerate the console exceptions - // that implement ExceptionInterface (PHP's `$e instanceof ExceptionInterface`). - e.downcast_ref::<CommandNotFoundException>().is_some() - || e.downcast_ref::<NamespaceNotFoundException>().is_some() - || e.downcast_ref::<ConsoleLogicException>().is_some() - || e.downcast_ref::<ConsoleRuntimeException>().is_some() - || e.downcast_ref::<ConsoleInvalidArgumentException>() - .is_some() - || e.downcast_ref::<InvalidOptionException>().is_some() - || e.downcast_ref::<MissingInputException>().is_some() -} - -/// `is_exception_interface` for a node of the `anyhow::Error` source chain (`&dyn Error`), used -/// while walking the getPrevious() chain in `do_render_throwable`. +/// PHP's `$e instanceof ExceptionInterface`, enumerating the console exceptions that implement it. +// TODO(plugin): a plugin can throw an exception class of its own that implements +// ExceptionInterface, and no enumeration on this side can name it. Answering that needs the +// interfaces a thrown exception implements, not just its superclasses. fn throwable_is_exception_interface(e: &(dyn std::error::Error + 'static)) -> bool { - e.downcast_ref::<CommandNotFoundException>().is_some() - || e.downcast_ref::<NamespaceNotFoundException>().is_some() - || e.downcast_ref::<ConsoleLogicException>().is_some() - || e.downcast_ref::<ConsoleRuntimeException>().is_some() - || e.downcast_ref::<ConsoleInvalidArgumentException>() - .is_some() - || e.downcast_ref::<InvalidOptionException>().is_some() - || e.downcast_ref::<MissingInputException>().is_some() + shirabe_php_shim::AnyThrowable::of(e).is_some_and(|e| { + e.is_instanceof::<CommandNotFoundException>() + || e.is_instanceof::<NamespaceNotFoundException>() + || e.is_instanceof::<ConsoleLogicException>() + || e.is_instanceof::<ConsoleRuntimeException>() + || e.is_instanceof::<ConsoleInvalidArgumentException>() + || e.is_instanceof::<InvalidOptionException>() + || e.is_instanceof::<MissingInputException>() + }) } -/// PHP's `$e->getCode()` for a node of the source chain. Enumerates the flat standard exception -/// structs that carry a `code`; everything else defaults to PHP's 0. +/// PHP's `$e->getCode()` for a node of the source chain; everything the port does not recognize as +/// an exception defaults to PHP's 0. fn throwable_get_code(e: &(dyn std::error::Error + 'static)) -> i64 { - if let Some(e) = e.downcast_ref::<shirabe_php_shim::Exception>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::RuntimeException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::UnexpectedValueException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::InvalidArgumentException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::TypeError>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::LogicException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::BadMethodCallException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::OutOfBoundsException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::ErrorException>() { - return e.code; - } - if let Some(e) = e.downcast_ref::<shirabe_php_shim::PharException>() { - return e.code; - } - 0 + shirabe_php_shim::AnyThrowable::of(e).map_or(0, shirabe_php_shim::AnyThrowable::get_code) } /// PHP's `get_debug_type($e)` for the title line, reached only when the message is empty or output -/// is verbose. PHP returns the exception's fully-qualified class name; Rust has no runtime FQCN, so -/// this maps the enumerable exception types to their PHP class names and falls back to `Exception`. -/// TODO(phase-c): the fully-qualified name (e.g. `Composer\...`) cannot be reproduced faithfully. +/// is verbose. fn throwable_debug_type(e: &(dyn std::error::Error + 'static)) -> String { - let name = if e - .downcast_ref::<shirabe_php_shim::RuntimeException>() - .is_some() - { - "RuntimeException" - } else if e - .downcast_ref::<shirabe_php_shim::UnexpectedValueException>() - .is_some() - { - "UnexpectedValueException" - } else if e - .downcast_ref::<shirabe_php_shim::InvalidArgumentException>() - .is_some() - { - "InvalidArgumentException" - } else if e.downcast_ref::<shirabe_php_shim::TypeError>().is_some() { - "TypeError" - } else if e - .downcast_ref::<shirabe_php_shim::LogicException>() - .is_some() - { - "LogicException" - } else if e - .downcast_ref::<shirabe_php_shim::BadMethodCallException>() - .is_some() - { - "BadMethodCallException" - } else if e - .downcast_ref::<shirabe_php_shim::OutOfBoundsException>() - .is_some() - { - "OutOfBoundsException" - } else if e - .downcast_ref::<shirabe_php_shim::ErrorException>() - .is_some() - { - "ErrorException" - } else if e - .downcast_ref::<shirabe_php_shim::PharException>() - .is_some() - { - "PharException" - } else { - "Exception" - }; - name.to_string() -} - -/// Helper mirroring PHP's `$e instanceof CommandNotFoundException`. -fn downcast_command_not_found(e: &anyhow::Error) -> Option<&CommandNotFoundException> { - if let Some(cnf) = e.downcast_ref::<CommandNotFoundException>() { - return Some(cnf); - } - e.downcast_ref::<NamespaceNotFoundException>().map(|n| &n.0) -} - -/// Helper mirroring PHP's `$e instanceof NamespaceNotFoundException`. -fn is_namespace_not_found(e: &anyhow::Error) -> bool { - e.downcast_ref::<NamespaceNotFoundException>().is_some() + shirabe_php_shim::AnyThrowable::of(e).map_or_else( + || "Exception".to_string(), + shirabe_php_shim::PhpClass::php_class_name, + ) } /// Borrows the shared input as a mutable `dyn InputInterface` for passing to @@ -3161,13 +3033,11 @@ pub(crate) fn register_worker_reverse_application( pub(crate) fn run_worker_reverse_command(name: &str, input_line: &str) -> anyhow::Result<i64> { let application = WORKER_REVERSE_APPLICATION .with(|slot| slot.borrow().as_ref().and_then(std::rc::Weak::upgrade)) - .ok_or_else(|| { - anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( - "cannot run command {name}: no application is registered for worker callbacks" - ), - code: 0, - }) + .ok_or_else(|| -> anyhow::Error { + shirabe_php_shim::RuntimeException::new(format!( + "cannot run command {name}: no application is registered for worker callbacks" + )) + .into() })?; let command = application.borrow_mut().find(name)?; // PHP's `(string) $input` omits the command name when the input was built without an diff --git a/crates/shirabe/src/dependency_resolver/decisions.rs b/crates/shirabe/src/dependency_resolver/decisions.rs index 81f00884..ac596054 100644 --- a/crates/shirabe/src/dependency_resolver/decisions.rs +++ b/crates/shirabe/src/dependency_resolver/decisions.rs @@ -102,14 +102,11 @@ impl Decisions { panic!( "{}", - LogicException { - message: format!( - "Did not find a decision rule using {}", - literal_or_package_id - ), - code: 0, - } - .message + LogicException::new(format!( + "Did not find a decision rule using {}", + literal_or_package_id + )) + .get_message() ); } @@ -171,7 +168,7 @@ impl Decisions { literal_string, level, package, previous_decision )) .0 - .message + .get_message() ); } diff --git a/crates/shirabe/src/dependency_resolver/generic_rule.rs b/crates/shirabe/src/dependency_resolver/generic_rule.rs index 8b64dc46..3f098f01 100644 --- a/crates/shirabe/src/dependency_resolver/generic_rule.rs +++ b/crates/shirabe/src/dependency_resolver/generic_rule.rs @@ -44,11 +44,7 @@ impl GenericRule { let binary = hash_raw(algo, &joined); match binary.get(..4) { Some(chunk) => Ok(i32::from_ne_bytes(chunk.try_into().unwrap()) as i64), - None => Err(RuntimeException { - message: format!("Failed unpacking: {}", joined), - code: 0, - } - .into()), + None => Err(RuntimeException::new(format!("Failed unpacking: {}", joined)).into()), } } diff --git a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs index 0936dd0c..d43d9a79 100644 --- a/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs +++ b/crates/shirabe/src/dependency_resolver/multi_conflict_rule.rs @@ -16,10 +16,9 @@ impl MultiConflictRule { reason_data: ReasonData, ) -> anyhow::Result<Self> { if literals.len() < 3 { - return Err(RuntimeException { - message: "multi conflict rule requires at least 3 literals".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "multi conflict rule requires at least 3 literals".to_string(), + ) .into()); } @@ -59,11 +58,7 @@ impl MultiConflictRule { let binary = hash_raw(algo, &format!("c:{}", joined)); match binary.get(..4) { Some(chunk) => Ok(i32::from_ne_bytes(chunk.try_into().unwrap()) as i64), - None => Err(RuntimeException { - message: format!("Failed unpacking: {}", joined), - code: 0, - } - .into()), + None => Err(RuntimeException::new(format!("Failed unpacking: {}", joined)).into()), } } diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index 35699a90..f5143897 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -147,11 +147,9 @@ impl PoolBuilder { self.warn_about_non_matching_update_allow_list(request)?; if request.get_locked_repository().is_none() { - return Err(LogicException { - message: "No lock repo present and yet a partial update was requested." - .to_string(), - code: 0, - } + return Err(LogicException::new( + "No lock repo present and yet a partial update was requested.".to_string(), + ) .into()); } @@ -798,10 +796,9 @@ impl PoolBuilder { fn warn_about_non_matching_update_allow_list(&self, request: &Request) -> anyhow::Result<()> { if request.get_locked_repository().is_none() { - return Err(LogicException { - message: "No lock repo present and yet a partial update was requested.".to_string(), - code: 0, - } + return Err(LogicException::new( + "No lock repo present and yet a partial update was requested.".to_string(), + ) .into()); } diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs index 3c69babe..3b9feba5 100644 --- a/crates/shirabe/src/dependency_resolver/problem.rs +++ b/crates/shirabe/src/dependency_resolver/problem.rs @@ -82,10 +82,9 @@ impl Problem { let rule_ref = rule.borrow(); if rule_ref.get_reason() != rule::RULE_ROOT_REQUIRE { - return Err(LogicException { - message: "Single reason problems must contain a root require rule.".to_string(), - code: 0, - } + return Err(LogicException::new( + "Single reason problems must contain a root require rule.".to_string(), + ) .into()); } diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs index d78900c0..410831dd 100644 --- a/crates/shirabe/src/dependency_resolver/request.rs +++ b/crates/shirabe/src/dependency_resolver/request.rs @@ -47,15 +47,12 @@ impl Request { let package_name = strtolower(package_name); let constraint = constraint.unwrap_or_else(|| MatchAllConstraint::new(None).into()); if self.requires.contains_key(&package_name) { - return Err(LogicException { - message: format!( - "Overwriting requires seems like a bug ({} {} => {}, check why it is happening, might be a root alias", - package_name, - self.requires[&package_name].get_pretty_string(), - constraint.get_pretty_string() - ), - code: 0, - } + return Err(LogicException::new(format!( + "Overwriting requires seems like a bug ({} {} => {}, check why it is happening, might be a root alias", + package_name, + self.requires[&package_name].get_pretty_string(), + constraint.get_pretty_string() + )) .into()); } self.requires.insert(package_name, constraint); diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs index 0ed442ce..5e009260 100644 --- a/crates/shirabe/src/dependency_resolver/rule.rs +++ b/crates/shirabe/src/dependency_resolver/rule.rs @@ -166,10 +166,7 @@ impl Rule { pub fn disable(&mut self) -> anyhow::Result<()> { if let Rule::MultiConflict(_) = self { - return Err(RuntimeException { - message: "Disabling multi conflict rules is not possible. Please contact composer at https://github.com/composer/composer to let us debug what lead to this situation.".to_string(), - code: 0, - } + return Err(RuntimeException::new("Disabling multi conflict rules is not possible. Please contact composer at https://github.com/composer/composer to let us debug what lead to this situation.".to_string()) .into()); } *self.bitfield_mut() = @@ -291,11 +288,7 @@ impl Rule { Ok(source_package) } - _ => Err(LogicException { - message: "Not implemented".to_string(), - code: 0, - } - .into()), + _ => Err(LogicException::new("Not implemented".to_string()).into()), } } diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs index f7300d93..50f7cb45 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set.rs @@ -59,11 +59,7 @@ impl RuleSet { ) -> anyhow::Result<()> { let types = Self::types(); if !types.contains_key(&r#type) { - return Err(OutOfBoundsException { - message: format!("Unknown rule type: {}", r#type), - code: 0, - } - .into()); + return Err(OutOfBoundsException::new(format!("Unknown rule type: {}", r#type)).into()); } let hash = rule.borrow().get_hash()?.to_string(); diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs index 2f4837d4..6f2213dc 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs @@ -309,13 +309,11 @@ impl RuleSetGenerator { } // otherwise, looks like a bug - return Err(anyhow::anyhow!(shirabe_php_shim::LogicException { - message: format!( - "Fixed package {} was not added to solver pool.", - package.get_pretty_string() - ), - code: 0, - })); + return Err(shirabe_php_shim::LogicException::new(format!( + "Fixed package {} was not added to solver pool.", + package.get_pretty_string() + )) + .into()); } self.add_rules_for_package(package.clone(), platform_requirement_filter); diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs index dd5bd383..a9453a3e 100644 --- a/crates/shirabe/src/dependency_resolver/solver.rs +++ b/crates/shirabe/src/dependency_resolver/solver.rs @@ -381,10 +381,11 @@ impl Solver { let (learn_literal, new_level, new_rule, why) = self.analyze(level, rule)?; if new_level <= 0 || new_level >= level { - return Err(anyhow::anyhow!(SolverBugException::new(format!( + return Err(SolverBugException::new(format!( "Trying to revert to invalid level {} from level {}.", new_level, level - )))); + )) + .into()); } level = new_level; @@ -505,12 +506,12 @@ impl Solver { let inner_literal = loop { if decision_id <= 0 { - return Err(anyhow::anyhow!(SolverBugException::new(format!( + return Err(SolverBugException::new(format!( "Reached invalid decision id {} while looking through {} for a literal present in the analyzed rule {}.", decision_id, rule.borrow(), analyzed_rule.borrow() - )))); + )).into()); } decision_id -= 1; @@ -587,10 +588,11 @@ impl Solver { let learned_literal = match learned_literal { Some(l) => l, None => { - return Err(anyhow::anyhow!(SolverBugException::new(format!( + return Err(SolverBugException::new(format!( "Did not find a learnable literal in analyzed rule {}.", analyzed_rule.borrow() - )))); + )) + .into()); } }; diff --git a/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs b/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs index d509ef6a..9c47d99b 100644 --- a/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs +++ b/crates/shirabe/src/dependency_resolver/solver_bug_exception.rs @@ -12,17 +12,12 @@ impl SolverBugException { Please report the command you ran, the exact error you received, and your composer.json on https://github.com/composer/composer/issues - thank you!\n", message ); - SolverBugException(RuntimeException { - message: full_message, - code: 0, - }) + SolverBugException(RuntimeException::new(full_message)) } } -impl std::fmt::Display for SolverBugException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for SolverBugException {} +shirabe_php_shim::impl_php_exception!( + SolverBugException, + 0, + r"Composer\DependencyResolver\SolverBugException" +); diff --git a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs index 39f2b2c1..4c948608 100644 --- a/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs +++ b/crates/shirabe/src/dependency_resolver/solver_problems_exception.rs @@ -18,14 +18,6 @@ pub struct SolverProblemsException { impl SolverProblemsException { pub const ERROR_DEPENDENCY_RESOLUTION_FAILED: i64 = 2; - pub fn get_code(&self) -> i64 { - self.inner.code - } - - pub fn get_message(&self) -> &str { - &self.inner.message - } - pub fn new( problems: Vec<Problem>, learned_pool: Vec<Vec<std::rc::Rc<std::cell::RefCell<Rule>>>>, @@ -35,10 +27,7 @@ impl SolverProblemsException { problems.len() ); Self { - inner: RuntimeException { - message, - code: Self::ERROR_DEPENDENCY_RESOLUTION_FAILED, - }, + inner: RuntimeException::with_code(message, Self::ERROR_DEPENDENCY_RESOLUTION_FAILED), problems, learned_pool, } @@ -170,10 +159,11 @@ impl SolverProblemsException { } } -impl std::fmt::Display for SolverProblemsException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.inner.message) - } -} - -impl std::error::Error for SolverProblemsException {} +// `learned_pool` holds `Rc<RefCell<Rule>>`, so this exception rides an inner `Result` rather than +// an `anyhow::Error`. +shirabe_php_shim::impl_php_exception!( + SolverProblemsException, + inner, + r"Composer\DependencyResolver\SolverProblemsException", + !Send +); diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs index 6ddf29b6..d2ee4a75 100644 --- a/crates/shirabe/src/downloader/archive_downloader.rs +++ b/crates/shirabe/src/downloader/archive_downloader.rs @@ -9,6 +9,7 @@ use crate::util::Filesystem; use crate::util::Platform; use indexmap::IndexMap; use shirabe_external_packages::symfony::finder::Finder; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ DIRECTORY_SEPARATOR, PhpMixed, RuntimeException, bin2hex, file_exists, is_dir, random_bytes, realpath, @@ -155,7 +156,7 @@ pub trait ArchiveDownloader { Ok(false) => {} Err(e) => { // ignore error, and simply do not renameAsOne - if e.downcast_ref::<RuntimeException>().is_none() { + if !e.is_instanceof::<RuntimeException>() { return Err(e); } } @@ -273,14 +274,11 @@ fn rename_recursively( ); if is_dir(&target) { if !is_dir(file) { - return Err(RuntimeException { - message: format!( - "Installing {} would lead to overwriting the {} directory with a file from the package, invalid operation.", - package, - target.display() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Installing {} would lead to overwriting the {} directory with a file from the package, invalid operation.", + package, + target.display() + )) .into()); } rename_recursively(filesystem, package.clone(), file, &target)?; diff --git a/crates/shirabe/src/downloader/download_manager.rs b/crates/shirabe/src/downloader/download_manager.rs index 423eda28..8c10de5d 100644 --- a/crates/shirabe/src/downloader/download_manager.rs +++ b/crates/shirabe/src/downloader/download_manager.rs @@ -9,6 +9,7 @@ use crate::package::PackageInterfaceHandle; use crate::util::Filesystem; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_keys, array_reverse, array_shift, dirname, implode, in_array_strict, preg_quote, rtrim, str_replace, @@ -99,14 +100,11 @@ impl DownloadManager { ) -> anyhow::Result<std::rc::Rc<std::cell::RefCell<dyn DownloaderInterface>>> { let r#type = strtolower(r#type); if !self.downloaders.contains_key(&r#type) { - return Err(InvalidArgumentException { - message: format!( - "Unknown downloader type: {}. Available types: {}.", - r#type, - implode(", ", &array_keys(&self.downloaders)), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Unknown downloader type: {}. Available types: {}.", + r#type, + implode(", ", &array_keys(&self.downloaders)), + )) .into()); } @@ -134,28 +132,22 @@ impl DownloadManager { } else if installation_source.as_deref() == Some("source") { self.get_downloader(&package.get_source_type().unwrap_or_default())? } else { - return Err(InvalidArgumentException { - message: format!( - "Package {} does not have an installation source set", - package, - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} does not have an installation source set", + package, + )) .into()); }; let downloader_installation_source = downloader.borrow().get_installation_source(); if installation_source.as_deref() != Some(&downloader_installation_source) { - return Err(LogicException { - message: format!( - "Downloader \"{}\" is a {} type downloader and can not be used to download {} for package {}", - downloader.borrow().php_class_name(), - downloader_installation_source, - installation_source.unwrap_or_default(), - package, - ), - code: 0, - } + return Err(LogicException::new(format!( + "Downloader \"{}\" is a {} type downloader and can not be used to download {} for package {}", + downloader.borrow().php_class_name(), + downloader_installation_source, + installation_source.unwrap_or_default(), + package, + )) .into()); } @@ -227,19 +219,18 @@ impl DownloadManager { { Ok(r) => r, Err(e) => { - let is_runtime = e.downcast_ref::<RuntimeException>().is_some(); - let is_irrecoverable = - e.downcast_ref::<IrrecoverableDownloadException>().is_some(); - if is_runtime && !is_irrecoverable { + if e.is_instanceof::<RuntimeException>() + && !e.is_instanceof::<IrrecoverableDownloadException>() + { if sources.is_empty() { return Err(e); } let message = e - .downcast_ref::<RuntimeException>() + .catch::<RuntimeException>() .unwrap() - .message - .clone(); + .get_message() + .to_string(); self.io.write_error3( &format!( " <warning>Failed to download {} from {}: {}</warning>", @@ -352,17 +343,17 @@ impl DownloadManager { Ok(p) => return Ok(p), Err(e) => { // PHP catches only \RuntimeException; other exceptions propagate uncaught. - if e.downcast_ref::<RuntimeException>().is_none() { + if !e.is_instanceof::<RuntimeException>() { return Err(e); } if !self.io.is_interactive() { return Err(e); } let message = e - .downcast_ref::<RuntimeException>() + .catch::<RuntimeException>() .unwrap() - .message - .clone(); + .get_message() + .to_string(); self.io.write_error3( &format!("<error> Update failed ({})</error>", message), true, @@ -477,10 +468,10 @@ impl DownloadManager { } if sources.is_empty() { - return Err(InvalidArgumentException { - message: format!("Package {} must have a source or dist specified", package), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} must have a source or dist specified", + package + )) .into()); } diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs index cdcf750e..9b86ee56 100644 --- a/crates/shirabe/src/downloader/file_downloader.rs +++ b/crates/shirabe/src/downloader/file_downloader.rs @@ -25,6 +25,7 @@ use crate::util::Silencer; use crate::util::Url as UrlUtil; use crate::util::sync_executor; use indexmap::IndexMap; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ DIRECTORY_SEPARATOR, InvalidArgumentException, PATHINFO_BASENAME, PATHINFO_EXTENSION, PHP_URL_PATH, PhpMixed, RuntimeException, UnexpectedValueException, array_search, file_exists, @@ -359,11 +360,9 @@ impl FileDownloader { url: &str, ) -> anyhow::Result<String> { if !shirabe_php_shim::extension_loaded("openssl") && Some(0) == strpos(url, "https:") { - return Err(RuntimeException { - message: "You must enable the openssl extension to download files via https" - .to_string(), - code: 0, - } + return Err(RuntimeException::new( + "You must enable the openssl extension to download files via https".to_string(), + ) .into()); } @@ -400,10 +399,9 @@ impl DownloaderInterface for FileDownloader { output: bool, ) -> anyhow::Result<Option<PhpMixed>> { if package.get_dist_url().is_none() { - return Err(InvalidArgumentException { - message: "The given package is missing url information".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "The given package is missing url information".to_string(), + ) .into()); } @@ -558,15 +556,15 @@ impl DownloaderInterface for FileDownloader { } self.clear_last_cache_write(package.clone()); - if e.downcast_ref::<IrrecoverableDownloadException>().is_some() { + if e.is_instanceof::<IrrecoverableDownloadException>() { return Err(e); } - if e.downcast_ref::<MaxFileSizeExceededException>().is_some() { + if e.is_instanceof::<MaxFileSizeExceededException>() { return Err(e); } - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { // if we got an http response with a proper code, then requesting again will probably not help, abort if 0 != te.get_code() && !matches!(te.get_code(), 500 | 502 | 503 | 504) { @@ -592,7 +590,7 @@ impl DownloaderInterface for FileDownloader { } if !urls.is_empty() { let code = e - .downcast_ref::<TransportException>() + .catch::<TransportException>() .map_or(0, |te| te.get_code()); if self.io.borrow().is_debug() { self.io.borrow().write_error(&format!( @@ -628,13 +626,10 @@ impl DownloaderInterface for FileDownloader { // === $result->then(verify) === if !file_exists(&file_name) { - return Err(UnexpectedValueException { - message: format!( - "{} could not be saved to {}, make sure the directory is writable and you have internet connectivity", - url.base, file_name - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "{} could not be saved to {}, make sure the directory is writable and you have internet connectivity", + url.base, file_name + )) .into()); } @@ -642,13 +637,10 @@ impl DownloaderInterface for FileDownloader { && !checksum.is_empty() && hash_file("sha1", &file_name).as_deref() != Some(checksum) { - return Err(UnexpectedValueException { - message: format!( - "The checksum verification of the file failed (downloaded from {})", - url.base - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "The checksum verification of the file failed (downloaded from {})", + url.base + )) .into()); } @@ -809,10 +801,10 @@ impl DownloaderInterface for FileDownloader { } let result = Filesystem::remove_directory_async_via(&self.filesystem, path).await?; if !result { - return Err(RuntimeException { - message: format!("Could not completely delete {}, aborting.", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not completely delete {}, aborting.", + path + )) .into()); } diff --git a/crates/shirabe/src/downloader/filesystem_exception.rs b/crates/shirabe/src/downloader/filesystem_exception.rs index f0aa831f..6e90e328 100644 --- a/crates/shirabe/src/downloader/filesystem_exception.rs +++ b/crates/shirabe/src/downloader/filesystem_exception.rs @@ -7,17 +7,15 @@ pub struct FilesystemException(pub Exception); impl FilesystemException { pub fn new(message: String, code: i64) -> Self { - FilesystemException(Exception { - message: format!("Filesystem exception: \n{}", message), + FilesystemException(Exception::with_code( + format!("Filesystem exception: \n{}", message), code, - }) + )) } } -impl std::fmt::Display for FilesystemException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for FilesystemException {} +shirabe_php_shim::impl_php_exception!( + FilesystemException, + 0, + r"Composer\Downloader\FilesystemException" +); diff --git a/crates/shirabe/src/downloader/fossil_downloader.rs b/crates/shirabe/src/downloader/fossil_downloader.rs index 72409a1f..86a44f0b 100644 --- a/crates/shirabe/src/downloader/fossil_downloader.rs +++ b/crates/shirabe/src/downloader/fossil_downloader.rs @@ -47,14 +47,11 @@ impl FossilDownloader { .execute(&command, output, cwd.as_deref())? != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - command.join(" "), - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + command.join(" "), + self.inner.process.borrow().get_error_output() + )) .into()); } Ok(()) @@ -168,13 +165,10 @@ impl VcsDownloader for FossilDownloader { )); if !self.has_metadata_repository(path) { - return Err(RuntimeException { - message: format!( - "The .fslckout file is missing from {}, see https://getcomposer.org/commit-deps for more information", - path - ), - code: 0, - }.into()); + return Err(RuntimeException::new(format!( + "The .fslckout file is missing from {}, see https://getcomposer.org/commit-deps for more information", + path + )).into()); } let real_path = shirabe_php_shim::realpath(path); diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs index 275050fd..0a32d2fd 100644 --- a/crates/shirabe/src/downloader/git_downloader.rs +++ b/crates/shirabe/src/downloader/git_downloader.rs @@ -86,14 +86,11 @@ impl GitDownloader { .execute_args(&command, &mut output, Some(&path)) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - implode(" ", &command), - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + implode(" ", &command), + self.inner.process.borrow().get_error_output(), + )) .into()); } @@ -186,14 +183,11 @@ impl GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - implode(" ", &command), - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + implode(" ", &command), + self.inner.process.borrow().get_error_output(), + )) .into()); } @@ -232,14 +226,11 @@ impl GitDownloader { .execute_args(&command, &mut output, Some(&path)) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - implode(" ", &command), - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + implode(" ", &command), + self.inner.process.borrow().get_error_output(), + )) .into()); } refs = trim(&output, None); @@ -486,15 +477,12 @@ impl GitDownloader { let command = format!("{} && {}", implode(" ", &command1), implode(" ", &command2)); - Err(RuntimeException { - message: Url::sanitize(format!( - "Failed to execute {}\n\n{}{}", - command, - self.inner.process.borrow().get_error_output(), - exception_extra, - )), - code: 0, - } + Err(RuntimeException::new(Url::sanitize(format!( + "Failed to execute {}\n\n{}{}", + command, + self.inner.process.borrow().get_error_output(), + exception_extra, + ))) .into()) } @@ -570,11 +558,9 @@ impl GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!("Could not reset changes\n\n:{}", output), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("Could not reset changes\n\n:{}", output)).into(), + ); } let mut output = String::new(); if self.inner.process.borrow_mut().execute_args( @@ -583,11 +569,9 @@ impl GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!("Could not reset changes\n\n:{}", output), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("Could not reset changes\n\n:{}", output)).into(), + ); } self.has_discarded_changes.borrow_mut().insert(path, true); @@ -609,11 +593,9 @@ impl GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!("Could not stash changes\n\n:{}", output), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("Could not stash changes\n\n:{}", output)).into(), + ); } self.has_stashed_changes.borrow_mut().insert(path, true); @@ -631,11 +613,9 @@ impl GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!("Could not view diff\n\n:{}", output), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("Could not view diff\n\n:{}", output)).into(), + ); } self.inner @@ -692,10 +672,10 @@ impl GitDownloader { path: &str, ) -> anyhow::Result<()> { if self.get_local_changes(package, path)?.is_some() { - return Err(RuntimeException { - message: format!("Source directory {} has uncommitted changes.", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Source directory {} has uncommitted changes.", + path + )) .into()); } @@ -738,14 +718,11 @@ impl ChangeReportInterface for GitDownloader { .execute_args(&command, &mut output, Some(path)) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - implode(" ", &command), - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + implode(" ", &command), + self.inner.process.borrow().get_error_output(), + )) .into()); } @@ -848,10 +825,9 @@ impl VcsDownloader for GitDownloader { .insert(r#ref.as_deref().unwrap_or("").to_string(), true); } } else if git_version.is_none() { - return Err(RuntimeException { - message: "git was not found in your PATH, skipping source download".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "git was not found in your PATH, skipping source download".to_string(), + ) .into()); } @@ -975,13 +951,10 @@ impl VcsDownloader for GitDownloader { ], ]; if Platform::get_env("COMPOSER_DISABLE_NETWORK").is_some() { - return Err(RuntimeException { - message: format!( - "The required git reference for {} is not in cache and network is disabled, aborting", - package.get_name(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The required git reference for {} is not in cache and network is disabled, aborting", + package.get_name(), + )) .into()); } } @@ -1022,13 +995,10 @@ impl VcsDownloader for GitDownloader { GitUtil::clean_env(&self.inner.process); let path = self.normalize_path(path); if !self.has_metadata_repository(&path) { - return Err(RuntimeException { - message: format!( - "The .git directory is missing from {}, see https://getcomposer.org/commit-deps for more information", - path - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The .git directory is missing from {}, see https://getcomposer.org/commit-deps for more information", + path + )) .into()); } @@ -1060,13 +1030,10 @@ impl VcsDownloader for GitDownloader { msg = format!("Checking out {}", self.get_short_hash(&r#ref)); remote_url = "%url%".to_string(); if Platform::get_env("COMPOSER_DISABLE_NETWORK").is_some() { - return Err(RuntimeException { - message: format!( - "The required git reference for {} is not in cache and network is disabled, aborting", - target.get_name(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The required git reference for {} is not in cache and network is disabled, aborting", + target.get_name(), + )) .into()); } } @@ -1196,13 +1163,10 @@ impl VcsDownloader for GitDownloader { .as_bool() != Some(true)) { - return Err(RuntimeException { - message: format!( - "Source directory {} has unpushed changes on the current branch: \n{}", - path, unpushed - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Source directory {} has unpushed changes on the current branch: \n{}", + path, unpushed + )) .into()); } @@ -1285,11 +1249,7 @@ impl VcsDownloader for GitDownloader { } } Some("n") => { - return Err(RuntimeException { - message: "Update aborted".to_string(), - code: 0, - } - .into()); + return Err(RuntimeException::new("Update aborted".to_string()).into()); } Some("v") => { self.inner @@ -1362,13 +1322,10 @@ impl VcsDownloader for GitDownloader { Some(&path), ) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to apply stashed changes:\n\n{}", - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to apply stashed changes:\n\n{}", + self.inner.process.borrow().get_error_output() + )) .into()); } } @@ -1399,14 +1356,11 @@ impl VcsDownloader for GitDownloader { .execute_args(&command, &mut output, Some(&path)) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - implode(" ", &command), - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + implode(" ", &command), + self.inner.process.borrow().get_error_output(), + )) .into()); } diff --git a/crates/shirabe/src/downloader/gzip_downloader.rs b/crates/shirabe/src/downloader/gzip_downloader.rs index b0494479..9473deff 100644 --- a/crates/shirabe/src/downloader/gzip_downloader.rs +++ b/crates/shirabe/src/downloader/gzip_downloader.rs @@ -127,10 +127,7 @@ impl ArchiveDownloader for GzipDownloader { implode(" ", &command), self.inner.process.borrow().get_error_output(), ); - return Err(anyhow::anyhow!(RuntimeException { - message: process_error, - code: 0 - })); + return Err(RuntimeException::new(process_error).into()); } self.extract_using_ext(file, &target_filepath); diff --git a/crates/shirabe/src/downloader/hg_downloader.rs b/crates/shirabe/src/downloader/hg_downloader.rs index e555ccf5..dfd25618 100644 --- a/crates/shirabe/src/downloader/hg_downloader.rs +++ b/crates/shirabe/src/downloader/hg_downloader.rs @@ -64,10 +64,9 @@ impl VcsDownloader for HgDownloader { _prev_package: Option<PackageInterfaceHandle>, ) -> anyhow::Result<Option<PhpMixed>> { if HgUtils::get_version(&self.inner.process).is_none() { - return Err(RuntimeException { - message: "hg was not found in your PATH, skipping source download".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "hg was not found in your PATH, skipping source download".to_string(), + ) .into()); } @@ -111,14 +110,11 @@ impl VcsDownloader for HgDownloader { shirabe_php_shim::realpath(path).as_deref(), ) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - command.join(" "), - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + command.join(" "), + self.inner.process.borrow().get_error_output() + )) .into()); } @@ -145,13 +141,10 @@ impl VcsDownloader for HgDownloader { )); if !self.has_metadata_repository(path) { - return Err(RuntimeException { - message: format!( - "The .hg directory is missing from {}, see https://getcomposer.org/commit-deps for more information", - path - ), - code: 0, - }.into()); + return Err(RuntimeException::new(format!( + "The .hg directory is missing from {}, see https://getcomposer.org/commit-deps for more information", + path + )).into()); } let pull_command = |url: String| -> Vec<String> { @@ -195,14 +188,11 @@ impl VcsDownloader for HgDownloader { shirabe_php_shim::realpath(path).as_deref(), ) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - command.join(" "), - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + command.join(" "), + self.inner.process.borrow().get_error_output() + )) .into()); } diff --git a/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs b/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs index ebec74df..7a730ff2 100644 --- a/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs +++ b/crates/shirabe/src/downloader/max_file_size_exceeded_exception.rs @@ -1,6 +1,6 @@ //! ref: composer/src/Composer/Downloader/MaxFileSizeExceededException.php -use crate::downloader::TransportException; +use crate::downloader::transport_exception::TransportException; #[derive(Debug)] pub struct MaxFileSizeExceededException(pub TransportException); @@ -11,10 +11,8 @@ impl MaxFileSizeExceededException { } } -impl std::fmt::Display for MaxFileSizeExceededException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for MaxFileSizeExceededException {} +shirabe_php_shim::impl_php_exception!( + MaxFileSizeExceededException, + 0, + r"Composer\Downloader\MaxFileSizeExceededException" +); diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs index f10da72a..9449e74a 100644 --- a/crates/shirabe/src/downloader/path_downloader.rs +++ b/crates/shirabe/src/downloader/path_downloader.rs @@ -85,17 +85,14 @@ impl PathDownloader { package: PackageInterfaceHandle, path: &str, ) -> anyhow::Result<String> { - let url = package.get_dist_url().ok_or_else(|| RuntimeException { - message: format!( + let url = package.get_dist_url().ok_or_else(|| { + RuntimeException::new(format!( "The package {} has no dist url configured, cannot install.", package.get_pretty_name() - ), - code: 0, - })?; - let real_url = realpath(&url).ok_or_else(|| RuntimeException { - message: format!("Failed to realpath {}", url), - code: 0, + )) })?; + let real_url = realpath(&url) + .ok_or_else(|| RuntimeException::new(format!("Failed to realpath {}", url)))?; if realpath(path).as_deref() == Some(&real_url) { return Ok(": Source already present".to_string()); @@ -157,10 +154,7 @@ impl PathDownloader { && !self.safe_junctions() { if !allowed_strategies.contains(&Self::STRATEGY_MIRROR) { - return Err(RuntimeException { - message: "You are on an old Windows / old PHP combo which does not allow Composer to use junctions/symlinks and this path repository has symlink:true in its options so copying is not allowed".to_string(), - code: 0, - } + return Err(RuntimeException::new("You are on an old Windows / old PHP combo which does not allow Composer to use junctions/symlinks and this path repository has symlink:true in its options so copying is not allowed".to_string()) .into()); } current_strategy = Self::STRATEGY_MIRROR; @@ -173,10 +167,7 @@ impl PathDownloader { && !function_exists("symlink") { if !allowed_strategies.contains(&Self::STRATEGY_MIRROR) { - return Err(RuntimeException { - message: "Your PHP has the symlink() function disabled which does not allow Composer to use symlinks and this path repository has symlink:true in its options so copying is not allowed".to_string(), - code: 0, - } + return Err(RuntimeException::new("Your PHP has the symlink() function disabled which does not allow Composer to use symlinks and this path repository has symlink:true in its options so copying is not allowed".to_string()) .into()); } current_strategy = Self::STRATEGY_MIRROR; @@ -243,26 +234,22 @@ impl DownloaderInterface for PathDownloader { output: bool, ) -> anyhow::Result<Option<PhpMixed>> { let path = Filesystem::trim_trailing_slash(path); - let url = package.get_dist_url().ok_or_else(|| RuntimeException { - message: format!( + let url = package.get_dist_url().ok_or_else(|| { + RuntimeException::new(format!( "The package {} has no dist url configured, cannot download.", package.get_pretty_name() - ), - code: 0, + )) })?; let real_url = realpath(&url); if real_url.is_none() || !file_exists(real_url.as_deref().unwrap_or("")) || !is_dir(real_url.as_deref().unwrap_or("")) { - return Err(RuntimeException { - message: format!( - "Source path \"{}\" is not found for package {}", - url, - package.get_name() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Source path \"{}\" is not found for package {}", + url, + package.get_name() + )) .into()); } let real_url = real_url.unwrap(); @@ -282,15 +269,12 @@ impl DownloaderInterface for PathDownloader { // // Please see https://github.com/composer/composer/pull/5974 and https://github.com/composer/composer/pull/6174 // for previous attempts that were shut down because they did not work well enough or introduced too many risks. - return Err(RuntimeException { - message: format!( - "Package {} cannot install to \"{}\" inside its source at \"{}\"", - package.get_name(), - realpath(&path).unwrap_or_default(), - real_url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Package {} cannot install to \"{}\" inside its source at \"{}\"", + package.get_name(), + realpath(&path).unwrap_or_default(), + real_url + )) .into()); } @@ -316,17 +300,14 @@ impl DownloaderInterface for PathDownloader { output: bool, ) -> anyhow::Result<Option<PhpMixed>> { let path = Filesystem::trim_trailing_slash(path); - let url = package.get_dist_url().ok_or_else(|| RuntimeException { - message: format!( + let url = package.get_dist_url().ok_or_else(|| { + RuntimeException::new(format!( "The package {} has no dist url configured, cannot install.", package.get_pretty_name() - ), - code: 0, - })?; - let real_url = realpath(&url).ok_or_else(|| RuntimeException { - message: format!("Failed to realpath {}", url), - code: 0, + )) })?; + let real_url = realpath(&url) + .ok_or_else(|| RuntimeException::new(format!("Failed to realpath {}", url)))?; if realpath(&path).as_deref() == Some(&real_url) { if output { @@ -442,13 +423,10 @@ impl DownloaderInterface for PathDownloader { current_strategy = Self::STRATEGY_MIRROR; is_fallback = true; } else { - return Err(RuntimeException { - message: format!( - "Symlink from \"{}\" to \"{}\" failed!", - real_url, path - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Symlink from \"{}\" to \"{}\" failed!", + real_url, path + )) .into()); } } @@ -537,25 +515,21 @@ impl DownloaderInterface for PathDownloader { true, io_interface::NORMAL, ); - return Err(RuntimeException { - message: format!( - "Could not reliably remove junction for package {}", - package.get_name() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not reliably remove junction for package {}", + package.get_name() + )) .into()); } return Ok(None); } - let url = package.get_dist_url().ok_or_else(|| RuntimeException { - message: format!( + let url = package.get_dist_url().ok_or_else(|| { + RuntimeException::new(format!( "The package {} has no dist url configured, cannot remove.", package.get_pretty_name() - ), - code: 0, + )) })?; // ensure that the source path (dist url) is not the same as the install path, which diff --git a/crates/shirabe/src/downloader/rar_downloader.rs b/crates/shirabe/src/downloader/rar_downloader.rs index ee0148c7..f4f601bc 100644 --- a/crates/shirabe/src/downloader/rar_downloader.rs +++ b/crates/shirabe/src/downloader/rar_downloader.rs @@ -114,39 +114,30 @@ impl ArchiveDownloader for RarDownloader { process_error.as_deref().unwrap_or(""), ) }; - return Err(RuntimeException { - message: error, - code: 0, - } - .into()); + return Err(RuntimeException::new(error).into()); } let rar_archive = RarArchive::open(file); if rar_archive.is_none() { - return Err(UnexpectedValueException { - message: format!("Could not open RAR archive: {}", file), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Could not open RAR archive: {}", + file + )) .into()); } let rar_archive = rar_archive.unwrap(); let entries = rar_archive.get_entries(); if entries.is_none() { - return Err(RuntimeException { - message: "Could not retrieve RAR archive entries".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Could not retrieve RAR archive entries".to_string(), + ) .into()); } for entry in entries.unwrap() { if !entry.extract(path) { - return Err(RuntimeException { - message: "Could not extract entry".to_string(), - code: 0, - } - .into()); + return Err(RuntimeException::new("Could not extract entry".to_string()).into()); } } diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs index 6a8bdf0e..cb748a93 100644 --- a/crates/shirabe/src/downloader/svn_downloader.rs +++ b/crates/shirabe/src/downloader/svn_downloader.rs @@ -75,13 +75,10 @@ impl SvnDownloader { Some(path), ) != 0 { - return Err(RuntimeException { - message: format!( - "Could not reset changes\n\n:{}", - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not reset changes\n\n:{}", + self.inner.process.borrow().get_error_output() + )) .into()); } @@ -96,10 +93,10 @@ impl SvnDownloader { path: &str, ) -> anyhow::Result<()> { if self.get_local_changes(package, path)?.is_some() { - return Err(RuntimeException { - message: format!("Source directory {} has uncommitted changes.", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Source directory {} has uncommitted changes.", + path + )) .into()); } @@ -143,10 +140,9 @@ impl VcsDownloader for SvnDownloader { Some(self.inner.process.clone()), ); if util.binary_version().is_none() { - return Err(RuntimeException { - message: "svn was not found in your PATH, skipping source download".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "svn was not found in your PATH, skipping source download".to_string(), + ) .into()); } @@ -206,13 +202,10 @@ impl VcsDownloader for SvnDownloader { let r#ref = target.get_source_reference(); if !self.has_metadata_repository(path) { - return Err(RuntimeException { - message: format!( - "The .svn directory is missing from {}, see https://getcomposer.org/commit-deps for more information", - path - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The .svn directory is missing from {}, see https://getcomposer.org/commit-deps for more information", + path + )) .into()); } @@ -324,11 +317,7 @@ impl VcsDownloader for SvnDownloader { break; } Some("n") => { - return Err(RuntimeException { - message: "Update aborted".to_string(), - code: 0, - } - .into()); + return Err(RuntimeException::new("Update aborted".to_string()).into()); } Some("v") => { for line in &changes { @@ -384,14 +373,11 @@ impl VcsDownloader for SvnDownloader { .execute_args(&command, &mut output, Some(path)) != 0 { - return Err(RuntimeException { - message: format!( - "Failed to execute {}\n\n{}", - command.join(" "), - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + command.join(" "), + self.inner.process.borrow().get_error_output() + )) .into()); } @@ -403,10 +389,10 @@ impl VcsDownloader for SvnDownloader { .cloned() .unwrap_or_default() } else { - return Err(RuntimeException { - message: format!("Unable to determine svn url for path {}", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Unable to determine svn url for path {}", + path + )) .into()); }; @@ -431,10 +417,11 @@ impl VcsDownloader for SvnDownloader { util.set_cache_credentials(self.cache_credentials.get()); util.execute_local(command.clone(), path, None, self.inner.io.is_verbose()) .map_err(|e| { - RuntimeException { - message: format!("Failed to execute {}\n\n{}", command.join(" "), e), - code: 0, - } + RuntimeException::new(format!( + "Failed to execute {}\n\n{}", + command.join(" "), + e + )) .into() }) } else { diff --git a/crates/shirabe/src/downloader/transport_exception.rs b/crates/shirabe/src/downloader/transport_exception.rs index 155a5ce4..da8b4c50 100644 --- a/crates/shirabe/src/downloader/transport_exception.rs +++ b/crates/shirabe/src/downloader/transport_exception.rs @@ -1,11 +1,10 @@ //! ref: composer/src/Composer/Downloader/TransportException.php -use shirabe_php_shim::PhpMixed; +use shirabe_php_shim::{PhpMixed, RuntimeException}; #[derive(Debug, Clone)] pub struct TransportException { - pub message: String, - pub code: i64, + inner: RuntimeException, pub(crate) headers: Option<Vec<String>>, pub(crate) response: Option<String>, pub(crate) status_code: Option<i64>, @@ -15,8 +14,7 @@ pub struct TransportException { impl TransportException { pub fn new(message: String, code: i64) -> Self { Self { - message, - code, + inner: RuntimeException::with_code(message, code), headers: None, response: None, status_code: None, @@ -24,20 +22,6 @@ impl TransportException { } } - /// PHP exposes ($message, $code = 0) — alias of `new` used at call sites where the - /// status/exception code is provided up-front. - pub fn new_with_code(message: String, code: i64) -> Self { - Self::new(message, code) - } - - pub fn get_code(&self) -> i64 { - self.code - } - - pub fn get_message(&self) -> &str { - &self.message - } - pub fn set_headers(&mut self, headers: Vec<String>) { self.headers = Some(headers); } @@ -71,10 +55,8 @@ impl TransportException { } } -impl std::fmt::Display for TransportException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.message) - } -} - -impl std::error::Error for TransportException {} +shirabe_php_shim::impl_php_exception!( + TransportException, + inner, + r"Composer\Downloader\TransportException" +); diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs index 45ca0e3a..56bdea60 100644 --- a/crates/shirabe/src/downloader/vcs_downloader.rs +++ b/crates/shirabe/src/downloader/vcs_downloader.rs @@ -18,8 +18,9 @@ use crate::util::Filesystem; use crate::util::ProcessExecutor; use indexmap::IndexMap; use shirabe_php_shim::{ - InvalidArgumentException, PhpMixed, RuntimeException, array_map, array_shift, explode, - get_class_err, implode, rawurldecode, realpath, str_replace, strlen, strpos, substr, trim, + AnyThrowable, InvalidArgumentException, PhpClass as _, PhpMixed, RuntimeException, array_map, + array_shift, explode, implode, rawurldecode, realpath, str_replace, strlen, strpos, substr, + trim, }; #[derive(Debug)] @@ -129,13 +130,10 @@ pub trait VcsDownloader: prev_package: Option<PackageInterfaceHandle>, ) -> anyhow::Result<Option<PhpMixed>> { if package.get_source_reference().is_none() { - return Err(InvalidArgumentException { - message: format!( - "Package {} is missing reference information", - package.get_pretty_name(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} is missing reference information", + package.get_pretty_name(), + )) .into()); } @@ -157,7 +155,13 @@ pub trait VcsDownloader: } if self.io().is_debug() { self.io().write_error3( - &format!("Failed: [{}] {}", get_class_err(&e), e), + &format!( + "Failed: [{}] {}", + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), + e + ), true, io_interface::NORMAL, ); @@ -232,13 +236,10 @@ pub trait VcsDownloader: path: &str, ) -> anyhow::Result<Option<PhpMixed>> { if package.get_source_reference().is_none() { - return Err(InvalidArgumentException { - message: format!( - "Package {} is missing reference information", - package.get_pretty_name(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} is missing reference information", + package.get_pretty_name(), + )) .into()); } @@ -264,7 +265,13 @@ pub trait VcsDownloader: } if self.io().is_debug() { self.io().write_error3( - &format!("Failed: [{}] {}", get_class_err(&e), e), + &format!( + "Failed: [{}] {}", + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), + e + ), true, io_interface::NORMAL, ); @@ -292,13 +299,10 @@ pub trait VcsDownloader: path: &str, ) -> anyhow::Result<Option<PhpMixed>> { if target.get_source_reference().is_none() { - return Err(InvalidArgumentException { - message: format!( - "Package {} is missing reference information", - target.get_pretty_name(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package {} is missing reference information", + target.get_pretty_name(), + )) .into()); } @@ -333,7 +337,13 @@ pub trait VcsDownloader: } if self.io().is_debug() { self.io().write_error3( - &format!("Failed: [{}] {}", get_class_err(&e), e), + &format!( + "Failed: [{}] {}", + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), + e + ), true, io_interface::NORMAL, ); @@ -400,10 +410,10 @@ pub trait VcsDownloader: let result = Filesystem::remove_directory_async_via(self.filesystem(), path).await?; if !result { - return Err(RuntimeException { - message: format!("Could not completely delete {}, aborting.", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not completely delete {}, aborting.", + path + )) .into()); } @@ -441,10 +451,10 @@ pub trait VcsDownloader: ) -> anyhow::Result<Option<PhpMixed>> { // the default implementation just fails if there are any changes, override in child classes to provide stash-ability if self.get_local_changes(package, path)?.is_some() { - return Err(RuntimeException { - message: format!("Source directory {} has uncommitted changes.", path), - code: 0, - } + return Err(RuntimeException::new(format!( + "Source directory {} has uncommitted changes.", + path + )) .into()); } diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs index 8870194c..84ed4c9e 100644 --- a/crates/shirabe/src/downloader/zip_downloader.rs +++ b/crates/shirabe/src/downloader/zip_downloader.rs @@ -10,6 +10,7 @@ use crate::util::Platform; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_external_packages::symfony::process::ExecutableFinder; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ CmpOp, DIRECTORY_SEPARATOR, ErrorException, PhpMixed, RuntimeException, UnexpectedValueException, ZipArchive, bin2hex, class_exists, file_exists, file_get_contents, @@ -146,13 +147,10 @@ impl ZipDownloader { .borrow() .contains_key(&package.get_name()) { - return Err(RuntimeException { - message: format!( - "Failed to extract {} as the installation was aborted by another package operation.", - package.get_name() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to extract {} as the installation was aborted by another package operation.", + package.get_name() + )) .into()); } @@ -162,19 +160,16 @@ impl ZipDownloader { return self .try_fallback( - RuntimeException { - message: format!( - "Failed to extract {}: ({}) {}\n\n{}", - package.get_name(), - process - .get_exit_code() - .map(|c| c.to_string()) - .unwrap_or_default(), - command.join(" "), - output - ), - code: 0, - } + RuntimeException::new(format!( + "Failed to extract {}: ({}) {}\n\n{}", + package.get_name(), + process + .get_exit_code() + .map(|c| c.to_string()) + .unwrap_or_default(), + command.join(" "), + output + )) .into(), is_last_chance, file, @@ -337,13 +332,10 @@ impl ZipDownloader { && total_size > archive_sz * 100 && total_size > 50 * 1024 * 1024 { - return Err(RuntimeException { - message: format!( - "Invalid zip file for \"{}\" with compression ratio >99% (possible zip bomb)", - package.get_name(), - ), - code: 0, - }.into()); + return Err(RuntimeException::new(format!( + "Invalid zip file for \"{}\" with compression ratio >99% (possible zip bomb)", + package.get_name(), + )).into()); } } @@ -354,32 +346,26 @@ impl ZipDownloader { return Ok(None); } - Err(RuntimeException { - message: format!( - "There was an error extracting the ZIP file for \"{}\", it is either corrupted or using an invalid format.", - package.get_name(), - ), - code: 0, - }.into()) + Err(RuntimeException::new(format!( + "There was an error extracting the ZIP file for \"{}\", it is either corrupted or using an invalid format.", + package.get_name(), + )).into()) } - Err(code) => Err(UnexpectedValueException { - message: self.get_error_message(code, file).trim_end().to_string(), + Err(code) => Err(UnexpectedValueException::with_code( + self.get_error_message(code, file).trim_end().to_string(), code, - } + ) .into()), } })(); result.map_err(|e| { - if let Some(err) = e.downcast_ref::<ErrorException>() { - RuntimeException { - message: format!( - "The archive for \"{}\" may contain identical file names with different capitalization (which fails on case insensitive filesystems): {}", - package.get_name(), - err.message, - ), - code: 0, - }.into() + if let Some(err) = e.catch::<ErrorException>() { + RuntimeException::new(format!( + "The archive for \"{}\" may contain identical file names with different capitalization (which fails on case insensitive filesystems): {}", + package.get_name(), + err.get_message(), + )).into() } else { e } @@ -578,11 +564,7 @@ impl crate::downloader::DownloaderInterface for ZipDownloader { ini_message ) }; - return Err(RuntimeException { - message: error, - code: 0, - } - .into()); + return Err(RuntimeException::new(error).into()); } { diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs index b821f647..3db924d4 100644 --- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs +++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs @@ -29,6 +29,7 @@ use shirabe_php_rpc::{ PhpThrow, PluginValue, RustMethodDispatcher, RustObjHandle, call_function, call_function_with_dispatcher, call_php_method, call_static_method, }; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, PATH_SEPARATOR, PhpMixed, RuntimeException, array_pop, array_push, array_search_in_vec, array_splice, file_exists, get_class, hash, implode, ini_get, is_array, @@ -201,13 +202,9 @@ impl EventDispatcher { ) -> anyhow::Result<i64> { match event { None => { - let name = event_name.ok_or_else(|| { - anyhow::anyhow!(InvalidArgumentException { - message: - "If no $event is passed in to Composer\\EventDispatcher\\EventDispatcher::dispatch you have to pass in an $eventName, got null." - .to_string(), - code: 0, - }) + let name = event_name.ok_or_else(|| -> anyhow::Error { + InvalidArgumentException::new("If no $event is passed in to Composer\\EventDispatcher\\EventDispatcher::dispatch you have to pass in an $eventName, got null." + .to_string()).into() })?; let mut event = Event::new(name.to_string(), Vec::new(), IndexMap::new()); self.do_dispatch(&mut event) @@ -411,15 +408,12 @@ impl EventDispatcher { Some(&mut PluginRpcDispatcher::default()), ))?; if !matches!(is_callable_value, PluginValue::Bool(true)) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Subscriber {}::{} for event {} is not callable, make sure the function is defined and public", - handle.class, - method_name, - event.get_name() - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Subscriber {}::{} for event {} is not callable, make sure the function is defined and public", + handle.class, + method_name, + event.get_name() + )).into()); } self.io.write_error3( &format!( @@ -431,18 +425,17 @@ impl EventDispatcher { true, crate::io::VERBOSE, ); - let stub_class = Self::event_stub_class(event).ok_or_else(|| { - // TODO(plugin): installer and plugin events have no proxy stub yet. - anyhow::anyhow!(RuntimeException { - message: format!( - "no proxy stub is available yet for the event `{}` dispatched to {}::{}", - event.get_name(), - handle.class, - method_name, - ), - code: 0, - }) - })?; + let stub_class = + Self::event_stub_class(event).ok_or_else(|| -> anyhow::Error { + // TODO(plugin): installer and plugin events have no proxy stub yet. + RuntimeException::new(format!( + "no proxy stub is available yet for the event `{}` dispatched to {}::{}", + event.get_name(), + handle.class, + method_name, + )) + .into() + })?; let event_rhandle = shirabe_php_rpc::alloc_rhandle(); let mut dispatcher = PluginRpcDispatcher { event: Some((event_rhandle, event)), @@ -469,10 +462,7 @@ impl EventDispatcher { // TODO(plugin): the original exception class is collapsed to // RuntimeException on this side of the boundary. Err(throw) => { - return Err(anyhow::anyhow!(RuntimeException { - message: throw.message, - code: throw.code, - })); + return Err(RuntimeException::with_code(throw.message, throw.code).into()); } }; } else if !is_string_callable { @@ -494,15 +484,12 @@ impl EventDispatcher { } _ => ("?".to_string(), "?".to_string()), }; - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Subscriber {}::{} for event {} is not callable, make sure the function is defined and public", - class_name, - method, - event.get_name() - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Subscriber {}::{} for event {} is not callable, make sure the function is defined and public", + class_name, + method, + event.get_name() + )).into()); } if let Callable::ArrayCallable(first, method_name) = &callable { let prefix = if is_object(first.as_ref()) { @@ -594,15 +581,14 @@ impl EventDispatcher { exit_code ), true, crate::io::QUIET); - return Err(anyhow::anyhow!(ScriptExecutionException( - RuntimeException { - message: format!( - "Error Output: {}", - self.process.borrow().get_error_output() - ), - code: exit_code, - } - ))); + return Err(ScriptExecutionException::new( + format!( + "Error Output: {}", + self.process.borrow().get_error_output() + ), + exit_code, + ) + .into()); } } else { if self @@ -638,7 +624,7 @@ impl EventDispatcher { match self.dispatch(Some(&script_name), Some(&mut script_event)) { Ok(v) => r#return = v, Err(e) => { - if e.downcast_ref::<ScriptExecutionException>().is_some() { + if e.is_instanceof::<ScriptExecutionException>() { self.io.write_error3( &format!( "<error>Script {} was called via {}</error>", @@ -839,10 +825,9 @@ try {{ true, crate::io::QUIET, ); - return Err(anyhow::anyhow!(RuntimeException { - message: throw.message, - code: throw.code, - })); + return Err( + RuntimeException::with_code(throw.message, throw.code).into() + ); } }; let command_output = result @@ -876,7 +861,7 @@ try {{ true, crate::io::QUIET, ); - return Err(anyhow::anyhow!(RuntimeException { message, code })); + return Err(RuntimeException::with_code(message, code).into()); } r#return = match result.as_array().and_then(|map| map.get("status")) { Some(PhpMixed::Int(status)) => *status, @@ -1043,15 +1028,14 @@ try {{ exit_code ), true, crate::io::QUIET); - return Err(anyhow::anyhow!(ScriptExecutionException( - RuntimeException { - message: format!( - "Error Output: {}", - self.process.borrow().get_error_output() - ), - code: exit_code, - } - ))); + return Err(ScriptExecutionException::new( + format!( + "Error Output: {}", + self.process.borrow().get_error_output() + ), + exit_code, + ) + .into()); } } _ => { @@ -1097,10 +1081,10 @@ try {{ let php_path = match php_path { Some(p) => p, None => { - return Err(anyhow::anyhow!(RuntimeException { - message: "Failed to locate PHP binary to execute ".to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "Failed to locate PHP binary to execute ".to_string(), + ) + .into()); } }; let php_args = finder.find_arguments(); @@ -1152,17 +1136,15 @@ try {{ ); } - let stub_class = Self::event_stub_class(event).ok_or_else(|| { + let stub_class = Self::event_stub_class(event).ok_or_else(|| -> anyhow::Error { // TODO(plugin): installer and plugin events have no proxy stub yet. - anyhow::anyhow!(RuntimeException { - message: format!( - "no proxy stub is available yet for the event `{}` dispatched to {}::{}", - event.get_name(), - class_name, - method_name, - ), - code: 0, - }) + RuntimeException::new(format!( + "no proxy stub is available yet for the event `{}` dispatched to {}::{}", + event.get_name(), + class_name, + method_name, + )) + .into() })?; Self::ensure_script_autoloader()?; @@ -1186,10 +1168,7 @@ try {{ Ok(value) => Ok(value.to_php_mixed()?), // TODO(plugin): the original exception class is collapsed to RuntimeException on // this side of the boundary. - Err(throw) => Err(anyhow::anyhow!(RuntimeException { - message: throw.message, - code: throw.code, - })), + Err(throw) => Err(RuntimeException::with_code(throw.message, throw.code).into()), } } @@ -1395,13 +1374,11 @@ try {{ fn push_event(&mut self, event: &dyn EventInterface) -> anyhow::Result<i64> { let event_name = event.get_name().to_string(); if self.event_stack.iter().any(|n| n == &event_name) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Circular call to script handler '{}' detected", - PhpMixed::String(event_name), - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Circular call to script handler '{}' detected", + PhpMixed::String(event_name), + )) + .into()); } Ok(array_push(&mut self.event_stack, event_name)) @@ -1597,13 +1574,13 @@ try {{ pub(crate) fn ensure_composer_php_runtime() -> anyhow::Result<()> { // TODO(plugin): the real PHP classes are taken from a Composer checkout for now; how // they ship with a released Shirabe binary is part of the plugin distribution work. - let autoload = Self::composer_php_runtime_autoload().ok_or_else(|| { - anyhow::anyhow!(RuntimeException { - message: "unable to locate the Composer PHP runtime; set SHIRABE_COMPOSER_PHP_DIR \ + let autoload = Self::composer_php_runtime_autoload().ok_or_else(|| -> anyhow::Error { + RuntimeException::new( + "unable to locate the Composer PHP runtime; set SHIRABE_COMPOSER_PHP_DIR \ to a Composer checkout with its vendor directory installed" .to_string(), - code: 0, - }) + ) + .into() })?; unwrap_php_result(call_function( "__shirabe_require", @@ -1857,10 +1834,7 @@ pub(crate) fn unwrap_php_result( ) -> anyhow::Result<PluginValue> { match outcome? { Ok(value) => Ok(value), - Err(throw) => Err(anyhow::anyhow!(RuntimeException { - message: throw.message, - code: throw.code, - })), + Err(throw) => Err(RuntimeException::with_code(throw.message, throw.code).into()), } } diff --git a/crates/shirabe/src/event_dispatcher/script_execution_exception.rs b/crates/shirabe/src/event_dispatcher/script_execution_exception.rs index 05567b01..e4ce1ec0 100644 --- a/crates/shirabe/src/event_dispatcher/script_execution_exception.rs +++ b/crates/shirabe/src/event_dispatcher/script_execution_exception.rs @@ -7,19 +7,13 @@ use shirabe_php_shim::RuntimeException; pub struct ScriptExecutionException(pub RuntimeException); impl ScriptExecutionException { - pub fn get_code(&self) -> i64 { - self.0.code - } - - pub fn get_message(&self) -> &str { - &self.0.message - } -} - -impl std::fmt::Display for ScriptExecutionException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + pub fn new(message: String, code: i64) -> Self { + Self(RuntimeException::with_code(message, code)) } } -impl std::error::Error for ScriptExecutionException {} +shirabe_php_shim::impl_php_exception!( + ScriptExecutionException, + 0, + r"Composer\EventDispatcher\ScriptExecutionException" +); diff --git a/crates/shirabe/src/exception/irrecoverable_download_exception.rs b/crates/shirabe/src/exception/irrecoverable_download_exception.rs index a8d2dbb4..ed424eff 100644 --- a/crates/shirabe/src/exception/irrecoverable_download_exception.rs +++ b/crates/shirabe/src/exception/irrecoverable_download_exception.rs @@ -5,10 +5,14 @@ use shirabe_php_shim::RuntimeException; #[derive(Debug)] pub struct IrrecoverableDownloadException(pub RuntimeException); -impl std::fmt::Display for IrrecoverableDownloadException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) +impl IrrecoverableDownloadException { + pub fn new(message: String) -> Self { + Self(RuntimeException::new(message)) } } -impl std::error::Error for IrrecoverableDownloadException {} +shirabe_php_shim::impl_php_exception!( + IrrecoverableDownloadException, + 0, + r"Composer\Exception\IrrecoverableDownloadException" +); diff --git a/crates/shirabe/src/exception/no_ssl_exception.rs b/crates/shirabe/src/exception/no_ssl_exception.rs index 968ecfd7..b0470e58 100644 --- a/crates/shirabe/src/exception/no_ssl_exception.rs +++ b/crates/shirabe/src/exception/no_ssl_exception.rs @@ -6,10 +6,10 @@ use shirabe_php_shim::RuntimeException; #[derive(Debug)] pub struct NoSslException(pub RuntimeException); -impl std::fmt::Display for NoSslException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) +impl NoSslException { + pub fn new(message: String) -> Self { + Self(RuntimeException::new(message)) } } -impl std::error::Error for NoSslException {} +shirabe_php_shim::impl_php_exception!(NoSslException, 0, r"Composer\Exception\NoSslException"); diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs index 7328bf42..d7439d58 100644 --- a/crates/shirabe/src/factory.rs +++ b/crates/shirabe/src/factory.rs @@ -55,6 +55,7 @@ use shirabe_external_packages::symfony::console::formatter::OutputFormatter; use shirabe_external_packages::symfony::console::formatter::OutputFormatterStyle; use shirabe_external_packages::symfony::console::formatter::OutputFormatterStyleInterface; use shirabe_external_packages::symfony::console::output::ConsoleOutput; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, PATHINFO_EXTENSION, PHP_EOL, PHP_OS, PhpMixed, RuntimeException, UnexpectedValueException, array_replace_recursive, class_exists, dirname, extension_loaded, @@ -105,12 +106,8 @@ impl Factory { .map(|s| s.is_empty()) .unwrap_or(true) { - return Err(anyhow::anyhow!(RuntimeException { - message: - "The APPDATA or COMPOSER_HOME environment variable must be set for composer to run correctly" - .to_string(), - code: 0, - })); + return Err(RuntimeException::new("The APPDATA or COMPOSER_HOME environment variable must be set for composer to run correctly" + .to_string()).into()); } let appdata = Platform::get_env("APPDATA").unwrap_or_default(); @@ -358,13 +355,10 @@ impl Factory { let env_trimmed = trim(&env_str, Some(" \t\n\r\0\u{0B}")); if !env_trimmed.is_empty() { if is_dir(&env_trimmed) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "The COMPOSER environment variable is set to {} which is a directory, this variable should point to a composer.json or be left unset.", - env_trimmed - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "The COMPOSER environment variable is set to {} which is a directory, this variable should point to a composer.json or be left unset.", + env_trimmed + )).into()); } return Ok(env_trimmed); @@ -465,25 +459,25 @@ impl Factory { } else { "" }; - return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!("{}{}{}", message, PHP_EOL, instructions), - code: 0, - })); + return Err(InvalidArgumentException::new(format!( + "{}{}{}", + message, PHP_EOL, instructions + )) + .into()); } if !Platform::is_input_completion_process() && let Err(e) = file.validate_schema(JsonFile::LAX_SCHEMA, None) { - if let Some(jve) = e.downcast_ref::<JsonValidationException>() { + if let Some(jve) = e.catch::<JsonValidationException>() { let errors = format!( " - {}", implode(&format!("{} - ", PHP_EOL), jve.get_errors()) ); let message = format!("{}:{}{}", jve.get_message(), PHP_EOL, errors); - return Err(anyhow::anyhow!(JsonValidationException::new( - message, - jve.get_errors().clone(), - ))); + return Err( + JsonValidationException::new(message, jve.get_errors().clone()).into(), + ); } return Err(e); } @@ -1358,10 +1352,7 @@ impl Factory { factory.create_composer(io, config, disable_plugins, None, true, disable_scripts)?; // fullLoad=true guarantees a full Composer; narrow PartialComposer -> Composer (PHP `: Composer`). composer.as_full().ok_or_else(|| { - anyhow::anyhow!(RuntimeException { - message: "Composer expected with fullLoad=true".to_string(), - code: 0, - }) + RuntimeException::new("Composer expected with fullLoad=true".to_string()).into() }) } @@ -1394,10 +1385,7 @@ impl Factory { let composer = factory.create_composer(io, config, disable_plugins, None, true, disable_scripts)?; composer.as_full().ok_or_else(|| { - anyhow::anyhow!(RuntimeException { - message: "Composer expected with fullLoad=true".to_string(), - code: 0, - }) + RuntimeException::new("Composer expected with fullLoad=true".to_string()).into() }) } @@ -1465,12 +1453,8 @@ impl Factory { unsafe { WARNED = true }; disable_tls = true; } else if !extension_loaded("openssl") { - return Err(anyhow::anyhow!(NoSslException(RuntimeException { - message: - "The openssl extension is required for SSL/TLS protection but is not available. If you can not enable the openssl extension, you can disable this error, at your own risk, by setting the 'disable-tls' option to true." - .to_string(), - code: 0, - }))); + return Err(NoSslException::new("The openssl extension is required for SSL/TLS protection but is not available. If you can not enable the openssl extension, you can disable this error, at your own risk, by setting the 'disable-tls' option to true." + .to_string()).into()); } let mut http_downloader_options: IndexMap<String, PhpMixed> = IndexMap::new(); if !disable_tls { @@ -1509,7 +1493,7 @@ impl Factory { let http_downloader = match http_downloader_result { Ok(h) => h, Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && strpos(te.get_message(), "cafile").is_some() { io.write3( @@ -1547,12 +1531,11 @@ impl Factory { let auth_data = json_decode(&composer_auth_env_str, false)?; if matches!(auth_data, PhpMixed::Null) { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: - "COMPOSER_AUTH environment variable is malformed, should be a valid JSON object" - .to_string(), - code: 0, - })); + return Err(UnexpectedValueException::new( + "COMPOSER_AUTH environment variable is malformed, should be a valid JSON object" + .to_string(), + ) + .into()); } if let Some(io_ref) = &io { @@ -1591,12 +1574,8 @@ impl Factory { fn get_user_dir() -> anyhow::Result<String> { let home = Platform::get_env("HOME").unwrap_or_default(); if home.is_empty() { - return Err(anyhow::anyhow!(RuntimeException { - message: - "The HOME or COMPOSER_HOME environment variable must be set for composer to run correctly" - .to_string(), - code: 0, - })); + return Err(RuntimeException::new("The HOME or COMPOSER_HOME environment variable must be set for composer to run correctly" + .to_string()).into()); } Ok(trim(&strtr(&home, "\\", "/"), Some("/"))) @@ -1615,20 +1594,19 @@ impl Factory { let result = match file_or_data { ValidateJsonInput::File(file) => file.validate_schema(schema, None), ValidateJsonInput::Data(data) => { - let source = source.ok_or_else(|| { - anyhow::anyhow!(InvalidArgumentException { - message: - "$source is required to be provided if $fileOrData is arbitrary data" - .to_string(), - code: 0, - }) + let source = source.ok_or_else(|| -> anyhow::Error { + InvalidArgumentException::new( + "$source is required to be provided if $fileOrData is arbitrary data" + .to_string(), + ) + .into() })?; JsonFile::validate_json_schema(source, &data, schema, None) } }; if let Err(e) = result { - if let Some(jve) = e.downcast_ref::<JsonValidationException>() { + if let Some(jve) = e.catch::<JsonValidationException>() { let msg = format!( "{}, this may result in errors and should be resolved:{} - {}", jve.get_message(), @@ -1638,10 +1616,7 @@ impl Factory { if let Some(io_ref) = io { io_ref.write_error3(&format!("<warning>{}</>", msg), true, crate::io::NORMAL); } else { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: msg, - code: 0 - })); + return Err(UnexpectedValueException::new(msg).into()); } } else { return Err(e); diff --git a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs index 12c69091..7f08e8dd 100644 --- a/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs +++ b/crates/shirabe/src/filter/platform_requirement_filter/platform_requirement_filter_factory.rs @@ -38,13 +38,10 @@ impl PlatformRequirementFilterFactory { list, )?)) } - other => Err(anyhow::anyhow!(InvalidArgumentException { - message: format!( - "PlatformRequirementFilter: Unknown $boolOrList parameter {}. Please report at https://github.com/composer/composer/issues/new.", - shirabe_php_shim::get_debug_type(&other) - ), - code: 0, - })), + other => Err(InvalidArgumentException::new(format!( + "PlatformRequirementFilter: Unknown $boolOrList parameter {}. Please report at https://github.com/composer/composer/issues/new.", + shirabe_php_shim::get_debug_type(&other) + )).into()), } } diff --git a/crates/shirabe/src/installed_versions.rs b/crates/shirabe/src/installed_versions.rs index ce78f013..6d702413 100644 --- a/crates/shirabe/src/installed_versions.rs +++ b/crates/shirabe/src/installed_versions.rs @@ -202,11 +202,10 @@ impl InstalledVersions { return Ok(implode(" || ", &ranges)); } - Err(OutOfBoundsException { - message: format!("Package \"{}\" is not installed", package_name), - code: 0, - } - .into()) + Err( + OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) + .into(), + ) } /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present @@ -229,11 +228,10 @@ impl InstalledVersions { .map(|s| s.to_string())); } - Err(OutOfBoundsException { - message: format!("Package \"{}\" is not installed", package_name), - code: 0, - } - .into()) + Err( + OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) + .into(), + ) } /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present @@ -256,11 +254,10 @@ impl InstalledVersions { .map(|s| s.to_string())); } - Err(OutOfBoundsException { - message: format!("Package \"{}\" is not installed", package_name), - code: 0, - } - .into()) + Err( + OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) + .into(), + ) } /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference @@ -283,11 +280,10 @@ impl InstalledVersions { .map(|s| s.to_string())); } - Err(OutOfBoundsException { - message: format!("Package \"{}\" is not installed", package_name), - code: 0, - } - .into()) + Err( + OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) + .into(), + ) } /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. @@ -309,11 +305,10 @@ impl InstalledVersions { }); } - Err(OutOfBoundsException { - message: format!("Package \"{}\" is not installed", package_name), - code: 0, - } - .into()) + Err( + OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) + .into(), + ) } pub fn get_root_package() -> IndexMap<String, PhpMixed> { diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs index 9028af88..702569aa 100644 --- a/crates/shirabe/src/installer.rs +++ b/crates/shirabe/src/installer.rs @@ -28,6 +28,7 @@ pub use package_event::*; pub use package_events::*; pub use plugin_installer::*; pub use project_installer::*; +use shirabe_php_shim::Catch as _; pub use suggested_packages_reporter::*; use crate::io::io_interface; @@ -226,10 +227,11 @@ impl Installer { gc_disable(); if self.update_allow_list.is_some() && self.update_mirrors { - return Err(RuntimeException { - message: "The installer options updateMirrors and updateAllowList are mutually exclusive.".to_string(), - code: 0, - }.into()); + return Err(RuntimeException::new( + "The installer options updateMirrors and updateAllowList are mutually exclusive." + .to_string(), + ) + .into()); } let is_fresh_install = self @@ -535,7 +537,7 @@ impl Installer { }); } Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { self.io .error(&format!("Failed to audit {} packages.", target), &[]); if self.io.is_verbose() { @@ -570,27 +572,25 @@ impl Installer { let mut locked_repository: Option<crate::repository::LockArrayRepositoryHandle> = None; let try_load_locked = || -> anyhow::Result< - Result<Option<crate::repository::LockArrayRepositoryHandle>, ParsingException>, + Result<Option<crate::repository::LockArrayRepositoryHandle>, anyhow::Error>, > { - if self.locker.borrow_mut().is_locked() { - match self.locker.borrow_mut().get_locked_repository(true) { - Ok(r) => Ok(Ok(Some(r))), - Err(e) => match e.downcast::<ParsingException>() { - Ok(p) => Ok(Err(p)), - Err(other) => Err(other), - }, - } - } else { - Ok(Ok(None)) + if self.locker.borrow_mut().is_locked() { + match self.locker.borrow_mut().get_locked_repository(true) { + Ok(r) => Ok(Ok(Some(r))), + Err(e) if e.is_instanceof::<ParsingException>() => Ok(Err(e)), + Err(e) => Err(e), } - }; + } else { + Ok(Ok(None)) + } + }; match try_load_locked()? { Ok(r) => locked_repository = r, Err(e) => { if self.update_allow_list.is_some() || self.update_mirrors { // in case we are doing a partial update or updating mirrors, the lock file is needed so we error - return Err(e.into()); + return Err(e); } // otherwise, ignoring parse errors as the lock file will be regenerated from scratch when // doing a full update diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs index 1f1c60a5..454dfa86 100644 --- a/crates/shirabe/src/installer/installation_manager.rs +++ b/crates/shirabe/src/installer/installation_manager.rs @@ -191,11 +191,7 @@ impl InstallationManager { } } - Err(InvalidArgumentException { - message: format!("Unknown installer type: {}", r#type), - code: 0, - } - .into()) + Err(InvalidArgumentException::new(format!("Unknown installer type: {}", r#type)).into()) } /// Checks whether provided package is installed in one of the registered installers. diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs index e53b9546..6c94e0c1 100644 --- a/crates/shirabe/src/installer/library_installer.rs +++ b/crates/shirabe/src/installer/library_installer.rs @@ -210,14 +210,11 @@ impl LibraryInstaller { assert!( self.download_manager.is_some(), "{}", - LogicException { - message: format!( + LogicException::new(format!( "{} should be initialized with a fully loaded Composer instance to be able to install/... packages", "LibraryInstaller", - ), - code: 0, - } - .message + )) + .get_message() ); self.download_manager.as_ref().unwrap() @@ -345,10 +342,10 @@ impl InstallerInterface for LibraryInstaller { target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { if !repo.borrow_mut().has_package(initial.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", initial), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + initial + )) .into()); } @@ -378,10 +375,10 @@ impl InstallerInterface for LibraryInstaller { package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { if !repo.borrow_mut().has_package(package.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", package), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + package + )) .into()); } diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs index 52c152ea..99d3786c 100644 --- a/crates/shirabe/src/installer/metapackage_installer.rs +++ b/crates/shirabe/src/installer/metapackage_installer.rs @@ -86,10 +86,10 @@ impl InstallerInterface for MetapackageInstaller { target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { if !repo.borrow_mut().has_package(initial.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", initial), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + initial + )) .into()); } @@ -115,10 +115,10 @@ impl InstallerInterface for MetapackageInstaller { package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { if !repo.borrow_mut().has_package(package.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", package), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + package + )) .into()); } diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs index 95f31f2e..d36f5c9e 100644 --- a/crates/shirabe/src/installer/noop_installer.rs +++ b/crates/shirabe/src/installer/noop_installer.rs @@ -69,10 +69,10 @@ impl InstallerInterface for NoopInstaller { ) -> anyhow::Result<Option<PhpMixed>> { let mut repo = repo.borrow_mut(); if !repo.has_package(initial.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", initial), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + initial + )) .into()); } @@ -91,10 +91,10 @@ impl InstallerInterface for NoopInstaller { ) -> anyhow::Result<Option<PhpMixed>> { let mut repo = repo.borrow_mut(); if !repo.has_package(package.clone())? { - return Err(InvalidArgumentException { - message: format!("Package is not installed: {}", package), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Package is not installed: {}", + package + )) .into()); } repo.remove_package(package); diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs index 7683a7d5..abe313b7 100644 --- a/crates/shirabe/src/installer/plugin_installer.rs +++ b/crates/shirabe/src/installer/plugin_installer.rs @@ -121,13 +121,10 @@ impl InstallerInterface for PluginInstaller { let extra = package.get_extra(); let class = extra.get("class").cloned().unwrap_or(PhpMixed::Null); if empty(&class) { - return Err(UnexpectedValueException { - message: format!( - "Error while installing {}, composer-plugin packages should have a class defined in their extra key to be usable.", - package.get_pretty_name() - ), - code: 0, - }.into()); + return Err(UnexpectedValueException::new(format!( + "Error while installing {}, composer-plugin packages should have a class defined in their extra key to be usable.", + package.get_pretty_name() + )).into()); } self.inner.download(package, prev_package).await diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs index 59d44410..8b878352 100644 --- a/crates/shirabe/src/installer/project_installer.rs +++ b/crates/shirabe/src/installer/project_installer.rs @@ -52,10 +52,10 @@ impl InstallerInterface for ProjectInstaller { if std::path::Path::new(install_path).exists() && !self.filesystem.borrow().is_dir_empty(install_path) { - return Err(InvalidArgumentException { - message: format!("Project directory {} is not empty.", install_path), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Project directory {} is not empty.", + install_path + )) .into()); } if !std::path::Path::new(install_path).is_dir() { @@ -109,11 +109,7 @@ impl InstallerInterface for ProjectInstaller { _initial: PackageInterfaceHandle, _target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { - Err(InvalidArgumentException { - message: "not supported".to_string(), - code: 0, - } - .into()) + Err(InvalidArgumentException::new("not supported".to_string()).into()) } async fn uninstall( @@ -121,11 +117,7 @@ impl InstallerInterface for ProjectInstaller { _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { - Err(InvalidArgumentException { - message: "not supported".to_string(), - code: 0, - } - .into()) + Err(InvalidArgumentException::new("not supported".to_string()).into()) } fn get_install_path(&self, _package: PackageInterfaceHandle) -> Option<String> { diff --git a/crates/shirabe/src/io/base_io.rs b/crates/shirabe/src/io/base_io.rs index 5dd7682f..52ef9a82 100644 --- a/crates/shirabe/src/io/base_io.rs +++ b/crates/shirabe/src/io/base_io.rs @@ -145,13 +145,11 @@ pub trait BaseIO: IOInterface { } if !Preg::is_match(php_regex!(r"{^[.A-Za-z0-9_]+$}"), &token_str) { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: format!( - "Your github oauth token for {} contains invalid characters: \"{}\"", - domain, token_str - ), - code: 0, - })); + return Err(UnexpectedValueException::new(format!( + "Your github oauth token for {} contains invalid characters: \"{}\"", + domain, token_str + )) + .into()); } self.check_and_set_authentication( domain, diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index bbe1db9f..d1224fdc 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -30,10 +30,9 @@ impl BufferIO { let stream = match fopen("php://memory", "rw") { Ok(stream) => stream, Err(_) => { - return Err(RuntimeException { - message: "Unable to open memory output stream".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Unable to open memory output stream".to_string(), + ) .into()); } }; @@ -116,10 +115,7 @@ impl BufferIO { let mut input = self.inner.input.borrow_mut(); let Some(streamable) = input.as_streamable_mut() else { - return Err(RuntimeException { - message: "Setting the user inputs requires at least the version 3.2 of the symfony/console component.".to_string(), - code: 0, - } + return Err(RuntimeException::new("Setting the user inputs requires at least the version 3.2 of the symfony/console component.".to_string()) .into()); }; @@ -133,10 +129,9 @@ impl BufferIO { let stream = match fopen("php://memory", "r+") { Ok(stream) => stream, Err(_) => { - return Err(RuntimeException { - message: "Unable to open memory output stream".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Unable to open memory output stream".to_string(), + ) .into()); } }; diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index 12b70318..443a3e51 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -348,7 +348,7 @@ impl ConsoleIO { let mut input = self.input.borrow_mut(); question_helper .ask(&mut *input, error_output, question)? - .map_err(anyhow::Error::new) + .map_err(anyhow::Error::from) } } @@ -490,18 +490,15 @@ impl IOInterfaceImmutable for ConsoleIO { >, > = Box::new(move |answer: Option<PhpMixed>| { validator(answer.unwrap_or(PhpMixed::Null)).map_err(|e| { - shirabe_external_packages::symfony::console::exception::InvalidArgumentException( - shirabe_php_shim::InvalidArgumentException { - message: e.to_string(), - code: 0, - }, + shirabe_external_packages::symfony::console::exception::InvalidArgumentException::new( + e.to_string(), ) }) }); question.set_validator(Some(adapted)); question .set_max_attempts(attempts) - .map_err(|e| anyhow::anyhow!(e.0.message))?; + .map_err(|e| anyhow::anyhow!(e.0.get_message().to_string()))?; self.ask_question(&question) } diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs index 1d6a75ce..2d061ced 100644 --- a/crates/shirabe/src/json/json_file.rs +++ b/crates/shirabe/src/json/json_file.rs @@ -11,6 +11,7 @@ use crate::util::Silencer; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_external_packages::seld::json_lint::{ParsingException, ParsingExceptionDetails}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, RuntimeException, UnexpectedValueException, dirname, file_exists, file_get_contents, @@ -108,10 +109,9 @@ impl JsonFile { io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> anyhow::Result<Self> { if http_downloader.is_none() && Preg::is_match(php_regex!(r"{^https?://}i"), &path) { - return Err(InvalidArgumentException { - message: "http urls require a HttpDownloader instance to be passed".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "http urls require a HttpDownloader instance to be passed".to_string(), + ) .into()); } Ok(Self { @@ -145,10 +145,10 @@ impl JsonFile { .map(|s| s.to_string())) } else { if !Filesystem::is_readable(&self.path) { - return Err(RuntimeException { - message: format!("The file \"{}\" is not readable.", self.path), - code: 0, - } + return Err(RuntimeException::new(format!( + "The file \"{}\" is not readable.", + self.path + )) .into()); } if let Some(io) = &self.io @@ -173,17 +173,13 @@ impl JsonFile { Err(e) => { // TransportException keeps its message verbatim; any other exception is wrapped // with the "Could not read" prefix. - if let Some(te) = e.downcast_ref::<TransportException>() { - return Err(RuntimeException { - message: te.message.clone(), - code: 0, - } - .into()); - } - return Err(RuntimeException { - message: format!("Could not read {}\n\n{}", self.path, e), - code: 0, + if let Some(te) = e.catch::<TransportException>() { + return Err(RuntimeException::new(te.get_message().to_string()).into()); } + return Err(RuntimeException::new(format!( + "Could not read {}\n\n{}", + self.path, e + )) .into()); } }; @@ -191,11 +187,7 @@ impl JsonFile { let json = match json { Some(j) => j, None => { - return Err(RuntimeException { - message: format!("Could not read {}", self.path), - code: 0, - } - .into()); + return Err(RuntimeException::new(format!("Could not read {}", self.path)).into()); } }; @@ -230,21 +222,18 @@ impl JsonFile { let dir = dirname(&self.path); if !is_dir(&dir) { if file_exists(&dir) { - return Err(UnexpectedValueException { - message: format!( - "{} exists and is not a directory.", - realpath(&dir).unwrap_or_default(), - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "{} exists and is not a directory.", + realpath(&dir).unwrap_or_default(), + )) .into()); } // PHP: @mkdir($dir, 0777, true) if !Silencer::call(|| Ok(mkdir(&dir, 0o777, true))).unwrap_or(false) { - return Err(UnexpectedValueException { - message: format!("{} does not exist and could not be created.", dir), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "{} does not exist and could not be created.", + dir + )) .into()); } } @@ -305,10 +294,10 @@ impl JsonFile { /// @return true true on success pub fn validate_schema(&self, schema: i64, schema_file: Option<&str>) -> anyhow::Result<bool> { if !Filesystem::is_readable(&self.path) { - return Err(RuntimeException { - message: format!("The file \"{}\" is not readable.", self.path), - code: 0, - } + return Err(RuntimeException::new(format!( + "The file \"{}\" is not readable.", + self.path + )) .into()); } let content = file_get_contents(&self.path).unwrap_or_default(); @@ -446,10 +435,8 @@ impl JsonFile { data: &T, options: JsonEncodeOptions, ) -> anyhow::Result<String> { - let json = json_encode_ex(data, options.to_flags()).map_err(|err| RuntimeException { - message: format!("JSON encoding failed: {}", err), - code: 0, - })?; + let json = json_encode_ex(data, options.to_flags()) + .map_err(|err| RuntimeException::new(format!("JSON encoding failed: {}", err)))?; if options.pretty_print && options.indent != Self::INDENT_DEFAULT { // Pretty printing and not using default indentation diff --git a/crates/shirabe/src/json/json_manipulator.rs b/crates/shirabe/src/json/json_manipulator.rs index eec823b6..d66fe645 100644 --- a/crates/shirabe/src/json/json_manipulator.rs +++ b/crates/shirabe/src/json/json_manipulator.rs @@ -36,10 +36,9 @@ impl JsonManipulator { contents = "{}".to_string(); } if !Preg::is_match3(php_regex!("#^\\{(.*)\\}$#s"), &contents, None) { - return Err(InvalidArgumentException { - message: "The json file must be an object ({})".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "The json file must be an object ({})".to_string(), + ) .into()); } let newline = if strpos(&contents, "\r\n").is_some() { @@ -823,10 +822,10 @@ impl JsonManipulator { ); } } else { - return Err(LogicException { - message: format!("Nothing matched above for: {}", children), - code: 0, - } + return Err(LogicException::new(format!( + "Nothing matched above for: {}", + children + )) .into()); } } @@ -940,10 +939,7 @@ impl JsonManipulator { children_clean = Some(children.clone()); } - let children_clean = children_clean.ok_or_else(|| InvalidArgumentException { - message: "JsonManipulator: $childrenClean is not defined. Please report at https://github.com/composer/composer/issues/new.".to_string(), - code: 0, - })?; + let children_clean = children_clean.ok_or_else(|| InvalidArgumentException::new("JsonManipulator: $childrenClean is not defined. Please report at https://github.com/composer/composer/issues/new.".to_string()))?; // no child data left, $name was the only key in let mut empty_match: IndexMap<String, String> = IndexMap::new(); @@ -1113,11 +1109,9 @@ impl JsonManipulator { ); } } else { - return Err(LogicException { - message: format!("Nothing matched above for: {}", children), - code: 0, - } - .into()); + return Err( + LogicException::new(format!("Nothing matched above for: {}", children)).into(), + ); } self.contents = format!("{}{}{}", node_start, children, node_end); @@ -1132,10 +1126,9 @@ impl JsonManipulator { index: i64, ) -> anyhow::Result<bool> { if index < 0 { - return Err(InvalidArgumentException { - message: "Index can only be positive integer".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "Index can only be positive integer".to_string(), + ) .into()); } diff --git a/crates/shirabe/src/json/json_validation_exception.rs b/crates/shirabe/src/json/json_validation_exception.rs index a63edda3..cb7370d2 100644 --- a/crates/shirabe/src/json/json_validation_exception.rs +++ b/crates/shirabe/src/json/json_validation_exception.rs @@ -11,7 +11,7 @@ pub struct JsonValidationException { impl JsonValidationException { pub fn new(message: String, errors: Vec<String>) -> Self { Self { - inner: Exception { message, code: 0 }, + inner: Exception::new(message), errors, } } @@ -19,16 +19,10 @@ impl JsonValidationException { pub fn get_errors(&self) -> &Vec<String> { &self.errors } - - pub fn get_message(&self) -> &str { - &self.inner.message - } -} - -impl std::fmt::Display for JsonValidationException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.inner.message) - } } -impl std::error::Error for JsonValidationException {} +shirabe_php_shim::impl_php_exception!( + JsonValidationException, + inner, + r"Composer\Json\JsonValidationException" +); diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index 088cd6ab..52b6efa4 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -435,10 +435,9 @@ impl PackageInterface for AliasPackage { if let Some(existing) = self.repository.as_ref().and_then(|w| w.upgrade()) && !std::rc::Rc::ptr_eq(&existing, repository.as_rc()) { - return Err(LogicException { - message: "A package can only be added to one repository".to_string(), - code: 0, - } + return Err(LogicException::new( + "A package can only be added to one repository".to_string(), + ) .into()); } self.repository = Some(repository.downgrade()); diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs index ad2b41ab..f60c57f1 100644 --- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs +++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs @@ -28,10 +28,10 @@ impl ArchivableFilesFinder { let sources_real_path = realpath(sources); if sources_real_path.is_none() { - return Err(RuntimeException { - message: format!("Could not realpath() the source directory \"{}\"", sources), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not realpath() the source directory \"{}\"", + sources + )) .into()); } let sources = fs.normalize_path(&sources_real_path.unwrap()); diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs index afc89f32..435843a6 100644 --- a/crates/shirabe/src/package/archiver/archive_manager.rs +++ b/crates/shirabe/src/package/archiver/archive_manager.rs @@ -114,10 +114,9 @@ impl ArchiveManager { ignore_filters: bool, ) -> anyhow::Result<String> { if format.is_empty() { - return Err(anyhow::anyhow!(InvalidArgumentException { - message: "Format must be specified".to_string(), - code: 0, - })); + return Err( + InvalidArgumentException::new("Format must be specified".to_string()).into(), + ); } let mut usable_archiver_idx: Option<usize> = None; @@ -131,10 +130,11 @@ impl ArchiveManager { let usable_archiver_idx = match usable_archiver_idx { Some(i) => i, None => { - return Err(anyhow::anyhow!(RuntimeException { - message: format!("No archiver found to support {} format", format), - code: 0, - })); + return Err(RuntimeException::new(format!( + "No archiver found to support {} format", + format + )) + .into()); } }; diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs index fbab8c67..0968f2a4 100644 --- a/crates/shirabe/src/package/archiver/phar_archiver.rs +++ b/crates/shirabe/src/package/archiver/phar_archiver.rs @@ -101,10 +101,10 @@ impl ArchiverInterface for PharArchiver { } else if format == "tar.gz" || format == "tar.bz2" { let compress_algo = *compress_formats.get(format.as_str()).unwrap(); if !PharData::can_compress(compress_algo) { - return Err(RuntimeException { - message: format!("Can not compress to {} format", format), - code: 0, - } + return Err(RuntimeException::new(format!( + "Can not compress to {} format", + format + )) .into()); } if format == "tar.gz" && function_exists("gzcompress") { @@ -124,10 +124,10 @@ impl ArchiverInterface for PharArchiver { if compress_formats.contains_key(format.as_str()) { let compress_algo = *compress_formats.get(format.as_str()).unwrap(); if !PharData::can_compress(compress_algo) { - return Err(RuntimeException { - message: format!("Can not compress to {} format", format), - code: 0, - } + return Err(RuntimeException::new(format!( + "Can not compress to {} format", + format + )) .into()); } @@ -147,7 +147,7 @@ impl ArchiverInterface for PharArchiver { "Could not create archive '{}' from '{}': {}", target_outer, sources, e ); - anyhow::anyhow!(RuntimeException { message, code: 0 }) + RuntimeException::new(message).into() }) } diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs index 186d76c7..e5bb613d 100644 --- a/crates/shirabe/src/package/archiver/zip_archiver.rs +++ b/crates/shirabe/src/package/archiver/zip_archiver.rs @@ -111,7 +111,7 @@ impl ArchiverInterface for ZipArchiver { sources, zip.get_status_string() ); - Err(RuntimeException { message, code: 0 }.into()) + Err(RuntimeException::new(message).into()) } fn supports(&self, format: String, _source_type: Option<String>) -> bool { diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index 0940183c..2c6d3971 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -69,23 +69,17 @@ impl ArrayLoader { class: &str, ) -> anyhow::Result<CompleteOrRootPackage> { if !config.contains_key("name") { - return Err(UnexpectedValueException { - message: format!( - "Unknown package has no name defined ({}).", - json_encode(&PhpMixed::Array(config.clone())).unwrap_or_default() - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Unknown package has no name defined ({}).", + json_encode(&PhpMixed::Array(config.clone())).unwrap_or_default() + )) .into()); } if !config.contains_key("version") || !is_scalar(config.get("version").unwrap()) { - return Err(UnexpectedValueException { - message: format!( - "Package {} has no version defined.", - config.get("name").and_then(|v| v.as_string()).unwrap_or("") - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Package {} has no version defined.", + config.get("name").and_then(|v| v.as_string()).unwrap_or("") + )) .into()); } let mut config_version = config.get("version").cloned().unwrap_or(PhpMixed::Null); @@ -118,14 +112,11 @@ impl ArrayLoader { { Ok(v) => version = v, Err(e) => { - return Err(UnexpectedValueException { - message: format!( - "Failed to normalize version for package \"{}\": {}", - config.get("name").and_then(|v| v.as_string()).unwrap_or(""), - e - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Failed to normalize version for package \"{}\": {}", + config.get("name").and_then(|v| v.as_string()).unwrap_or(""), + e + )) .into()); } } @@ -226,18 +217,14 @@ impl ArrayLoader { }) .unwrap_or(false); if !has_required { - return Err(UnexpectedValueException { - message: format!( - "Package {}'s source key should be specified as {{\"type\": ..., \"url\": ..., \"reference\": ...}},\n{} given.", - - config - .get("name") - .and_then(|v| v.as_string()) - .unwrap_or(""), - json_encode(&source).unwrap_or_default(), - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Package {}'s source key should be specified as {{\"type\": ..., \"url\": ..., \"reference\": ...}},\n{} given.", + config + .get("name") + .and_then(|v| v.as_string()) + .unwrap_or(""), + json_encode(&source).unwrap_or_default(), + )) .into()); } let source_map = source_map.unwrap(); @@ -270,18 +257,14 @@ impl ArrayLoader { .map(|m| m.contains_key("type") && m.contains_key("url")) .unwrap_or(false); if !has_required { - return Err(UnexpectedValueException { - message: format!( - "Package {}'s dist key should be specified as {{\"type\": ..., \"url\": ..., \"reference\": ..., \"shasum\": ...}},\n{} given.", - - config - .get("name") - .and_then(|v| v.as_string()) - .unwrap_or(""), - json_encode(&dist).unwrap_or_default(), - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Package {}'s dist key should be specified as {{\"type\": ..., \"url\": ..., \"reference\": ..., \"shasum\": ...}},\n{} given.", + config + .get("name") + .and_then(|v| v.as_string()) + .unwrap_or(""), + json_encode(&dist).unwrap_or_default(), + )) .into()); } let dist_map = dist_map.unwrap(); @@ -672,13 +655,10 @@ impl ArrayLoader { let parsed_constraint = match self.version_parser.parse_constraints(&constraint) { Ok(c) => c, Err(_e) => { - return Err(UnexpectedValueException { - message: format!( - "Link constraint in {} {} > {} should be a valid version constraint, got \"{}\"", - source, description, target, constraint - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Link constraint in {} {} > {} should be a valid version constraint, got \"{}\"", + source, description, target, constraint + )) .into()); } }; @@ -702,11 +682,9 @@ impl ArrayLoader { config: &IndexMap<String, PhpMixed>, ) -> anyhow::Result<Option<String>> { if !config.contains_key("version") || !is_scalar(config.get("version").unwrap()) { - return Err(UnexpectedValueException { - message: "no/invalid version defined".to_string(), - code: 0, - } - .into()); + return Err( + UnexpectedValueException::new("no/invalid version defined".to_string()).into(), + ); } let mut config_version = config.get("version").cloned().unwrap_or(PhpMixed::Null); if !is_string(&config_version) { diff --git a/crates/shirabe/src/package/loader/invalid_package_exception.rs b/crates/shirabe/src/package/loader/invalid_package_exception.rs index 2bd22c61..23251994 100644 --- a/crates/shirabe/src/package/loader/invalid_package_exception.rs +++ b/crates/shirabe/src/package/loader/invalid_package_exception.rs @@ -27,7 +27,7 @@ impl InvalidPackageException { .join("\n") ); Self { - inner: Exception { message, code: 0 }, + inner: Exception::new(message), errors, warnings, data, @@ -47,10 +47,8 @@ impl InvalidPackageException { } } -impl std::fmt::Display for InvalidPackageException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.inner.message) - } -} - -impl std::error::Error for InvalidPackageException {} +shirabe_php_shim::impl_php_exception!( + InvalidPackageException, + inner, + r"Composer\Package\Loader\InvalidPackageException" +); diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs index c30fbe07..d9e7100a 100644 --- a/crates/shirabe/src/package/loader/json_loader.rs +++ b/crates/shirabe/src/package/loader/json_loader.rs @@ -34,10 +34,7 @@ impl JsonLoader { let config: IndexMap<String, PhpMixed> = match config { PhpMixed::Array(m) => m, _ => { - return Err(TypeError { - message: "Composer\\Package\\Loader\\LoaderInterface::load(): Argument #1 ($config) must be of type array".to_string(), - code: 0, - } + return Err(TypeError::new("Composer\\Package\\Loader\\LoaderInterface::load(): Argument #1 ($config) must be of type array".to_string()) .into()); } }; diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs index 284bb175..e26a7e95 100644 --- a/crates/shirabe/src/package/loader/root_package_loader.rs +++ b/crates/shirabe/src/package/loader/root_package_loader.rs @@ -78,10 +78,7 @@ impl RootPackageLoader { config["name"].as_string().unwrap_or(""), false, ) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!("Your package name {}", err), - code: 0, - })); + return Err(RuntimeException::new(format!("Your package name {}", err)).into()); } let mut auto_versioned = false; @@ -197,13 +194,10 @@ impl RootPackageLoader { let package_name = config["name"].as_string().unwrap_or("").to_string(); if links.contains_key(&package_name) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Root package '{}' cannot require itself in its composer.json\nDid you accidentally name your root package after an external package?", - package_name - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Root package '{}' cannot require itself in its composer.json\nDid you accidentally name your root package after an external package?", + package_name + )).into()); } } } @@ -216,10 +210,7 @@ impl RootPackageLoader { if let Some(err) = ValidatingArrayLoader::has_package_naming_error(link_name, true) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!("{}.{}", link_type, err), - code: 0, - })); + return Err(RuntimeException::new(format!("{}.{}", link_type, err)).into()); } } } @@ -291,14 +282,11 @@ impl RootPackageLoader { return { panic!( "{}", - UnexpectedValueException { - message: format!( + UnexpectedValueException::new(format!( "Invalid alias definition in \"{}\": \"{}\". Aliases should be in the form \"exact-version as other-exact-version\".", req_name, req_version - ), - code: 0, - } - .message + )) + .get_message() ) }; } diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index a050f2d7..fc8eed00 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -1583,11 +1583,12 @@ impl LoaderInterface for ValidatingArrayLoader { } if !self.errors.borrow().is_empty() { - return Err(anyhow::anyhow!(InvalidPackageException::new( + return Err(InvalidPackageException::new( self.errors.borrow().clone(), self.warnings.borrow().clone(), config.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - ))); + ) + .into()); } let package = self.loader.load( diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index a4d28861..3b20704a 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -26,6 +26,7 @@ use crate::util::ProcessExecutor; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_external_packages::seld::json_lint::ParsingException; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ DATE_RFC3339, LogicException, PhpMixed, RuntimeException, array_intersect, array_keys, array_map, array_merge, file_get_contents, filemtime, function_exists, hash, in_array_loose, @@ -194,10 +195,7 @@ impl Locker { if let Some(packages_dev) = lock_data.get("packages-dev").cloned() { locked_packages = array_merge(locked_packages, packages_dev); } else { - return Err(RuntimeException { - message: "The lock file does not contain require-dev information, run install with the --no-dev option or delete it and run composer update to generate a new lock file.".to_string(), - code: 0, - } + return Err(RuntimeException::new("The lock file does not contain require-dev information, run install with the --no-dev option or delete it and run composer update to generate a new lock file.".to_string()) .into()); } } @@ -270,12 +268,10 @@ impl Locker { return Ok(packages); } - Err(RuntimeException { - message: - "Your composer.lock is invalid. Run \"composer update\" to generate a new one." - .to_string(), - code: 0, - } + Err(RuntimeException::new( + "Your composer.lock is invalid. Run \"composer update\" to generate a new one." + .to_string(), + ) .into()) } @@ -438,10 +434,9 @@ impl Locker { } if !self.lock_file.exists() { - return Err(LogicException { - message: "No lockfile found. Unable to read locked packages".to_string(), - code: 0, - } + return Err(LogicException::new( + "No lockfile found. Unable to read locked packages".to_string(), + ) .into()); } @@ -582,7 +577,7 @@ impl Locker { let is_locked = match self.is_locked_result() { Ok(b) => b, Err(e) => { - if e.downcast_ref::<ParsingException>().is_some() { + if e.is_instanceof::<ParsingException>() { false } else { return Err(e); @@ -641,13 +636,10 @@ impl Locker { let contents = match contents { Some(s) => s, None => { - return Err(RuntimeException { - message: format!( - "Unable to read {} contents to update the lock file hash.", - composer_json.get_path() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Unable to read {} contents to update the lock file hash.", + composer_json.get_path() + )) .into()); } }; @@ -720,13 +712,10 @@ impl Locker { let version = package.get_pretty_version(); if name.is_empty() || version.is_empty() { - return Err(LogicException { - message: format!( - "Package \"{}\" has no version or name and can not be locked", - package, - ), - code: 0, - } + return Err(LogicException::new(format!( + "Package \"{}\" has no version or name and can not be locked", + package, + )) .into()); } diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs index bc99d08a..0f1e7d23 100644 --- a/crates/shirabe/src/package/package.rs +++ b/crates/shirabe/src/package/package.rs @@ -732,10 +732,9 @@ impl PackageInterface for Package { if let Some(existing) = self.repository.as_ref().and_then(|w| w.upgrade()) && !std::rc::Rc::ptr_eq(&existing, repository.as_rc()) { - return Err(LogicException { - message: "A package can only be added to one repository".to_string(), - code: 0, - } + return Err(LogicException::new( + "A package can only be added to one repository".to_string(), + ) .into()); } self.repository = Some(repository.downgrade()); diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs index 98853952..1968ff11 100644 --- a/crates/shirabe/src/package/version/version_guesser.rs +++ b/crates/shirabe/src/package/version/version_guesser.rs @@ -750,10 +750,9 @@ impl VersionGuesser { let version = match version { Some(v) if !v.is_empty() => v, _ => { - return Err(RuntimeException { - message: "COMPOSER_ROOT_VERSION not set or empty".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "COMPOSER_ROOT_VERSION not set or empty".to_string(), + ) .into()); } }; diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs index 84f2beef..733f342d 100644 --- a/crates/shirabe/src/package/version/version_selector.rs +++ b/crates/shirabe/src/package/version/version_selector.rs @@ -76,13 +76,10 @@ impl VersionSelector { show_warnings: ShowWarnings, ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> { if !base_package::STABILITIES.contains_key(preferred_stability) { - return Err(shirabe_php_shim::UnexpectedValueException { - message: format!( - "Expected a valid stability name as 3rd argument, got {}", - preferred_stability - ), - code: 0, - } + return Err(shirabe_php_shim::UnexpectedValueException::new(format!( + "Expected a valid stability name as 3rd argument, got {}", + preferred_stability + )) .into()); } diff --git a/crates/shirabe/src/plugin/php_plugin_proxy.rs b/crates/shirabe/src/plugin/php_plugin_proxy.rs index 686c05bd..af9d04db 100644 --- a/crates/shirabe/src/plugin/php_plugin_proxy.rs +++ b/crates/shirabe/src/plugin/php_plugin_proxy.rs @@ -2057,10 +2057,9 @@ impl PhpPluginProxy { Ok(_) => Ok(()), // TODO(plugin): the original exception class is collapsed to RuntimeException on // this side of the boundary. - Err(throw) => Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: throw.message, - code: throw.code, - })), + Err(throw) => { + Err(shirabe_php_shim::RuntimeException::with_code(throw.message, throw.code).into()) + } } } @@ -2080,10 +2079,9 @@ impl PhpPluginProxy { )?; match outcome { Ok(value) => Ok(value.to_php_mixed()?), - Err(throw) => Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: throw.message, - code: throw.code, - })), + Err(throw) => { + Err(shirabe_php_shim::RuntimeException::with_code(throw.message, throw.code).into()) + } } } } @@ -2160,10 +2158,11 @@ impl EventSubscriberInterface for PhpPluginProxy { // TODO(plugin): the original exception class is collapsed to RuntimeException on // this side of the boundary. Err(throw) => { - return Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: throw.message, - code: throw.code, - })); + return Err(shirabe_php_shim::RuntimeException::with_code( + throw.message, + throw.code, + ) + .into()); } }; decode_subscribed_events(&self.class, value) @@ -2191,10 +2190,11 @@ impl Capable for PhpPluginProxy { // TODO(plugin): the original exception class is collapsed to RuntimeException on // this side of the boundary. Err(throw) => { - return Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: throw.message, - code: throw.code, - })); + return Err(shirabe_php_shim::RuntimeException::with_code( + throw.message, + throw.code, + ) + .into()); } }; // PHP: `(array) $plugin->getCapabilities()` — the interface declares no return type, @@ -2289,12 +2289,10 @@ fn decode_listener_priority( } fn subscribed_events_shape_error(class: &str, value: &PluginValue) -> anyhow::Error { - anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( - "{class}::getSubscribedEvents() returned an unsupported shape over RPC: {value:?}" - ), - code: 0, - }) + shirabe_php_shim::RuntimeException::new(format!( + "{class}::getSubscribedEvents() returned an unsupported shape over RPC: {value:?}" + )) + .into() } impl Drop for PhpPluginProxy { @@ -2407,13 +2405,11 @@ impl PhpInstallerProxy { } fn unsupported_shape(&self, method: &str, value: &PluginValue) -> anyhow::Error { - anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( - "{}::{method}() returned an unsupported shape over RPC: {value:?}", - self.handle.class - ), - code: 0, - }) + shirabe_php_shim::RuntimeException::new(format!( + "{}::{method}() returned an unsupported shape over RPC: {value:?}", + self.handle.class + )) + .into() } } @@ -2632,15 +2628,11 @@ impl CommandProvider for PhpCommandProviderProxy { PluginValue::List(items) => items, PluginValue::Array(map) => map.into_values().collect(), _ => { - return Err(anyhow::anyhow!( - shirabe_php_shim::UnexpectedValueException { - message: format!( - "Plugin capability {} failed to return an array from getCommands", - self.handle.class - ), - code: 0, - } - )); + return Err(shirabe_php_shim::UnexpectedValueException::new(format!( + "Plugin capability {} failed to return an array from getCommands", + self.handle.class + )) + .into()); } }; let mut commands: Vec<std::rc::Rc<std::cell::RefCell<dyn BaseCommand>>> = Vec::new(); @@ -2661,13 +2653,10 @@ impl CommandProvider for PhpCommandProviderProxy { } fn invalid_command_error(capability: &PhpObjHandle) -> anyhow::Error { - anyhow::anyhow!(shirabe_php_shim::UnexpectedValueException { - message: format!( - "Plugin capability {} returned an invalid value, we expected an array of Composer\\Command\\BaseCommand objects", - capability.class - ), - code: 0, - }) + shirabe_php_shim::UnexpectedValueException::new(format!( + "Plugin capability {} returned an invalid value, we expected an array of Composer\\Command\\BaseCommand objects", + capability.class + )).into() } impl Drop for PhpCommandProviderProxy { @@ -2820,12 +2809,11 @@ impl PhpConsoleApplicationContext { let app = match value { PluginValue::PhpHandle(app) => app, other => { - return Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( + return Err(shirabe_php_shim::RuntimeException::new( + format!( "__shirabe_console_application_boot returned an unsupported shape over RPC: {other:?}" - ), - code: 0, - })); + ) + ).into()); } }; *self.app.borrow_mut() = Some(app.clone()); @@ -3058,13 +3046,11 @@ impl PhpCommandProxy { method: &str, value: &PluginValue, ) -> anyhow::Error { - anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( - "{}::{method}() returned an unsupported shape over RPC: {value:?}", - handle.class - ), - code: 0, - }) + shirabe_php_shim::RuntimeException::new(format!( + "{}::{method}() returned an unsupported shape over RPC: {value:?}", + handle.class + )) + .into() } } @@ -3081,14 +3067,11 @@ impl Command for PhpCommandProxy { ) -> anyhow::Result<i64> { let context = CONSOLE_APP_CONTEXT .with(|slot| slot.borrow().clone()) - .ok_or_else(|| { - anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( + .ok_or_else(|| -> anyhow::Error { + shirabe_php_shim::RuntimeException::new(format!( "cannot run plugin-provided command {}: no worker-side console application context was published", self.handle.class - ), - code: 0, - }) + )).into() })?; let app = context.booted_app()?; let input_line = input.borrow().__to_string(); @@ -3110,13 +3093,12 @@ impl Command for PhpCommandProxy { ) -> anyhow::Result<i64> { // `run` above never reaches this template hook; a direct call would bypass the // worker-side binding, so it stays an explicit error. - Err(anyhow::anyhow!(shirabe_php_shim::RuntimeException { - message: format!( + Err(shirabe_php_shim::RuntimeException::new( + format!( "plugin-provided command {} executes in the PHP worker through run(); execute() must not be called directly", self.handle.class - ), - code: 0, - })) + ) + ).into()) } fn is_proxy_command(&self) -> bool { diff --git a/crates/shirabe/src/plugin/plugin_blocked_exception.rs b/crates/shirabe/src/plugin/plugin_blocked_exception.rs index fcf52ace..b6a6070a 100644 --- a/crates/shirabe/src/plugin/plugin_blocked_exception.rs +++ b/crates/shirabe/src/plugin/plugin_blocked_exception.rs @@ -8,14 +8,12 @@ pub struct PluginBlockedException(pub UnexpectedValueException); impl PluginBlockedException { pub fn new(message: String) -> Self { - Self(UnexpectedValueException { message, code: 0 }) + Self(UnexpectedValueException::new(message)) } } -impl std::fmt::Display for PluginBlockedException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for PluginBlockedException {} +shirabe_php_shim::impl_php_exception!( + PluginBlockedException, + 0, + r"Composer\Plugin\PluginBlockedException" +); diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index 18239ce9..c68708a5 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -234,10 +234,7 @@ impl PluginManager { let requires_composer = match requires_composer { Some(r) => r, None => { - return Err(RuntimeException { - message: format!("Plugin {} is missing a require statement for a version of the composer-plugin-api package.", package.get_name()), - code: 0, - }.into()); + return Err(RuntimeException::new(format!("Plugin {} is missing a require statement for a version of the composer-plugin-api package.", package.get_name())).into()); } }; @@ -316,10 +313,7 @@ impl PluginManager { _ => false, }; if class_is_empty { - return Err(UnexpectedValueException { - message: format!("Error while installing {}, composer-plugin packages should have a class defined in their extra key to be usable.", package.get_pretty_name()), - code: 0, - }.into()); + return Err(UnexpectedValueException::new(format!("Error while installing {}, composer-plugin packages should have a class defined in their extra key to be usable.", package.get_pretty_name())).into()); } // PHP: is_array($extra['class']) ? $extra['class'] : [$extra['class']] — an associative // array iterates its values too, and a non-string entry reaches class_exists() where it @@ -470,14 +464,11 @@ impl PluginManager { if old_installer_plugin { if !self.php_runtime_is_a(&class, "Composer\\Installer\\InstallerInterface")? { - return Err(RuntimeException { - message: format!( - "Could not activate plugin \"{}\" as \"{}\" does not implement Composer\\Installer\\InstallerInterface", - package.get_name(), - class - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not activate plugin \"{}\" as \"{}\" does not implement Composer\\Installer\\InstallerInterface", + package.get_name(), + class + )) .into()); } self.io.write_error(&format!( @@ -511,14 +502,11 @@ impl PluginManager { .push(PluginOrInstaller::Installer(installer)); } else if self.php_runtime_class_exists(&class, true)? { if !self.php_runtime_is_a(&class, "Composer\\Plugin\\PluginInterface")? { - return Err(RuntimeException { - message: format!( - "Could not activate plugin \"{}\" as \"{}\" does not implement Composer\\Plugin\\PluginInterface", - package.get_name(), - class - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not activate plugin \"{}\" as \"{}\" does not implement Composer\\Plugin\\PluginInterface", + package.get_name(), + class + )) .into()); } let handle = self.php_runtime_new_object(&class)?; @@ -534,14 +522,11 @@ impl PluginManager { .or_default() .push(PluginOrInstaller::Plugin(plugin)); } else if fail_on_missing_classes { - return Err(UnexpectedValueException { - message: format!( - "Plugin {} could not be initialized, class not found: {}", - package.get_name(), - class - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Plugin {} could not be initialized, class not found: {}", + package.get_name(), + class + )) .into()); } } @@ -1033,14 +1018,11 @@ impl PluginManager { // || !trim(...)). Once the first branch has declined, a present key always fails one // of the three disjuncts, so a present key unconditionally throws here. if let Some(value) = capabilities.get(capability) { - return Err(UnexpectedValueException { - message: format!( - "Plugin {} provided invalid capability class name(s), got {}", - plugin.get_class_name(), - var_export(value, true) - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Plugin {} provided invalid capability class name(s), got {}", + plugin.get_class_name(), + var_export(value, true) + )) .into()); } @@ -1066,14 +1048,11 @@ impl PluginManager { Some(&mut PluginRpcDispatcher::default()), ))?; if !matches!(exists, PluginValue::Bool(true)) { - return Err(RuntimeException { - message: format!( - "Cannot instantiate Capability, as class {} from plugin {} does not exist.", - capability_class, - plugin.get_class_name() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Cannot instantiate Capability, as class {} from plugin {} does not exist.", + capability_class, + plugin.get_class_name() + )) .into()); } @@ -1116,12 +1095,9 @@ impl PluginManager { if !php_is_a(&handle, "Composer\\Plugin\\Capability\\Capability")? || !php_is_a(&handle, capability_class_name)? { - return Err(RuntimeException { - message: format!( - "Class {capability_class} must implement both Composer\\Plugin\\Capability\\Capability and {capability_class_name}." - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Class {capability_class} must implement both Composer\\Plugin\\Capability\\Capability and {capability_class_name}." + )) .into()); } diff --git a/crates/shirabe/src/question/strict_confirmation_question.rs b/crates/shirabe/src/question/strict_confirmation_question.rs index 5141eb58..0fed4378 100644 --- a/crates/shirabe/src/question/strict_confirmation_question.rs +++ b/crates/shirabe/src/question/strict_confirmation_question.rs @@ -71,11 +71,8 @@ impl StrictConfirmationQuestion { Box::new(|answer: Option<PhpMixed>| { let answer = answer.unwrap_or(PhpMixed::Null); if !is_bool(&answer) { - return Err(InvalidArgumentException( - shirabe_php_shim::InvalidArgumentException { - message: "Please answer yes, y, no, or n.".to_string(), - code: 0, - }, + return Err(InvalidArgumentException::new( + "Please answer yes, y, no, or n.".to_string(), )); } Ok(answer) diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs index 99f340d1..2d16f137 100644 --- a/crates/shirabe/src/repository/artifact_repository.rs +++ b/crates/shirabe/src/repository/artifact_repository.rs @@ -47,10 +47,9 @@ impl ArtifactRepository { io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> anyhow::Result<Self> { if !extension_loaded("zip") { - return Err(RuntimeException { - message: "The artifact repository requires PHP's zip extension".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "The artifact repository requires PHP's zip extension".to_string(), + ) .into()); } @@ -171,13 +170,10 @@ impl ArtifactRepository { } else if file_extension == "zip" { file_type = "zip"; } else { - return Err(RuntimeException { - message: format!( - "Files with \"{}\" extensions aren't supported. Only ZIP and TAR/TAR.GZ/TGZ archives are supported.", - file_extension - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Files with \"{}\" extensions aren't supported. Only ZIP and TAR/TAR.GZ/TGZ archives are supported.", + file_extension + )) .into()); } @@ -228,10 +224,10 @@ impl ArtifactRepository { .unwrap_or_default(); match self.loader.load(cfg, None) { Ok(package) => Ok(Some(package)), - Err(exception) => Err(UnexpectedValueException { - message: format!("Failed loading package in {}: {}", pathname, exception), - code: 0, - } + Err(exception) => Err(UnexpectedValueException::new(format!( + "Failed loading package in {}: {}", + pathname, exception + )) .into()), } } diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index b2476e1e..5371c2c3 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -38,6 +38,7 @@ use futures::stream::FuturesOrdered; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_metadata_minifier::MetadataMinifier; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ CmpOp, InvalidArgumentException, LogicException, PHP_EOL, PhpMixed, RuntimeException, UnexpectedValueException, extension_loaded, hash, http_build_query, json_decode, parse_url_all, @@ -183,10 +184,9 @@ impl ComposerRepository { .to_string(); repo_config.insert("url".to_string(), PhpMixed::String(url_after.clone())); if url_after.is_empty() { - return Err(InvalidArgumentException { - message: "The repository url must not be an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "The repository url must not be an empty string".to_string(), + ) .into()); } @@ -214,10 +214,10 @@ impl ComposerRepository { .and_then(|v| v.as_string()) .is_some_and(|s| !s.is_empty()); if url_bits_arr.is_none() || !scheme_present { - return Err(UnexpectedValueException { - message: format!("Invalid url given for Composer repository: {}", current_url), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Invalid url given for Composer repository: {}", + current_url + )) .into()); } @@ -387,12 +387,10 @@ impl ComposerRepository { if self.has_partial_packages()? { if self.partial_packages_by_name.is_none() { - return Err(LogicException { - message: - "hasPartialPackages failed to initialize $this->partialPackagesByName" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "hasPartialPackages failed to initialize $this->partialPackagesByName" + .to_string(), + ) .into()); } @@ -403,17 +401,11 @@ impl ComposerRepository { .create_packages(flat, Some("packages.json inline packages".to_string())); } - return Err(LogicException { - message: "Composer repositories that have lazy providers and no available-packages list can not load the complete list of packages, use getPackageNames instead.".to_string(), - code: 0, - }.into()); + return Err(LogicException::new("Composer repositories that have lazy providers and no available-packages list can not load the complete list of packages, use getPackageNames instead.".to_string()).into()); } if has_providers { - return Err(LogicException { - message: "Composer repositories that have providers can not load the complete list of packages, use getPackageNames instead.".to_string(), - code: 0, - }.into()); + return Err(LogicException::new("Composer repositories that have providers can not load the complete list of packages, use getPackageNames instead.".to_string()).into()); } // PHP relies on ArrayRepository::getPackages() invoking the virtual initialize(), @@ -516,10 +508,9 @@ impl ComposerRepository { fn load_package_list(&mut self, package_filter: Option<&str>) -> anyhow::Result<Vec<String>> { if self.list_url.is_none() { - return Err(LogicException { - message: "Make sure to call loadRootServerFile before loadPackageList".to_string(), - code: 0, - } + return Err(LogicException::new( + "Make sure to call loadRootServerFile before loadPackageList".to_string(), + ) .into()); } @@ -636,10 +627,7 @@ impl ComposerRepository { let constraint = package_name_map.get(&name).and_then(|c| c.clone()); for (_uid, candidate) in candidates.iter() { if candidate.get_name() != name { - return Err(LogicException { - message: "whatProvides should never return a package with a different name than the requested one".to_string(), - code: 0, - }.into()); + return Err(LogicException::new("whatProvides should never return a package with a different name than the requested one".to_string()).into()); } names_found.insert(name.clone(), true); @@ -946,16 +934,13 @@ impl ComposerRepository { if !allow_partial_advisories && !is_full { let data_mixed = PhpMixed::Array(data.iter().map(|(k, v)| (k.clone(), v.clone())).collect()); - return Err(RuntimeException { - message: format!( - "Advisory for {} could not be loaded as a full advisory from {}{}{}", - name, - repo_name, - PHP_EOL, - var_export(&data_mixed, true), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Advisory for {} could not be loaded as a full advisory from {}{}{}", + name, + repo_name, + PHP_EOL, + var_export(&data_mixed, true), + )) .into()); } let affected_versions: &AnyConstraint = advisory.affected_versions(); @@ -1151,7 +1136,7 @@ impl ComposerRepository { ) { Ok(resp) => resp.decode_json()?, Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_status_code() == Some(404) { return Ok(result); @@ -1183,11 +1168,10 @@ impl ComposerRepository { if self.has_partial_packages()? { if self.partial_packages_by_name.is_none() { - return Err(LogicException { - message: "hasPartialPackages failed to initialize $this->partialPackagesByName" + return Err(LogicException::new( + "hasPartialPackages failed to initialize $this->partialPackagesByName" .to_string(), - code: 0, - } + ) .into()); } for (_name, versions) in self.partial_packages_by_name.as_ref().unwrap().iter() { @@ -1438,7 +1422,7 @@ impl ComposerRepository { } Err(e) => { // 404s are acceptable for lazy provider repos - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { let status_code = te.get_status_code(); if self.lazy_providers_url.is_some() && matches!(status_code, Some(404 | 499)) @@ -1726,12 +1710,10 @@ impl ComposerRepository { let mut names_found: IndexMap<String, bool> = IndexMap::new(); if self.lazy_providers_url.is_none() { - return Err(LogicException { - message: - "loadAsyncPackages only supports v2 protocol composer repos with a metadata-url" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "loadAsyncPackages only supports v2 protocol composer repos with a metadata-url" + .to_string(), + ) .into()); } @@ -1964,10 +1946,7 @@ impl ComposerRepository { package_name: Option<&str>, ) -> anyhow::Result<PhpMixed> { if self.lazy_providers_url.is_none() { - return Err(LogicException { - message: "startCachedAsyncDownload only supports v2 protocol composer repos with a metadata-url".to_string(), - code: 0, - }.into()); + return Err(LogicException::new("startCachedAsyncDownload only supports v2 protocol composer repos with a metadata-url".to_string()).into()); } let name = strtolower(file_name); @@ -2142,13 +2121,11 @@ impl ComposerRepository { } if !extension_loaded("openssl") && self.url.starts_with("https") { - return Err(RuntimeException { - message: format!( - "You must enable the openssl extension in your php.ini to load information from {}", - self.url - ), - code: 0, - }.into()); + return Err(RuntimeException::new(format!( + "You must enable the openssl extension in your php.ini to load information from {}", + self.url + )) + .into()); } let mut data: Option<IndexMap<String, PhpMixed>> = None; @@ -2387,13 +2364,10 @@ impl ComposerRepository { api_url: api_url.clone(), }); if api_url.is_none() && !self.has_available_package_list { - return Err(UnexpectedValueException { - message: format!( - "Invalid security advisory configuration on {}: If the repository does not provide a security-advisories.api-url then available-packages or available-package-patterns are required to be provided for performance reason.", - self.get_repo_name() - ), - code: 0, - }.into()); + return Err(UnexpectedValueException::new(format!( + "Invalid security advisory configuration on {}: If the repository does not provide a security-advisories.api-url then available-packages or available-package-patterns are required to be provided for performance reason.", + self.get_repo_name() + )).into()); } } } @@ -2457,10 +2431,9 @@ impl ComposerRepository { fn canonicalize_url(&self, url: &str) -> anyhow::Result<String> { if url.is_empty() { - return Err(InvalidArgumentException { - message: "Expected a string with a value and not an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "Expected a string with a value and not an empty string".to_string(), + ) .into()); } @@ -2491,11 +2464,9 @@ impl ComposerRepository { let data = self.load_root_server_file(None)?; let data = match data { RootData::True => { - return Err(LogicException { - message: "loadRootServerFile should not return true during initialization" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "loadRootServerFile should not return true during initialization".to_string(), + ) .into()); } RootData::Data(d) => d, @@ -2724,19 +2695,16 @@ impl ComposerRepository { })(); result.map_err(|e| { - RuntimeException { - message: format!( - "Could not load packages in {}{}: [{}] {}", - self.get_repo_name(), - source - .as_ref() - .map(|s| format!(" from {}", s)) - .unwrap_or_default(), - "Exception", - e - ), - code: 0, - } + RuntimeException::new(format!( + "Could not load packages in {}{}: [{}] {}", + self.get_repo_name(), + source + .as_ref() + .map(|s| format!(" from {}", s)) + .unwrap_or_default(), + "Exception", + e + )) .into() }) } @@ -2749,10 +2717,9 @@ impl ComposerRepository { store_last_modified_time: bool, ) -> anyhow::Result<IndexMap<String, PhpMixed>> { if filename.is_empty() { - return Err(InvalidArgumentException { - message: "$filename should not be an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "$filename should not be an empty string".to_string(), + ) .into()); } @@ -2830,13 +2797,10 @@ impl ComposerRepository { } // TODO use scarier wording once we know for sure it doesn't do false positives anymore - return Err(RepositorySecurityException(shirabe_php_shim::Exception { - message: format!( - "The contents of {} do not match its signature. This could indicate a man-in-the-middle attack or e.g. antivirus software corrupting files. Try running composer again and report this if you think it is a mistake.", - filename - ), - code: 0, - }).into()); + return Err(RepositorySecurityException::new(format!( + "The contents of {} do not match its signature. This could indicate a man-in-the-middle attack or e.g. antivirus software corrupting files. Try running composer again and report this if you think it is a mistake.", + filename + )).into()); } if let Some(dispatcher) = self.event_dispatcher.as_ref() { @@ -2904,15 +2868,15 @@ impl ComposerRepository { if e.downcast_ref::<RetryMarker>().is_some() { continue; } - if e.downcast_ref::<LogicException>().is_some() { + if e.is_instanceof::<LogicException>() { return Err(e); } - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_status_code() == Some(404) { return Err(e); } - if e.downcast_ref::<RepositorySecurityException>().is_some() { + if e.is_instanceof::<RepositorySecurityException>() { return Err(e); } @@ -2948,10 +2912,7 @@ impl ComposerRepository { match data { Some(d) => Ok(d), - None => Err(LogicException { - message: "ComposerRepository: Undefined $data. Please report at https://github.com/composer/composer/issues/new.".to_string(), - code: 0, - }.into()), + None => Err(LogicException::new("ComposerRepository: Undefined $data. Please report at https://github.com/composer/composer/issues/new.".to_string()).into()), } } @@ -2962,10 +2923,9 @@ impl ComposerRepository { last_modified_time: &str, ) -> anyhow::Result<FetchFileIfLastModifiedResult> { if filename.is_empty() { - return Err(InvalidArgumentException { - message: "$filename should not be an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "$filename should not be an empty string".to_string(), + ) .into()); } @@ -3079,10 +3039,10 @@ impl ComposerRepository { match result { Ok(v) => Ok(v), Err(e) => { - if e.downcast_ref::<LogicException>().is_some() { + if e.is_instanceof::<LogicException>() { return Err(e); } - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_status_code() == Some(404) { return Err(e); @@ -3109,10 +3069,9 @@ impl ComposerRepository { last_modified_time: Option<&str>, ) -> anyhow::Result<PhpMixed> { if filename.is_empty() { - return Err(InvalidArgumentException { - message: "$filename should not be an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "$filename should not be an empty string".to_string(), + ) .into()); } @@ -3263,7 +3222,7 @@ impl ComposerRepository { cache_key: &str, last_modified_time: Option<&str>, ) -> anyhow::Result<PhpMixed> { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_status_code() == Some(404) { self.packages_not_found_cache @@ -3289,7 +3248,7 @@ impl ComposerRepository { } // special error code returned when network is being artificially disabled - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && te.get_status_code() == Some(499) { let resp = Response::new(self.url.clone(), Some(404), Vec::new(), Some(String::new())); @@ -3364,10 +3323,7 @@ impl ComposerRepository { /// @return true if the package name is present in availablePackages or matched by availablePackagePatterns pub(crate) fn lazy_providers_repo_contains(&self, name: &str) -> anyhow::Result<bool> { if !self.has_available_package_list { - return Err(LogicException { - message: "lazyProvidersRepoContains should not be called unless hasAvailablePackageList is true".to_string(), - code: 0, - }.into()); + return Err(LogicException::new("lazyProvidersRepoContains should not be called unless hasAvailablePackageList is true".to_string()).into()); } if let Some(ref available) = self.available_packages diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs index 47241bde..d9085bfd 100644 --- a/crates/shirabe/src/repository/filesystem_repository.rs +++ b/crates/shirabe/src/repository/filesystem_repository.rs @@ -19,9 +19,9 @@ use crate::util::Filesystem; use crate::util::Platform; use indexmap::IndexMap; use shirabe_php_shim::{ - Exception, InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, - array_flip, dirname, get_class_err, get_debug_type, in_array_strict, is_array, is_null, - is_string, ksort, realpath, str_repeat, usort, var_export, + AnyThrowable, InvalidArgumentException, LogicException, PhpClass as _, PhpMixed, + UnexpectedValueException, array_flip, dirname, get_debug_type, in_array_strict, is_array, + is_null, is_string, ksort, realpath, str_repeat, usort, var_export, }; use shirabe_semver::constraint::AnyConstraint; @@ -57,10 +57,9 @@ impl FilesystemRepository { let filesystem = filesystem .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None)))); if dump_versions && root_package.is_none() { - return Err(InvalidArgumentException { - message: "Expected a root package instance if $dumpVersions is true".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "Expected a root package instance if $dumpVersions is true".to_string(), + ) .into()); } Ok(Self { @@ -134,10 +133,9 @@ impl FilesystemRepository { } if !is_array(&packages_value) { - return Err(UnexpectedValueException { - message: "Could not parse package list from the repository".to_string(), - code: 0, - } + return Err(UnexpectedValueException::new( + "Could not parse package list from the repository".to_string(), + ) .into()); } @@ -145,15 +143,14 @@ impl FilesystemRepository { })() { Ok(p) => p, Err(e) => { - return Err(InvalidRepositoryException(Exception { - message: format!( - "Invalid repository data in {}, packages could not be loaded: [{}] {}", - self.file.get_path(), - get_class_err(&e), - e, - ), - code: 0, - }) + return Err(InvalidRepositoryException::new(format!( + "Invalid repository data in {}, packages could not be loaded: [{}] {}", + self.file.get_path(), + AnyThrowable::of(e.as_ref()) + .expect("PHP reaches this only with a caught \\Throwable") + .php_class_name(), + e, + )) .into()); } }; @@ -464,12 +461,10 @@ impl FilesystemRepository { self.inner.get_packages()?.into_iter().collect(); let mut current_root: RootPackageInterfaceHandle = match &self.root_package { None => { - return Err(LogicException { - message: - "It should not be possible to dump packages if no root package is given" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "It should not be possible to dump packages if no root package is given" + .to_string(), + ) .into()); } Some(r) => r.clone(), diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs index d7be0a9f..98cef1d0 100644 --- a/crates/shirabe/src/repository/filter_repository.rs +++ b/crates/shirabe/src/repository/filter_repository.rs @@ -49,13 +49,10 @@ impl FilterRepository { )); } _ => { - return Err(InvalidArgumentException { - message: format!( - r#""only" key for repository {} should be an array"#, - repo.get_repo_name()? - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + r#""only" key for repository {} should be an array"#, + repo.get_repo_name()? + )) .into()); } } @@ -79,25 +76,19 @@ impl FilterRepository { )); } _ => { - return Err(InvalidArgumentException { - message: format!( - r#""exclude" key for repository {} should be an array"#, - repo.get_repo_name()? - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + r#""exclude" key for repository {} should be an array"#, + repo.get_repo_name()? + )) .into()); } } } if exclude.is_some() && only.is_some() { - return Err(InvalidArgumentException { - message: format!( - r#"Only one of "only" and "exclude" can be specified for repository {}"#, - repo.get_repo_name()? - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + r#"Only one of "only" and "exclude" can be specified for repository {}"#, + repo.get_repo_name()? + )) .into()); } if let Some(canonical_val) = options.get("canonical") { @@ -106,13 +97,10 @@ impl FilterRepository { canonical = *b; } _ => { - return Err(InvalidArgumentException { - message: format!( - r#""canonical" key for repository {} should be a boolean"#, - repo.get_repo_name()? - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + r#""canonical" key for repository {} should be a boolean"#, + repo.get_repo_name()? + )) .into()); } } diff --git a/crates/shirabe/src/repository/invalid_repository_exception.rs b/crates/shirabe/src/repository/invalid_repository_exception.rs index e7aa8509..ed84759c 100644 --- a/crates/shirabe/src/repository/invalid_repository_exception.rs +++ b/crates/shirabe/src/repository/invalid_repository_exception.rs @@ -8,14 +8,12 @@ pub struct InvalidRepositoryException(pub Exception); impl InvalidRepositoryException { pub fn new(message: String) -> Self { - Self(Exception { message, code: 0 }) + Self(Exception::new(message)) } } -impl std::fmt::Display for InvalidRepositoryException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for InvalidRepositoryException {} +shirabe_php_shim::impl_php_exception!( + InvalidRepositoryException, + 0, + r"Composer\Repository\InvalidRepositoryException" +); diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs index 62c8c5b3..810b0e13 100644 --- a/crates/shirabe/src/repository/package_repository.rs +++ b/crates/shirabe/src/repository/package_repository.rs @@ -16,7 +16,7 @@ use crate::repository::{ }; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_php_shim::{Exception, PhpMixed, RuntimeException, php_regex, var_export}; +use shirabe_php_shim::{PhpMixed, RuntimeException, php_regex, var_export}; use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] @@ -72,10 +72,7 @@ impl PackageRepository { e, shirabe_php_shim::json_encode(package).unwrap_or_default() ); - return Ok(Err(InvalidRepositoryException(Exception { - message: msg, - code: 0, - }))); + return Ok(Err(InvalidRepositoryException::new(msg))); } }; self.inner.add_package(package_loaded)?; @@ -101,7 +98,7 @@ impl PackageRepository { // skips re-initializing it. fn ensure_initialized(&self) -> anyhow::Result<()> { if !self.inner.is_initialized() { - self.initialize()?.map_err(anyhow::Error::new)?; + self.initialize()?.map_err(anyhow::Error::from)?; } Ok(()) } @@ -234,15 +231,13 @@ impl AdvisoryProviderInterface for PackageRepository { }; if !allow_partial_advisories && matches!(advisory, AnySecurityAdvisory::Partial(_)) { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Advisory for {} could not be loaded as a full advisory from {}\n{}", - package_name, - self.get_repo_name()?, - var_export(data, true) - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Advisory for {} could not be loaded as a full advisory from {}\n{}", + package_name, + self.get_repo_name()?, + var_export(data, true) + )) + .into()); } if !advisory.affected_versions().matches(package_constraint) { diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs index ec2da043..313fc75e 100644 --- a/crates/shirabe/src/repository/path_repository.rs +++ b/crates/shirabe/src/repository/path_repository.rs @@ -56,11 +56,9 @@ impl PathRepository { process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, ) -> anyhow::Result<Self> { if !repo_config.contains_key("url") { - return Err(RuntimeException { - message: "You must specify the `url` configuration for the path repository" - .to_string(), - code: 0, - } + return Err(RuntimeException::new( + "You must specify the `url` configuration for the path repository".to_string(), + ) .into()); } @@ -172,13 +170,10 @@ impl PathRepository { } } - return Err(RuntimeException { - message: format!( - "The `url` supplied for the path ({}) repository does not exist", - self.url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The `url` supplied for the path ({}) repository does not exist", + self.url + )) .into()); } @@ -355,10 +350,10 @@ impl PathRepository { self.inner .add_package(self.loader.load(package.clone(), None).map_err(|e| { - RuntimeException { - message: format!("Failed loading the package in {}", composer_file_path), - code: 0, - } + RuntimeException::new(format!( + "Failed loading the package in {}", + composer_file_path + )) })?); } @@ -371,13 +366,10 @@ impl PathRepository { if defined("GLOB_BRACE") { flags |= GLOB_BRACE; } else if self.url.contains('{') || self.url.contains('}') { - return Err(RuntimeException { - message: format!( - "The operating system does not support GLOB_BRACE which is required for the url {}", - self.url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "The operating system does not support GLOB_BRACE which is required for the url {}", + self.url + )) .into()); } diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs index 944bb4db..0b32e157 100644 --- a/crates/shirabe/src/repository/platform_repository.rs +++ b/crates/shirabe/src/repository/platform_repository.rs @@ -65,24 +65,20 @@ impl PlatformRepository { let mut overrides_map: IndexMap<String, PlatformOverride> = IndexMap::new(); for (name, version) in overrides { if !is_string(&version) && !matches!(version, PhpMixed::Bool(false)) { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: format!( - "config.platform.{} should be a string or false, but got {} {}", - name, - shirabe_php_shim::get_debug_type(&version), - var_export(&version, true) - ), - code: 0, - })); + return Err(UnexpectedValueException::new(format!( + "config.platform.{} should be a string or false, but got {} {}", + name, + shirabe_php_shim::get_debug_type(&version), + var_export(&version, true) + )) + .into()); } if name == "php" && matches!(version, PhpMixed::Bool(false)) { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: format!( - "config.platform.{} cannot be set to false as you cannot disable php entirely.", - name - ), - code: 0, - })); + return Err(UnexpectedValueException::new(format!( + "config.platform.{} cannot be set to false as you cannot disable php entirely.", + name + )) + .into()); } overrides_map.insert( strtolower(&name), @@ -153,13 +149,11 @@ impl PlatformRepository { for r#override in &overrides { // Check that it's a platform package. if !Self::is_platform_package(&r#override.name) { - return Err(anyhow::anyhow!(InvalidArgumentException { - message: format!( - "Invalid platform package name in config.platform: {}", - r#override.name - ), - code: 0, - })); + return Err(InvalidArgumentException::new(format!( + "Invalid platform package name in config.platform: {}", + r#override.name + )) + .into()); } if !matches!(r#override.version, PhpMixed::Bool(false)) { @@ -1488,13 +1482,11 @@ impl PlatformRepository { pub fn add_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<()> { if package.as_complete().is_none() { - return Err(anyhow::anyhow!(UnexpectedValueException { - message: format!( - "Expected CompletePackage but got {}", - get_class(&PhpMixed::Null) - ), - code: 0, - })); + return Err(UnexpectedValueException::new(format!( + "Expected CompletePackage but got {}", + get_class(&PhpMixed::Null) + )) + .into()); } let name = package.get_name(); diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs index 856f58ad..a15d3a97 100644 --- a/crates/shirabe/src/repository/repository_factory.rs +++ b/crates/shirabe/src/repository/repository_factory.rs @@ -73,10 +73,7 @@ impl RepositoryFactory { repo_config.insert("json".to_string(), PhpMixed::String(repository.to_string())); return Ok(repo_config); } else { - return Err(InvalidArgumentException { - message: format!("Invalid repository URL ({}) given. This file does not contain a valid composer repository.", repository), - code: 0, - }.into()); + return Err(InvalidArgumentException::new(format!("Invalid repository URL ({}) given. This file does not contain a valid composer repository.", repository)).into()); } } @@ -87,10 +84,7 @@ impl RepositoryFactory { return Ok(repo_config); } - Err(InvalidArgumentException { - message: format!("Invalid repository url ({}) given. Has to be a .json file, an http url or a JSON object.", repository), - code: 0, - }.into()) + Err(InvalidArgumentException::new(format!("Invalid repository url ({}) given. Has to be a .json file, an http url or a JSON object.", repository)).into()) } pub fn from_string( @@ -121,13 +115,9 @@ impl RepositoryFactory { let repos = Self::create_repos(rm, vec![PhpMixed::Array(repo_config.into_iter().collect())])?; // PHP: return current($repos); - let (_, first) = repos - .into_iter() - .next() - .ok_or_else(|| UnexpectedValueException { - message: "create_repos returned no repository".to_string(), - code: 0, - })?; + let (_, first) = repos.into_iter().next().ok_or_else(|| { + UnexpectedValueException::new("create_repos returned no repository".to_string()) + })?; Ok(first) } @@ -149,10 +139,11 @@ impl RepositoryFactory { let rm = if let Some(rm) = rm { rm } else { - let io = io.ok_or_else(|| InvalidArgumentException { - message: "This function requires either an IOInterface or a RepositoryManager" - .to_string(), - code: 0, + let io = io.ok_or_else(|| { + InvalidArgumentException::new( + "This function requires either an IOInterface or a RepositoryManager" + .to_string(), + ) })?; owned_rm = Self::manager( io.clone(), @@ -243,21 +234,15 @@ impl RepositoryFactory { for (index, repo) in repo_configs.into_iter().enumerate() { match &repo { PhpMixed::String(_) => { - return Err(UnexpectedValueException { - message: "\"repositories\" should be an array of repository definitions, only a single repository was given".to_string(), - code: 0, - }.into()); + return Err(UnexpectedValueException::new("\"repositories\" should be an array of repository definitions, only a single repository was given".to_string()).into()); } PhpMixed::Array(repo_arr) => { if !repo_arr.contains_key("type") { - return Err(UnexpectedValueException { - message: format!( - "Repository \"{}\" ({}) must have a type defined", - index, - json_encode(&repo).unwrap_or_default() - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Repository \"{}\" ({}) must have a type defined", + index, + json_encode(&repo).unwrap_or_default() + )) .into()); } let repo_type = repo_arr @@ -296,15 +281,12 @@ impl RepositoryFactory { } } _ => { - return Err(UnexpectedValueException { - message: format!( - "Repository \"{}\" ({}) should be an array, {} given", - index, - json_encode(&repo).unwrap_or_default(), - get_debug_type(&repo) - ), - code: 0, - } + return Err(UnexpectedValueException::new(format!( + "Repository \"{}\" ({}) should be an array, {} given", + index, + json_encode(&repo).unwrap_or_default(), + get_debug_type(&repo) + )) .into()); } } diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs index 7fd2f80a..10ac973d 100644 --- a/crates/shirabe/src/repository/repository_manager.rs +++ b/crates/shirabe/src/repository/repository_manager.rs @@ -100,10 +100,10 @@ impl RepositoryManager { name: Option<&str>, ) -> anyhow::Result<RepositoryInterfaceHandle> { if !self.repository_classes.contains_key(r#type) { - return Err(InvalidArgumentException { - message: format!("Repository type is not registered: {}", r#type), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "Repository type is not registered: {}", + r#type + )) .into()); } @@ -193,10 +193,10 @@ impl RepositoryManager { )), // TODO(plugin): `setRepositoryClass` lets a plugin register a repository class of // its own, which needs a Rust-side counterpart before it can be built here. - other => Err(anyhow::anyhow!(RuntimeException { - message: format!("Repository class has no Rust implementation: {other}"), - code: 0, - })), + other => Err(RuntimeException::new(format!( + "Repository class has no Rust implementation: {other}" + )) + .into()), } } diff --git a/crates/shirabe/src/repository/repository_security_exception.rs b/crates/shirabe/src/repository/repository_security_exception.rs index b517cad9..13f2fe72 100644 --- a/crates/shirabe/src/repository/repository_security_exception.rs +++ b/crates/shirabe/src/repository/repository_security_exception.rs @@ -6,10 +6,14 @@ use shirabe_php_shim::Exception; #[derive(Debug)] pub struct RepositorySecurityException(pub Exception); -impl std::fmt::Display for RepositorySecurityException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) +impl RepositorySecurityException { + pub fn new(message: String) -> Self { + Self(Exception::new(message)) } } -impl std::error::Error for RepositorySecurityException {} +shirabe_php_shim::impl_php_exception!( + RepositorySecurityException, + 0, + r"Composer\Repository\RepositorySecurityException" +); diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs index def73f14..bd57ec20 100644 --- a/crates/shirabe/src/repository/repository_set.rs +++ b/crates/shirabe/src/repository/repository_set.rs @@ -21,6 +21,7 @@ use crate::repository::LockArrayRepositoryHandle; use crate::repository::PlatformRepository; use crate::repository::{FindPackageConstraint, RepositoryInterfaceHandle}; use indexmap::IndexMap; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{LogicException, RuntimeException, ksort, strtolower}; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; @@ -155,10 +156,7 @@ impl RepositorySet { /// @param RepositoryInterface $repo A package repository pub fn add_repository(&mut self, repo: RepositoryInterfaceHandle) -> anyhow::Result<()> { if self.locked { - return Err(RuntimeException { - message: "Pool has already been created from this repository set, it cannot be modified anymore.".to_string(), - code: 0, - } + return Err(RuntimeException::new("Pool has already been created from this repository set, it cannot be modified anymore.".to_string()) .into()); } @@ -374,17 +372,17 @@ impl RepositorySet { Err(e) => { // PHP catches only \Composer\Downloader\TransportException; other // exceptions propagate uncaught. - if e.downcast_ref::<TransportException>().is_none() { + if !e.is_instanceof::<TransportException>() { return Err(e); } if !ignore_unreachable { return Err(e); } let message = e - .downcast_ref::<TransportException>() + .catch::<TransportException>() .unwrap() - .message - .clone(); + .get_message() + .to_string(); unreachable_repos.push(message); } } @@ -482,11 +480,9 @@ impl RepositorySet { || repo_ref.as_any().is::<InstalledRepository>() }; if is_installed && !self.allow_installed_repositories { - return Err(LogicException { - message: "The pool can not accept packages from an installed repository" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "The pool can not accept packages from an installed repository".to_string(), + ) .into()); } } @@ -505,11 +501,9 @@ impl RepositorySet { || repo_ref.as_any().is::<InstalledRepository>() }; if is_installed && !self.allow_installed_repositories { - return Err(LogicException { - message: "The pool can not accept packages from an installed repository" - .to_string(), - code: 0, - } + return Err(LogicException::new( + "The pool can not accept packages from an installed repository".to_string(), + ) .into()); } } @@ -582,10 +576,7 @@ impl RepositorySet { let mut allowed_packages: Vec<String> = vec![]; for package_name in &package_names { if PlatformRepository::is_platform_package(package_name) { - return Err(LogicException { - message: "createPoolForPackage(s) can not be used for platform packages, as they are never loaded by the PoolBuilder which expects them to be fixed. Use createPoolWithAllPackages or pass in a proper request with the platform packages you need fixed in it.".to_string(), - code: 0, - } + return Err(LogicException::new("createPoolForPackage(s) can not be used for platform packages, as they are never loaded by the PoolBuilder which expects them to be fixed. Use createPoolWithAllPackages or pass in a proper request with the platform packages you need fixed in it.".to_string()) .into()); } diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs index 0719701f..e4ebe379 100644 --- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs +++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs @@ -16,6 +16,7 @@ use crate::util::ForgejoUrl; use crate::util::http::Response; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, php_regex, urlencode, }; @@ -107,7 +108,7 @@ impl ForgejoDriver { ); let response = self .get_contents(&resource_url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let mut resource = response.decode_json()?; // The Forgejo contents API only returns files up to 1MB as base64 encoded files; @@ -134,7 +135,7 @@ impl ForgejoDriver { if let Some(git_url) = git_url { resource = self .get_contents(&git_url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; } } @@ -157,22 +158,22 @@ impl ForgejoDriver { Some(b64) => match base64_decode(&b64) { Some(bytes) => match String::from_utf8(bytes) { Ok(s) => Ok(Some(s)), - Err(_) => Err(RuntimeException { - message: format!("Could not retrieve {} for {}", file, identifier), - code: 0, - } + Err(_) => Err(RuntimeException::new(format!( + "Could not retrieve {} for {}", + file, identifier + )) .into()), }, - None => Err(RuntimeException { - message: format!("Could not retrieve {} for {}", file, identifier), - code: 0, - } + None => Err(RuntimeException::new(format!( + "Could not retrieve {} for {}", + file, identifier + )) .into()), }, - None => Err(RuntimeException { - message: format!("Could not retrieve {} for {}", file, identifier), - code: 0, - } + None => Err(RuntimeException::new(format!( + "Could not retrieve {} for {}", + file, identifier + )) .into()), } } @@ -193,7 +194,7 @@ impl ForgejoDriver { ); let commit = self .get_contents(&resource_url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; let date_str = if let PhpMixed::Array(ref arr) = commit { @@ -208,9 +209,8 @@ impl ForgejoDriver { None }; - let date_str = date_str.ok_or_else(|| RuntimeException { - message: format!("Could not parse commit date for {}", identifier), - code: 0, + let date_str = date_str.ok_or_else(|| { + RuntimeException::new(format!("Could not parse commit date for {}", identifier)) })?; let date: chrono::DateTime<chrono::FixedOffset> = shirabe_php_shim::date_create(&date_str)?; @@ -243,7 +243,7 @@ impl ForgejoDriver { while let Some(url) = resource { let response = self .get_contents(&url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let branch_data = response.decode_json()?; if let PhpMixed::List(ref list) = branch_data { for branch in list { @@ -286,7 +286,7 @@ impl ForgejoDriver { while let Some(url) = resource { let response = self .get_contents(&url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let tags_data = response.decode_json()?; if let PhpMixed::List(ref list) = tags_data { for tag in list { @@ -599,7 +599,7 @@ impl ForgejoDriver { &mut self, url: &str, fetching_repo_data: bool, - ) -> anyhow::Result<Response, TransportException> { + ) -> anyhow::Result<Response, Box<TransportException>> { match self.inner.get_contents(url) { Ok(response) => Ok(response), Err(e) => match e.get_code() { @@ -610,14 +610,7 @@ impl ForgejoDriver { if !self.inner.io.is_interactive() { self.attempt_clone_fallback() - .map_err(|inner_e| TransportException { - message: inner_e.to_string(), - code: 0, - headers: None, - response: None, - status_code: None, - response_info: vec![], - })?; + .map_err(|inner_e| TransportException::new(inner_e.to_string(), 0))?; return Ok(Response::new( "dummy".to_string(), @@ -645,14 +638,7 @@ impl ForgejoDriver { ); let auth_result = forgejo .authorize_o_auth_interactively(&origin_url, message.as_deref()) - .map_err(|inner_e| TransportException { - message: inner_e.to_string(), - code: 0, - headers: None, - response: None, - status_code: None, - response_info: vec![], - })?; + .map_err(|inner_e| TransportException::new(inner_e.to_string(), 0))?; if let Ok(true) = auth_result { return self.inner.get_contents(url); @@ -734,7 +720,7 @@ impl crate::repository::vcs::VcsDriverInterface for ForgejoDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs index 1b61ed4d..3795d1ca 100644 --- a/crates/shirabe/src/repository/vcs/fossil_driver.rs +++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs @@ -12,6 +12,7 @@ use crate::util::ProcessExecutor; use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PhpMixed, RuntimeException, dirname, is_dir, is_file, is_writable, php_regex, }; @@ -77,10 +78,7 @@ impl FossilDriver { .unwrap_or("") .to_string(); if !Cache::is_usable(&cache_repo_dir) || !Cache::is_usable(&cache_vcs_dir) { - return Err(RuntimeException { - message: "FossilDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), - code: 0, - } + return Err(RuntimeException::new("FossilDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string()) .into()); } @@ -105,13 +103,10 @@ impl FossilDriver { None, ) != 0 { - return Err(RuntimeException { - message: format!( - "fossil was not found, check that it is installed and in your PATH env.\n\n{}", - self.inner.process.borrow().get_error_output() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "fossil was not found, check that it is installed and in your PATH env.\n\n{}", + self.inner.process.borrow().get_error_output() + )) .into()); } Ok(()) @@ -124,13 +119,10 @@ impl FossilDriver { fs.ensure_directory_exists(&self.checkout_dir)?; if !is_writable(dirname(&self.checkout_dir)) { - return Err(RuntimeException { - message: format!( - "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", - self.inner.url, self.checkout_dir - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", + self.inner.url, self.checkout_dir + )) .into()); } @@ -173,13 +165,10 @@ impl FossilDriver { ) != 0 { let output = self.inner.process.borrow().get_error_output().to_string(); - return Err(RuntimeException { - message: format!( - "Failed to clone {} to repository {}\n\n{}", - self.inner.url, repo_file, output - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to clone {} to repository {}\n\n{}", + self.inner.url, repo_file, output + )) .into()); } @@ -192,13 +181,10 @@ impl FossilDriver { ) != 0 { let output = self.inner.process.borrow().get_error_output().to_string(); - return Err(RuntimeException { - message: format!( - "Failed to open repository {} in {}\n\n{}", - repo_file, self.checkout_dir, output - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to open repository {} in {}\n\n{}", + repo_file, self.checkout_dir, output + )) .into()); } } @@ -231,13 +217,10 @@ impl FossilDriver { pub fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> { if identifier.starts_with('-') { - return Err(RuntimeException { - message: format!( - "Invalid fossil identifier detected. Identifier must not start with a -, given: {}", - identifier - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid fossil identifier detected. Identifier must not start with a -, given: {}", + identifier + )) .into()); } @@ -420,7 +403,7 @@ impl crate::repository::vcs::VcsDriverInterface for FossilDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs index 8fe93018..d833b576 100644 --- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs @@ -16,6 +16,7 @@ use crate::util::http::Response; use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_key_exists, array_search_mixed, extension_loaded, http_build_query_mixed, implode, is_array, php_regex, @@ -90,13 +91,10 @@ impl GitBitbucketDriver { &self.inner.url, Some(&mut m), ) { - return Err(InvalidArgumentException { - message: format!( - "The Bitbucket repository URL {} is invalid. It must be the HTTPS URL of a Bitbucket repository.", - self.inner.url.clone(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The Bitbucket repository URL {} is invalid. It must be the HTTPS URL of a Bitbucket repository.", + self.inner.url.clone(), + )) .into()); } @@ -706,7 +704,7 @@ impl GitBitbucketDriver { if !self.inner.io.has_authentication(&self.inner.origin_url) && bitbucket_util.authorize_oauth(&self.inner.origin_url) { - return self.inner.get_contents(url).map_err(anyhow::Error::from); + return self.inner.get_contents(url).map_err(|e| (*e).into()); } if !self.inner.io.is_interactive() && fetching_repo_data { @@ -722,7 +720,7 @@ impl GitBitbucketDriver { } } - Err(e.into()) + Err((*e).into()) } } } @@ -742,7 +740,7 @@ impl GitBitbucketDriver { match self.setup_fallback_driver(&self.generate_ssh_url()) { Ok(()) => Ok(true), Err(e) => { - if e.downcast_ref::<RuntimeException>().is_some() { + if e.is_instanceof::<RuntimeException>() { self.fallback_driver = None; self.inner.io.write_error(&format!( @@ -799,11 +797,10 @@ impl GitBitbucketDriver { if self.root_identifier.is_none() { if !self.get_repo_data()? { if self.fallback_driver.is_none() { - return Err(LogicException { - message: "A fallback driver should be setup if getRepoData returns false" + return Err(LogicException::new( + "A fallback driver should be setup if getRepoData returns false" .to_string(), - code: 0, - } + ) .into()); } @@ -811,13 +808,10 @@ impl GitBitbucketDriver { } if self.vcs_type.as_deref() != Some("git") { - return Err(RuntimeException { - message: format!( - "{} does not appear to be a git repository, use {} but remember that Bitbucket no longer supports the mercurial repositories. https://bitbucket.org/blog/sunsetting-mercurial-support-in-bitbucket", - self.inner.url, self.clone_https_url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "{} does not appear to be a git repository, use {} but remember that Bitbucket no longer supports the mercurial repositories. https://bitbucket.org/blog/sunsetting-mercurial-support-in-bitbucket", + self.inner.url, self.clone_https_url + )) .into()); } @@ -918,7 +912,7 @@ impl crate::repository::vcs::VcsDriverInterface for GitBitbucketDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs index 1376ebbc..9f8a6b32 100644 --- a/crates/shirabe/src/repository/vcs/git_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_driver.rs @@ -15,6 +15,7 @@ use chrono::TimeZone; use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, RuntimeException, dirname, is_dir, is_writable, realpath, sys_get_temp_dir, @@ -52,13 +53,10 @@ impl GitDriver { if Filesystem::is_local_path(&self.inner.url) { self.inner.url = Preg::replace(php_regex!(r"{[\\/]\.git/?$}"), "", &self.inner.url); if !is_dir(&self.inner.url) { - return Err(RuntimeException { - message: format!( - "Failed to read package information from {} as the path does not exist", - self.inner.url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to read package information from {} as the path does not exist", + self.inner.url + )) .into()); } self.repo_dir = self.inner.url.clone(); @@ -73,10 +71,7 @@ impl GitDriver { .unwrap_or("") .to_string(); if !Cache::is_usable(&cache_vcs_dir) { - return Err(RuntimeException { - message: "GitDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), - code: 0, - } + return Err(RuntimeException::new("GitDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string()) .into()); } @@ -96,25 +91,19 @@ impl GitDriver { fs.ensure_directory_exists(&dirname(&self.repo_dir))?; if !is_writable(dirname(&self.repo_dir)) { - return Err(RuntimeException { - message: format!( - "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", - self.inner.url, - dirname(&self.repo_dir) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", + self.inner.url, + dirname(&self.repo_dir) + )) .into()); } if Preg::is_match(php_regex!(r"{^ssh://[^@]+@[^:]+:[^0-9]+}"), &self.inner.url) { - return Err(InvalidArgumentException { - message: format!( - "The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.", - self.inner.url - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.", + self.inner.url + )) .into()); } @@ -126,13 +115,10 @@ impl GitDriver { ); if !git_util.sync_mirror(&self.inner.url, &self.repo_dir)? { if !is_dir(&self.repo_dir) { - return Err(RuntimeException { - message: format!( - "Failed to clone {} to read package information from it", - self.inner.url - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to clone {} to read package information from it", + self.inner.url + )) .into()); } self.inner.io.write_error3(&format!( @@ -250,13 +236,10 @@ impl GitDriver { identifier: &str, ) -> anyhow::Result<Option<String>> { if identifier.starts_with('-') { - return Err(RuntimeException { - message: format!( - "Invalid git identifier detected. Identifier must not start with a -, given: {}", - identifier - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid git identifier detected. Identifier must not start with a -, given: {}", + identifier + )) .into()); } @@ -283,13 +266,10 @@ impl GitDriver { identifier: &str, ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { if identifier.starts_with('-') { - return Err(RuntimeException { - message: format!( - "Invalid git identifier detected. Identifier must not start with a -, given: {}", - identifier - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid git identifier detected. Identifier must not start with a -, given: {}", + identifier + )) .into()); } @@ -459,7 +439,7 @@ impl GitDriver { ) { Ok(_) => Ok(true), Err(e) => { - if e.downcast_ref::<RuntimeException>().is_some() { + if e.is_instanceof::<RuntimeException>() { Ok(false) } else { Err(e) @@ -543,7 +523,7 @@ impl crate::repository::vcs::VcsDriverInterface for GitDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs index d656ae61..72bcc8ae 100644 --- a/crates/shirabe/src/repository/vcs/github_driver.rs +++ b/crates/shirabe/src/repository/vcs/github_driver.rs @@ -15,6 +15,7 @@ use crate::util::http::Response; use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, PhpMixed, RuntimeException, array_diff, array_key_exists, array_map, array_search_mixed, base64_decode, basename, empty, explode, extension_loaded, in_array_loose, @@ -77,13 +78,10 @@ impl GitHubDriver { &self.inner.url, Some(&mut match_), ) { - return Err(InvalidArgumentException { - message: format!( - "The GitHub repository URL {} is invalid.", - self.inner.url.clone(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The GitHub repository URL {} is invalid.", + self.inner.url.clone(), + )) .into()); } @@ -737,7 +735,7 @@ impl GitHubDriver { ); let mut resource = self .get_contents(&resource_url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; // The GitHub contents API only returns files up to 1MB as base64 encoded files @@ -765,7 +763,7 @@ impl GitHubDriver { .to_string(); resource = self .get_contents(&git_url, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; } @@ -789,10 +787,10 @@ impl GitHubDriver { let content = match content { Some(c) => String::from_utf8_lossy(&c).to_string(), None => { - return Err(RuntimeException { - message: format!("Could not retrieve {} for {}", file, identifier), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not retrieve {} for {}", + file, identifier + )) .into()); } }; @@ -817,7 +815,7 @@ impl GitHubDriver { ); let commit = self .get_contents(&resource, false) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; let date_str = match commit { @@ -853,7 +851,7 @@ impl GitHubDriver { loop { let response = self .get_contents(resource.as_deref().unwrap_or(""), false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let tags_data = response.decode_json()?; if let PhpMixed::List(ref list) = tags_data { for tag in list { @@ -903,7 +901,7 @@ impl GitHubDriver { loop { let response = self .get_contents(resource.as_deref().unwrap_or(""), false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let branch_data = response.decode_json()?; if let PhpMixed::List(ref list) = branch_data { for branch in list { @@ -1015,7 +1013,7 @@ impl GitHubDriver { &mut self, url: &str, fetching_repo_data: bool, - ) -> anyhow::Result<Response, TransportException> { + ) -> anyhow::Result<Response, Box<TransportException>> { let response_result = self.inner.get_contents(url); match response_result { Ok(r) => Ok(r), @@ -1028,7 +1026,7 @@ impl GitHubDriver { ) .map_err(|err| TransportException::new(err.to_string(), 0))?; - match e.code { + match e.get_code() { 401 | 404 => { // try to authorize only if we are fetching the main /repos/foo/bar data, otherwise it must be a real 404 if !fetching_repo_data { @@ -1178,10 +1176,10 @@ impl GitHubDriver { }; } Err(e) => { - if e.code == 499 { + if e.get_code() == 499 { self.attempt_clone_fallback(Some(&e))?; } else { - return Err(e.into()); + return Err((*e).into()); } } } @@ -1232,13 +1230,11 @@ impl GitHubDriver { e: Option<&TransportException>, ) -> anyhow::Result<bool> { if !self.allow_git_fallback { - return Err(RuntimeException { - message: format!( - "Fallback to git driver disabled{}", - e.map(|e| format!(": {}", e.message)).unwrap_or_default() - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Fallback to git driver disabled{}", + e.map(|e| format!(": {}", e.get_message())) + .unwrap_or_default() + )) .into()); } @@ -1269,11 +1265,9 @@ impl GitHubDriver { pub(crate) fn setup_git_driver(&mut self, url: &str) -> anyhow::Result<()> { if !self.allow_git_fallback { - return Err(RuntimeException { - message: "Fallback to git driver disabled".to_string(), - code: 0, - } - .into()); + return Err( + RuntimeException::new("Fallback to git driver disabled".to_string()).into(), + ); } let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new(); repo_config.insert("url".to_string(), PhpMixed::String(url.to_string())); @@ -1377,7 +1371,7 @@ impl crate::repository::vcs::VcsDriverInterface for GitHubDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs index 5fe513f6..8432ed65 100644 --- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs +++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs @@ -16,6 +16,7 @@ use crate::util::http::Response; use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_search_mixed, array_shift, ctype_alnum, empty, explode, extension_loaded, implode, in_array_loose, is_array, @@ -82,13 +83,10 @@ impl GitLabDriver { pub fn initialize(&mut self) -> anyhow::Result<()> { let mut match_: IndexMap<CaptureKey, String> = IndexMap::new(); if !Preg::is_match3(Self::URL_REGEX, &self.inner.url, Some(&mut match_)) { - return Err(InvalidArgumentException { - message: format!( - "The GitLab repository URL {} is invalid. It must be the HTTP URL of a GitLab project.", - self.inner.url.clone(), - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The GitLab repository URL {} is invalid. It must be the HTTP URL of a GitLab project.", + self.inner.url.clone(), + )) .into()); } @@ -134,13 +132,10 @@ impl GitLabDriver { let origin = match origin { Some(o) => o, None => { - return Err(LogicException { - message: format!( - "It should not be possible to create a gitlab driver with an unparsable origin URL ({})", - self.inner.url - ), - code: 0, - } + return Err(LogicException::new(format!( + "It should not be possible to create a gitlab driver with an unparsable origin URL ({})", + self.inner.url + )) .into()); } }; @@ -153,10 +148,9 @@ impl GitLabDriver { { // https treated as a synonym for http. if !matches!(protocol, "git" | "http" | "https") { - return Err(RuntimeException { - message: "gitlab-protocol must be one of git, http.".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "gitlab-protocol must be one of git, http.".to_string(), + ) .into()); } self.protocol = if protocol == "git" { @@ -424,8 +418,8 @@ impl GitLabDriver { let content = match self.get_contents(&resource, false) { Ok(response) => response.get_body().map(|s| s.to_string()), Err(e) => { - if e.code != 404 { - return Err(e.into()); + if e.get_code() != 404 { + return Err((*e).into()); } return Ok(None); @@ -617,7 +611,7 @@ impl GitLabDriver { loop { let response = self .get_contents(resource.as_deref().unwrap_or(""), false) - .map_err(|e| anyhow::anyhow!("{}", e.message))?; + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))?; let data = response.decode_json()?; if let PhpMixed::List(ref list) = data { @@ -676,7 +670,7 @@ impl GitLabDriver { let resource = self.get_api_url(); let project = self .get_contents(&resource, true) - .map_err(|e| anyhow::anyhow!("{}", e.message))? + .map_err(|e| anyhow::anyhow!("{}", e.get_message()))? .decode_json()?; self.project = match project { PhpMixed::Array(m) => Some(m), @@ -769,7 +763,7 @@ impl GitLabDriver { &mut self, url: &str, fetching_repo_data: bool, - ) -> anyhow::Result<Response, TransportException> { + ) -> anyhow::Result<Response, Box<TransportException>> { let response_result = self.inner.get_contents(url); match response_result { Ok(response) => { @@ -839,21 +833,21 @@ impl GitLabDriver { .and_then(|v| v.as_string()) == Some("disabled") { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "The GitLab repository is disabled in the project".to_string(), 400, - )); + ))); } if !empty(&json_map.get("id").cloned().unwrap_or(PhpMixed::Null)) { self.is_private = false; } - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "GitLab API seems to not be authenticated as it did not return a default_branch" .to_string(), 401, - )); + ))); } } @@ -868,7 +862,7 @@ impl GitLabDriver { ) .map_err(|err| TransportException::new(err.to_string(), 0))?; - match e.code { + match e.get_code() { 401 | 404 => { // try to authorize only if we are fetching the main /repos/foo/bar data, otherwise it must be a real 404 if !fetching_repo_data { @@ -901,7 +895,9 @@ impl GitLabDriver { self.inner.io.write_error3( &format!( "<warning>Failed to download {}/{}:{}</warning>", - self.namespace, self.repository, e.message + self.namespace, + self.repository, + e.get_message() ), true, io_interface::NORMAL, @@ -1142,7 +1138,7 @@ impl crate::repository::vcs::VcsDriverInterface for GitLabDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs index 0283ed38..41aeebd9 100644 --- a/crates/shirabe/src/repository/vcs/hg_driver.rs +++ b/crates/shirabe/src/repository/vcs/hg_driver.rs @@ -12,6 +12,7 @@ use crate::util::Url; use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, RuntimeException, dirname, is_dir, is_writable, php_regex}; #[derive(Debug)] @@ -53,10 +54,7 @@ impl HgDriver { .unwrap_or("") .to_string(); if !Cache::is_usable(&cache_vcs_dir) { - return Err(RuntimeException { - message: "HgDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), - code: 0, - }.into()); + return Err(RuntimeException::new("HgDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string()).into()); } let sanitized = Preg::replace( @@ -70,13 +68,10 @@ impl HgDriver { fs.ensure_directory_exists(&cache_vcs_dir)?; if !is_writable(dirname(&self.repo_dir)) { - return Err(RuntimeException { - message: format!( - "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", - self.inner.url, cache_vcs_dir - ), - code: 0, - }.into()); + return Err(RuntimeException::new(format!( + "Can not clone {} to access package information. The \"{}\" directory is not writable by the current user.", + self.inner.url, cache_vcs_dir + )).into()); } self.inner.config.borrow_mut().prohibit_url_by_config( @@ -167,13 +162,10 @@ impl HgDriver { pub fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> { if identifier.starts_with('-') { - return Err(RuntimeException { - message: format!( - "Invalid hg identifier detected. Identifier must not start with a -, given: {}", - identifier - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid hg identifier detected. Identifier must not start with a -, given: {}", + identifier + )) .into()); } @@ -203,13 +195,10 @@ impl HgDriver { identifier: &str, ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { if identifier.starts_with('-') { - return Err(RuntimeException { - message: format!( - "Invalid hg identifier detected. Identifier must not start with a -, given: {}", - identifier - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Invalid hg identifier detected. Identifier must not start with a -, given: {}", + identifier + )) .into()); } @@ -443,7 +432,7 @@ impl crate::repository::vcs::VcsDriverInterface for HgDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index 452ac5a2..af16cd8c 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -79,10 +79,7 @@ impl PerforceDriver { .unwrap_or("") .to_string(); if !Cache::is_usable(&cache_vcs_dir) { - return Err(RuntimeException { - message: "PerforceDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string(), - code: 0, - }.into()); + return Err(RuntimeException::new("PerforceDriver requires a usable cache directory, and it looks like you set it to be disabled".to_string()).into()); } let repo_dir = format!("{}/{}", cache_vcs_dir, self.depot); @@ -180,11 +177,10 @@ impl PerforceDriver { } pub fn get_contents(&self, _url: &str) -> anyhow::Result<Response> { - Err(BadMethodCallException { - message: "Not implemented/used in PerforceDriver".to_string(), - code: 0, - } - .into()) + Err( + BadMethodCallException::new("Not implemented/used in PerforceDriver".to_string()) + .into(), + ) } pub fn supports( diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs index 716b1943..84e3a20d 100644 --- a/crates/shirabe/src/repository/vcs/svn_driver.rs +++ b/crates/shirabe/src/repository/vcs/svn_driver.rs @@ -14,6 +14,7 @@ use crate::util::Url; use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PhpMixed, RuntimeException, php_regex, stripos, strrpos, strtr, substr, trim, }; @@ -199,14 +200,14 @@ impl SvnDriver { Ok(c) => c, Err(e) => { // PHP catches only TransportException; other exceptions propagate uncaught. - if e.downcast_ref::<TransportException>().is_none() { + if !e.is_instanceof::<TransportException>() { return Err(e); } let message = e - .downcast_ref::<TransportException>() + .catch::<TransportException>() .unwrap() - .message - .clone(); + .get_message() + .to_string(); if stripos(&message, "path not found").is_none() && stripos(&message, "svn: warning: W160013").is_none() { @@ -277,8 +278,8 @@ impl SvnDriver { ) { Ok(o) => o, Err(e) => { - if let Some(e) = e.downcast_ref::<RuntimeException>() { - return Err(TransportException::new(e.message.clone(), 0).into()); + if let Some(e) = e.catch::<RuntimeException>() { + return Err(TransportException::new(e.get_message().to_string(), 0).into()); } return Err(e); } @@ -567,24 +568,18 @@ impl SvnDriver { Ok(o) => Ok(o), Err(e) => { if self.util.as_mut().unwrap().binary_version().is_none() { - return Err(RuntimeException { - message: format!( - "Failed to load {}, svn was not found, check that it is installed and in your PATH env.\n\n{}", - self.inner.url, - self.inner.process.borrow().get_error_output(), - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Failed to load {}, svn was not found, check that it is installed and in your PATH env.\n\n{}", + self.inner.url, + self.inner.process.borrow().get_error_output(), + )) .into()); } - Err(RuntimeException { - message: format!( - "Repository {} could not be processed, {}", - self.inner.url, e, - ), - code: 0, - } + Err(RuntimeException::new(format!( + "Repository {} could not be processed, {}", + self.inner.url, e, + )) .into()) } } @@ -655,7 +650,7 @@ impl crate::repository::vcs::VcsDriverInterface for SvnDriver { match self.get_composer_information(identifier) { Ok(info) => Ok(info.is_some()), Err(e) => { - if e.downcast_ref::<TransportException>().is_some() { + if e.is_instanceof::<TransportException>() { Ok(false) } else { Err(e) diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs index 3321201c..42f679df 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs @@ -13,6 +13,7 @@ use crate::util::http::Response; use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{DATE_RFC3339, PhpMixed, extension_loaded, php_regex}; #[derive(Debug)] @@ -66,7 +67,7 @@ impl VcsDriverBase { "http" } - pub fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> { + pub fn get_contents(&self, url: &str) -> anyhow::Result<Response, Box<TransportException>> { let options_mixed = self .repo_config .get("options") @@ -79,9 +80,11 @@ impl VcsDriverBase { self.http_downloader .borrow_mut() .get(url, options) - .map_err(|e| match e.downcast::<TransportException>() { - Ok(te) => te, - Err(other) => TransportException::new(other.to_string(), 0), + .map_err(|e| { + Box::new(match e.catch::<TransportException>() { + Some(te) => te.clone(), + None => TransportException::new(e.to_string(), 0), + }) }) } @@ -299,7 +302,7 @@ pub trait VcsDriver: VcsDriverInterface { "http" } - fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> { + fn get_contents(&self, url: &str) -> anyhow::Result<Response, Box<TransportException>> { let options_mixed = self .repo_config() .get("options") @@ -312,9 +315,11 @@ pub trait VcsDriver: VcsDriverInterface { self.http_downloader() .borrow_mut() .get(url, options) - .map_err(|e| match e.downcast::<TransportException>() { - Ok(te) => te, - Err(other) => TransportException::new(other.to_string(), 0), + .map_err(|e| { + Box::new(match e.catch::<TransportException>() { + Some(te) => te.clone(), + None => TransportException::new(e.to_string(), 0), + }) }) } diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs index 521d7aea..2b811c78 100644 --- a/crates/shirabe/src/repository/vcs_repository.rs +++ b/crates/shirabe/src/repository/vcs_repository.rs @@ -28,6 +28,7 @@ use crate::util::ProcessExecutor; use crate::util::Url; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, PhpClass, PhpMixed, php_regex, str_replace, strpos, }; @@ -305,10 +306,10 @@ impl VcsRepository { let driver_url = self.url.clone(); self.ensure_driver(); if self.driver.borrow().is_none() { - return Err(InvalidArgumentException { - message: format!("No driver found to handle VCS repository {}", driver_url), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "No driver found to handle VCS repository {}", + driver_url + )) .into()); } *self.version_parser.borrow_mut() = Some(VersionParser::new()); @@ -351,7 +352,7 @@ impl VcsRepository { } Ok(None) => {} Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && self.should_rethrow_transport_exception(te) { return Err(e); @@ -367,7 +368,7 @@ impl VcsRepository { } } Err(e) => { - if let Some(te) = e.downcast_ref::<TransportException>() + if let Some(te) = e.catch::<TransportException>() && self.should_rethrow_transport_exception(te) { return Err(e); @@ -575,7 +576,7 @@ impl VcsRepository { Ok(()) })(); if let Err(e) = result { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { self.version_transport_exceptions .borrow_mut() .entry("tags".to_string()) @@ -589,7 +590,7 @@ impl VcsRepository { } } if is_very_verbose { - let detail = if let Some(te) = e.downcast_ref::<TransportException>() { + let detail = if let Some(te) = e.catch::<TransportException>() { format!( "no composer file was found ({} HTTP status code)", te.get_code() @@ -786,7 +787,7 @@ impl VcsRepository { Ok(()) })(); if let Err(e) = result { - if let Some(te) = e.downcast_ref::<TransportException>() { + if let Some(te) = e.catch::<TransportException>() { self.version_transport_exceptions .borrow_mut() .entry("branches".to_string()) diff --git a/crates/shirabe/src/self_update/versions.rs b/crates/shirabe/src/self_update/versions.rs index 14ad10d8..d3611ede 100644 --- a/crates/shirabe/src/self_update/versions.rs +++ b/crates/shirabe/src/self_update/versions.rs @@ -73,14 +73,11 @@ impl Versions { io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, ) -> anyhow::Result<Result<(), InvalidArgumentException>> { if !Self::CHANNELS.contains(&channel.as_str()) { - return Ok(Err(InvalidArgumentException { - message: format!( - "Invalid channel {}, must be one of: {}", - channel, - Self::CHANNELS.join(", ") - ), - code: 0, - })); + return Ok(Err(InvalidArgumentException::new(format!( + "Invalid channel {}, must be one of: {}", + channel, + Self::CHANNELS.join(", ") + )))); } let channel_file = format!( @@ -146,13 +143,10 @@ impl Versions { } } - Ok(Err(UnexpectedValueException { - message: format!( - "There is no version of Composer available for your PHP version ({})", - PHP_VERSION - ), - code: 0, - })) + Ok(Err(UnexpectedValueException::new(format!( + "There is no version of Composer available for your PHP version ({})", + PHP_VERSION + )))) } fn get_versions_data(&mut self) -> anyhow::Result<PhpMixed> { diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs index a323c37e..69a84857 100644 --- a/crates/shirabe/src/util/auth_helper.rs +++ b/crates/shirabe/src/util/auth_helper.rs @@ -80,11 +80,7 @@ impl AuthHelper { ) { return Ok(PhpMixed::String(input)); } - Err(RuntimeException { - message: "Please answer (y)es or (n)o".to_string(), - code: 0, - } - .into()) + Err(RuntimeException::new("Please answer (y)es or (n)o".to_string()).into()) }), None, PhpMixed::String("y".to_string()), diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs index 50d4b700..d8e245a9 100644 --- a/crates/shirabe/src/util/bitbucket.rs +++ b/crates/shirabe/src/util/bitbucket.rs @@ -9,10 +9,11 @@ use crate::io::io_interface; use crate::util::HttpDownloader; use crate::util::ProcessExecutor; use indexmap::IndexMap; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{LogicException, PhpMixed, time}; fn transport_error_code(err: &anyhow::Error) -> Option<i64> { - err.downcast_ref::<TransportException>().map(|te| te.code) + err.catch::<TransportException>().map(|te| te.get_code()) } #[derive(Debug)] @@ -167,24 +168,18 @@ impl Bitbucket { let token_map = match token { PhpMixed::Array(ref m) => m.clone(), _ => { - return Err(LogicException { - message: format!( - "Expected a token configured with expires_in and access_token present, got {}", - shirabe_php_shim::json_encode(&token).unwrap_or_default() - ), - code: 0, - } + return Err(LogicException::new(format!( + "Expected a token configured with expires_in and access_token present, got {}", + shirabe_php_shim::json_encode(&token).unwrap_or_default() + )) .into()); } }; if !token_map.contains_key("expires_in") || !token_map.contains_key("access_token") { - return Err(LogicException { - message: format!( - "Expected a token configured with expires_in and access_token present, got {}", - shirabe_php_shim::json_encode(&token).unwrap_or_default() - ), - code: 0, - } + return Err(LogicException::new(format!( + "Expected a token configured with expires_in and access_token present, got {}", + shirabe_php_shim::json_encode(&token).unwrap_or_default() + )) .into()); } self.token = Some(token_map.into_iter().collect()); @@ -350,11 +345,7 @@ impl Bitbucket { match access_token { Some(t) => Ok(t), - None => Err(LogicException { - message: "Failed to initialize token above".to_string(), - code: 0, - } - .into()), + None => Err(LogicException::new("Failed to initialize token above".to_string()).into()), } } @@ -370,9 +361,10 @@ impl Bitbucket { .get_config_source_mut() .remove_config_setting(&format!("bitbucket-oauth.{}", origin_url))?; - let token = self.token.as_ref().ok_or_else(|| LogicException { - message: "Expected a token configured with expires_in present, got null".to_string(), - code: 0, + let token = self.token.as_ref().ok_or_else(|| { + LogicException::new( + "Expected a token configured with expires_in present, got null".to_string(), + ) })?; let expires_in = token .get("expires_in") @@ -380,13 +372,10 @@ impl Bitbucket { .ok_or_else(|| { let token_mixed = PhpMixed::Array(token.iter().map(|(k, v)| (k.clone(), v.clone())).collect()); - LogicException { - message: format!( - "Expected a token configured with expires_in present, got {}", - shirabe_php_shim::json_encode(&token_mixed).unwrap_or_default() - ), - code: 0, - } + LogicException::new(format!( + "Expected a token configured with expires_in present, got {}", + shirabe_php_shim::json_encode(&token_mixed).unwrap_or_default() + )) })?; let t = self.time.unwrap_or_else(time); diff --git a/crates/shirabe/src/util/config_validator.rs b/crates/shirabe/src/util/config_validator.rs index 9b6b292d..978a5cd2 100644 --- a/crates/shirabe/src/util/config_validator.rs +++ b/crates/shirabe/src/util/config_validator.rs @@ -10,6 +10,7 @@ use crate::package::loader::ValidatingArrayLoader; use indexmap::IndexMap; use serde::de::Error as _; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, php_regex}; use shirabe_spdx_licenses::SpdxLicenses; @@ -55,7 +56,7 @@ impl ConfigValidator { match schema_result { Ok(()) => {} Err(e) => { - if let Some(validation_e) = e.downcast_ref::<JsonValidationException>() { + if let Some(validation_e) = e.catch::<JsonValidationException>() { for message in validation_e.get_errors() { if lax_valid { publish_errors.push(message.clone()); @@ -298,7 +299,7 @@ impl ConfigValidator { ) { Ok(_) => {} Err(e) => { - if let Some(invalid_e) = e.downcast_ref::<InvalidPackageException>() { + if let Some(invalid_e) = e.catch::<InvalidPackageException>() { errors.extend_from_slice(invalid_e.get_errors()); } } diff --git a/crates/shirabe/src/util/error_handler.rs b/crates/shirabe/src/util/error_handler.rs index 2e574fe0..dcea940e 100644 --- a/crates/shirabe/src/util/error_handler.rs +++ b/crates/shirabe/src/util/error_handler.rs @@ -58,13 +58,7 @@ impl ErrorHandler { return Ok(true); } - return Err(ErrorException { - message, - code: 0, - severity: level, - filename: file, - lineno: line, - }); + return Err(ErrorException::new(message, 0, level, file, line, None)); } let io = IO.with(|cell| cell.borrow().clone()); diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs index daf26e22..7010fd86 100644 --- a/crates/shirabe/src/util/filesystem.rs +++ b/crates/shirabe/src/util/filesystem.rs @@ -124,9 +124,11 @@ impl Filesystem { // `rm -rf`/`rmdir` subprocess via the String-based ProcessExecutor, so it has to be // representable as UTF-8. let directory = directory.as_ref(); - let directory = directory.to_str().ok_or_else(|| RuntimeException { - message: format!("Path contains invalid UTF-8: {}", directory.display()), - code: 0, + let directory = directory.to_str().ok_or_else(|| { + RuntimeException::new(format!( + "Path contains invalid UTF-8: {}", + directory.display() + )) })?; let edge_case_result = self.remove_edge_cases(directory, true)?; if let Some(r) = edge_case_result { @@ -246,10 +248,7 @@ impl Filesystem { } if Preg::is_match3(php_regex!("{^(?:[a-z]:)?[/\\\\]+$}i"), directory, None) { - return Err(RuntimeException { - message: format!("Aborting an attempted deletion of {}, this was probably not intended, if it is a real use case please report it.", directory), - code: 0, - } + return Err(RuntimeException::new(format!("Aborting an attempted deletion of {}, this was probably not intended, if it is a real use case please report it.", directory)) .into()); } @@ -309,42 +308,36 @@ impl Filesystem { pub fn ensure_directory_exists(&mut self, directory: &str) -> anyhow::Result<()> { if !is_dir(directory) { if file_exists(directory) { - return Err(RuntimeException { - message: format!("{} exists and is not a directory.", directory), - code: 0, - } + return Err(RuntimeException::new(format!( + "{} exists and is not a directory.", + directory + )) .into()); } if is_link(directory) && !self.unlink_implementation(Path::new(directory)) { - return Err(RuntimeException { - message: format!( - "Could not delete symbolic link {}: {}", - directory, - error_get_last() - .as_ref() - .and_then(|m| m.get("message")) - .and_then(|v| v.as_string()) - .unwrap_or("") - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not delete symbolic link {}: {}", + directory, + error_get_last() + .as_ref() + .and_then(|m| m.get("message")) + .and_then(|v| v.as_string()) + .unwrap_or("") + )) .into()); } if !mkdir(directory, 0o777, true) { - let e = RuntimeException { - message: format!( - "{} does not exist and could not be created: {}", - directory, - error_get_last() - .as_ref() - .and_then(|m| m.get("message")) - .and_then(|v| v.as_string()) - .unwrap_or("") - ), - code: 0, - }; + let e = RuntimeException::new(format!( + "{} does not exist and could not be created: {}", + directory, + error_get_last() + .as_ref() + .and_then(|m| m.get("message")) + .and_then(|v| v.as_string()) + .unwrap_or("") + )); // in pathological cases with paths like path/to/broken-symlink/../foo is_dir will fail to detect path/to/foo // but normalizing the ../ away first makes it work so we attempt this just in case, and if it still fails we @@ -390,7 +383,7 @@ impl Filesystem { message.push_str("\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"); } - return Err(RuntimeException { message, code: 0 }.into()); + return Err(RuntimeException::new(message).into()); } } @@ -423,7 +416,7 @@ impl Filesystem { message.push_str("\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed"); } - return Err(RuntimeException { message, code: 0 }.into()); + return Err(RuntimeException::new(message).into()); } } @@ -464,7 +457,7 @@ impl Filesystem { // if copy fails we attempt to copy it manually as this can help bypass issues with VirtualBox shared folders // see https://github.com/composer/composer/issues/12057 - if str_contains(&e.message, "Bad address") { + if str_contains(e.get_message(), "Bad address") { let (source_handle, target_handle) = match (fopen(source, "r"), fopen(&target, "w")) { (Ok(source_handle), Ok(target_handle)) => { @@ -520,13 +513,11 @@ impl Filesystem { // TODO(phase-c): // The fallbacks below (copy_then_remove and the mv/xcopy subprocesses) operate on // path strings, so beyond this point the paths have to be representable as UTF-8. - let source = source.to_str().ok_or_else(|| RuntimeException { - message: format!("Path contains invalid UTF-8: {}", source.display()), - code: 0, + let source = source.to_str().ok_or_else(|| { + RuntimeException::new(format!("Path contains invalid UTF-8: {}", source.display())) })?; - let target = target.to_str().ok_or_else(|| RuntimeException { - message: format!("Path contains invalid UTF-8: {}", target.display()), - code: 0, + let target = target.to_str().ok_or_else(|| { + RuntimeException::new(format!("Path contains invalid UTF-8: {}", target.display())) })?; if !function_exists("proc_open") { @@ -735,11 +726,9 @@ impl Filesystem { pub fn size(&self, path: impl AsRef<Path>) -> anyhow::Result<i64> { let path = path.as_ref(); if !file_exists(path) { - return Err(RuntimeException { - message: format!("{} does not exist.", path.display()), - code: 0, - } - .into()); + return Err( + RuntimeException::new(format!("{} does not exist.", path.display())).into(), + ); } if is_dir(path) { return self.directory_size(path); @@ -987,13 +976,10 @@ impl Filesystem { /// Creates an NTFS junction. pub fn junction(&mut self, target: &str, junction: &str) -> anyhow::Result<()> { if !Platform::is_windows() { - return Err(LogicException { - message: format!( - "Function {} is not available on non-Windows platform", - "Composer\\Util\\Filesystem" - ), - code: 0, - } + return Err(LogicException::new(format!( + "Function {} is not available on non-Windows platform", + "Composer\\Util\\Filesystem" + )) .into()); } if !is_dir(target) { diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs index 34d44b7b..6c18e4f6 100644 --- a/crates/shirabe/src/util/forgejo.rs +++ b/crates/shirabe/src/util/forgejo.rs @@ -6,6 +6,7 @@ use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; use crate::io::io_interface; use crate::util::HttpDownloader; +use shirabe_php_shim::Catch as _; #[derive(Debug)] pub struct Forgejo { @@ -121,8 +122,8 @@ impl Forgejo { Ok(_) => {} Err(e) => { let code = e - .downcast_ref::<crate::downloader::TransportException>() - .map(|te| te.code) + .catch::<crate::downloader::TransportException>() + .map(|te| te.get_code()) .unwrap_or(0); if [403, 401, 404].contains(&code) { self.io.write_error3( diff --git a/crates/shirabe/src/util/forgejo_url.rs b/crates/shirabe/src/util/forgejo_url.rs index ce5a8948..639426e1 100644 --- a/crates/shirabe/src/util/forgejo_url.rs +++ b/crates/shirabe/src/util/forgejo_url.rs @@ -27,10 +27,10 @@ impl ForgejoUrl { pub fn create(repo_url: &str) -> anyhow::Result<Self> { match Self::try_from(Some(repo_url)) { Some(url) => Ok(url), - None => Err(InvalidArgumentException { - message: format!("This is not a valid Forgejo URL: {}", repo_url), - code: 0, - } + None => Err(InvalidArgumentException::new(format!( + "This is not a valid Forgejo URL: {}", + repo_url + )) .into()), } } diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs index a6f8d79e..3eec0947 100644 --- a/crates/shirabe/src/util/git.rs +++ b/crates/shirabe/src/util/git.rs @@ -64,11 +64,7 @@ impl Git { ); match io { None => { - return Err(RuntimeException { - message: msg, - code: 0, - } - .into()); + return Err(RuntimeException::new(msg).into()); } Some(io) => { io.write_error3( @@ -215,13 +211,10 @@ impl Git { }; if Preg::is_match(php_regex!(r"{^ssh://[^@]+@[^:]+:[^0-9]+}"), url) { - return Err(InvalidArgumentException { - message: format!( - "The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.", - url - ), - code: 0, - } + return Err(InvalidArgumentException::new(format!( + "The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.", + url + )) .into()); } @@ -1277,22 +1270,15 @@ impl Git { Option::<&str>::None, ) != 0 { - return Err(RuntimeException { - message: Url::sanitize(format!( - "Failed to clone {}, git was not found, check that it is installed and in your PATH env.\n\n{}", - url, - self.process.borrow().get_error_output() - )), - code: 0, - } + return Err(RuntimeException::new(Url::sanitize(format!( + "Failed to clone {}, git was not found, check that it is installed and in your PATH env.\n\n{}", + url, + self.process.borrow().get_error_output() + ))) .into()); } - Err(RuntimeException { - message: Url::sanitize(message.to_string()), - code: 0, - } - .into()) + Err(RuntimeException::new(Url::sanitize(message.to_string())).into()) } /// Retrieves the current git version. diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs index 01a5dd1f..066f0ff5 100644 --- a/crates/shirabe/src/util/github.rs +++ b/crates/shirabe/src/util/github.rs @@ -9,6 +9,7 @@ use crate::util::HttpDownloader; use crate::util::ProcessExecutor; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, date, in_array_loose, php_regex, stripos, strtolower}; #[derive(Debug)] @@ -233,7 +234,7 @@ impl GitHub { Ok(_) => {} Err(te) => { let code = te - .downcast_ref::<crate::downloader::TransportException>() + .catch::<crate::downloader::TransportException>() .map(|t| t.get_code()) .unwrap_or(0); if code == 403 || code == 401 { diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs index c14ad46a..cdeb0f21 100644 --- a/crates/shirabe/src/util/gitlab.rs +++ b/crates/shirabe/src/util/gitlab.rs @@ -10,6 +10,7 @@ use crate::util::HttpDownloader; use crate::util::ProcessExecutor; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PhpMixed, RuntimeException, http_build_query, in_array_strict, json_decode, php_regex, time, }; @@ -248,9 +249,9 @@ impl GitLab { Err(e) => { // 401 is bad credentials, // 403 is max login attempts exceeded - match e.downcast::<TransportException>() { - Ok(te) if te.code == 403 || te.code == 401 => { - if te.code == 401 { + match e.catch::<TransportException>() { + Some(te) if te.get_code() == 403 || te.get_code() == 401 => { + if te.get_code() == 401 { let response = te.get_response().and_then(|r| json_decode(r, true).ok()); let is_invalid_grant = response @@ -301,8 +302,7 @@ impl GitLab { continue; } - Ok(te) => return Err(te.into()), - Err(e) => return Err(e), + _ => return Err(e), } } }; @@ -360,10 +360,9 @@ impl GitLab { return Ok(true); } - Err(RuntimeException { - message: "Invalid GitLab credentials 5 times in a row, aborting.".to_string(), - code: 0, - } + Err(RuntimeException::new( + "Invalid GitLab credentials 5 times in a row, aborting.".to_string(), + ) .into()) } @@ -374,16 +373,17 @@ impl GitLab { ) -> anyhow::Result<bool> { let response = match self.refresh_token(scheme, origin_url) { Ok(r) => r, - Err(e) => match e.downcast::<TransportException>() { - Ok(te) => { + Err(e) => match e.catch::<TransportException>() { + Some(te) => { + let message = te.get_message().to_string(); self.io.write_error3( - &format!("Couldn't refresh access token: {}", te.message), + &format!("Couldn't refresh access token: {}", message), true, io_interface::NORMAL, ); return Ok(false); } - Err(e) => return Err(e), + None => return Err(e), }, }; @@ -488,10 +488,10 @@ impl GitLab { let refresh_token = match refresh_token { Some(t) => t, None => { - return Err(RuntimeException { - message: format!("No GitLab refresh token present for {}.", origin_url), - code: 0, - } + return Err(RuntimeException::new(format!( + "No GitLab refresh token present for {}.", + origin_url + )) .into()); } }; diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs index 604dae77..6409c9d5 100644 --- a/crates/shirabe/src/util/http/curl_downloader.rs +++ b/crates/shirabe/src/util/http/curl_downloader.rs @@ -60,7 +60,7 @@ static TIMEOUT_WARNING: AtomicBool = AtomicBool::new(false); enum Decision { Retry { url: String, delay_ms: Option<u64> }, Done(Response), - Failed(TransportException), + Failed(anyhow::Error), } impl CurlDownloader { @@ -108,7 +108,7 @@ impl CurlDownloader { url: &str, mut options: IndexMap<String, PhpMixed>, copy_to: Option<&str>, - ) -> anyhow::Result<Result<Response, TransportException>> { + ) -> anyhow::Result<Result<Response, anyhow::Error>> { let mut attributes: IndexMap<String, PhpMixed> = { let mut m = IndexMap::new(); m.insert("retryAuthFailure".to_string(), PhpMixed::Bool(true)); @@ -178,7 +178,7 @@ impl CurlDownloader { .as_ref() .map(|pm| pm.get_proxy_for_request(url)) .transpose() - .map_err(|e| anyhow::anyhow!(e.message))? + .map_err(|e| anyhow::anyhow!(e.get_message().to_string()))? .and_then(|p| p.get_status(Some(" using proxy (%s)")).ok()) .unwrap_or_default(); // `attributes.redirects == 0 && attributes.retries == 0` in PHP is always true here since @@ -206,7 +206,7 @@ impl CurlDownloader { )?; let send_options = crate::util::StreamContextFactory::init_options(¤t_url, send_options, true) - .map_err(|e| anyhow::anyhow!(e.message))?; + .map_err(|e| anyhow::anyhow!(e.get_message().to_string()))?; let send_result = self .send_once(¤t_url, &send_options, copy_to, &attributes) @@ -304,26 +304,24 @@ impl CurlDownloader { if let Some(filename) = filename { unlink_silent(format!("{}~", filename)); } - // PHP throws a MaxFileSizeExceededException (a TransportException subclass) with - // the raw "Maximum allowed download size reached..." message verbatim rather than - // wrapping it in the generic curl-error text. + // The message carries the raw "Maximum allowed download size reached..." text + // rather than the generic curl-error wrapper used below. if transport_err.is_max_file_size { return Ok(Decision::Failed( - MaxFileSizeExceededException(TransportException::new( - transport_err.message, - 0, - )) - .0, + MaxFileSizeExceededException::new(transport_err.message).into(), )); } - return Ok(Decision::Failed(TransportException::new( - format!( - "curl error while downloading {}: {}", - Url::sanitize(url.to_string()), - transport_err.message - ), - 0, - ))); + return Ok(Decision::Failed( + TransportException::new( + format!( + "curl error while downloading {}: {}", + Url::sanitize(url.to_string()), + transport_err.message + ), + 0, + ) + .into(), + )); } }; @@ -373,7 +371,7 @@ impl CurlDownloader { }); } Ok(_) => {} - Err(e) => return Ok(Decision::Failed(e)), + Err(e) => return Ok(Decision::Failed((*e).into())), } // Handle 3xx redirects, 304 Not Modified excluded. @@ -401,7 +399,7 @@ impl CurlDownloader { if let Some(filename) = filename { unlink_silent(format!("{}~", filename)); } - return Ok(Decision::Failed(e)); + return Ok(Decision::Failed((*e).into())); } } } @@ -443,7 +441,7 @@ impl CurlDownloader { e.set_headers(curl_response.inner.get_headers().clone()); e.set_status_code(Some(curl_response.inner.get_status_code())); e.set_response(curl_response.inner.get_body().map(|s| s.to_string())); - return Ok(Decision::Failed(e)); + return Ok(Decision::Failed((*e).into())); } // storeAuth on success. @@ -625,7 +623,7 @@ impl CurlDownloader { url: &str, attributes: &IndexMap<String, PhpMixed>, response: &CurlResponse, - ) -> anyhow::Result<Result<String, TransportException>> { + ) -> anyhow::Result<Result<String, Box<TransportException>>> { let mut target_url = String::new(); if let Some(location_header) = response.inner.get_header("location") && !location_header.is_empty() @@ -682,14 +680,14 @@ impl CurlDownloader { return Ok(Ok(target_url)); } - Ok(Err(TransportException::new( + Ok(Err(Box::new(TransportException::new( format!( "The \"{}\" file could not be downloaded, got redirect without Location ({})", url, response.inner.get_status_message().unwrap_or_default() ), 0, - ))) + )))) } fn is_authenticated_retry_needed( @@ -699,7 +697,7 @@ impl CurlDownloader { filename: Option<&str>, attributes: &IndexMap<String, PhpMixed>, response: &CurlResponse, - ) -> anyhow::Result<Result<PromptAuthResult, TransportException>> { + ) -> anyhow::Result<Result<PromptAuthResult, Box<TransportException>>> { let retry_auth_failure = attributes .get("retryAuthFailure") .and_then(|b| b.as_bool()) @@ -808,7 +806,7 @@ impl CurlDownloader { filename: Option<&str>, response: &CurlResponse, error_message: &str, - ) -> TransportException { + ) -> Box<TransportException> { if let Some(filename) = filename { unlink_silent(format!("{}~", filename)); } @@ -836,13 +834,13 @@ impl CurlDownloader { ); } - TransportException::new( + Box::new(TransportException::new( format!( "The \"{}\" file could not be downloaded ({}){}", url, error_message, details ), response.inner.get_status_code(), - ) + )) } fn method_is_get(options: &IndexMap<String, PhpMixed>) -> bool { diff --git a/crates/shirabe/src/util/http/proxy_item.rs b/crates/shirabe/src/util/http/proxy_item.rs index 1a0b3ee8..73948f88 100644 --- a/crates/shirabe/src/util/http/proxy_item.rs +++ b/crates/shirabe/src/util/http/proxy_item.rs @@ -20,28 +20,22 @@ impl ProxyItem { let syntax_error = format!("unsupported `{}` syntax", env_name); if strpbrk(&proxy_url, "\r\n\t").is_some() { - return Err(RuntimeException { - message: syntax_error, - code: 0, - }); + return Err(RuntimeException::new(syntax_error)); } let proxy_parsed = parse_url_all(&proxy_url); let proxy = match proxy_parsed.as_array() { None => { - return Err(RuntimeException { - message: syntax_error, - code: 0, - }); + return Err(RuntimeException::new(syntax_error)); } Some(a) => a.clone(), }; if !proxy.contains_key("host") { - return Err(RuntimeException { - message: format!("unable to find proxy host in {}", env_name), - code: 0, - }); + return Err(RuntimeException::new(format!( + "unable to find proxy host in {}", + env_name + ))); } let scheme = if proxy.contains_key("scheme") { @@ -100,16 +94,16 @@ impl ProxyItem { // but is considered valid depending on the PHP or Curl version. let port = match port { None => { - return Err(RuntimeException { - message: format!("unable to find proxy port in {}", env_name), - code: 0, - }); + return Err(RuntimeException::new(format!( + "unable to find proxy port in {}", + env_name + ))); } Some(0) => { - return Err(RuntimeException { - message: format!("port 0 is reserved in {}", env_name), - code: 0, - }); + return Err(RuntimeException::new(format!( + "port 0 is reserved in {}", + env_name + ))); } Some(p) => p, }; diff --git a/crates/shirabe/src/util/http/proxy_manager.rs b/crates/shirabe/src/util/http/proxy_manager.rs index 82e8ebc5..13f0b521 100644 --- a/crates/shirabe/src/util/http/proxy_manager.rs +++ b/crates/shirabe/src/util/http/proxy_manager.rs @@ -71,12 +71,12 @@ impl ProxyManager { pub fn get_proxy_for_request( &self, request_url: &str, - ) -> Result<RequestProxy, TransportException> { + ) -> Result<RequestProxy, Box<TransportException>> { if let Some(ref error) = self.error { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( format!("Unable to use a proxy: {}", error), 0, - )); + ))); } let scheme = request_url.split("://").next().unwrap_or("").to_string(); diff --git a/crates/shirabe/src/util/http/request_proxy.rs b/crates/shirabe/src/util/http/request_proxy.rs index 1262622f..78a64078 100644 --- a/crates/shirabe/src/util/http/request_proxy.rs +++ b/crates/shirabe/src/util/http/request_proxy.rs @@ -48,7 +48,7 @@ impl RequestProxy { pub fn get_curl_options( &self, ssl_options: &IndexMap<String, PhpMixed>, - ) -> Result<IndexMap<i64, PhpMixed>, TransportException> { + ) -> Result<IndexMap<i64, PhpMixed>, Box<TransportException>> { // PHP guards an HTTPS proxy behind `is_secure() && !supports_secure_proxy()` because // libcurl < 7.52.0 cannot speak TLS to a proxy. Shirabe always can (see // supports_secure_proxy), so the guard is dropped. @@ -90,10 +90,9 @@ impl RequestProxy { return Ok(format.replace("%s", self.status.as_deref().unwrap())); } - Err(InvalidArgumentException { - message: "String format specifier is missing".to_string(), - code: 0, - }) + Err(InvalidArgumentException::new( + "String format specifier is missing".to_string(), + )) } pub fn is_excluded_by_no_proxy(&self) -> bool { diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs index b60664da..e53ad82f 100644 --- a/crates/shirabe/src/util/http_downloader.rs +++ b/crates/shirabe/src/util/http_downloader.rs @@ -17,6 +17,7 @@ use crate::util::http::Response; use crate::util::sync_executor; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, LogicException, PhpMixed, array_replace_recursive, extension_loaded, file_get_contents, function_exists, implode, is_numeric, php_regex, rawurldecode, @@ -205,19 +206,14 @@ impl HttpDownloader { return self.mock_get(url, &options); } if url.is_empty() { - return Err(InvalidArgumentException { - message: "$url must not be an empty string".to_string(), - code: 0, - } + return Err(InvalidArgumentException::new( + "$url must not be an empty string".to_string(), + ) .into()); } if !sync && !self.allow_async { - return Err(LogicException { - message: - "You must use the HttpDownloader instance which is part of a Composer\\Loop instance to be able to run async http requests" - .to_string(), - code: 0, - } + return Err(LogicException::new("You must use the HttpDownloader instance which is part of a Composer\\Loop instance to be able to run async http requests" + .to_string()) .into()); } @@ -321,7 +317,7 @@ impl HttpDownloader { let curl = self.curl.as_ref().unwrap(); return match curl.download(&origin, url, options, copy_to).await { Ok(Ok(response)) => Ok(response), - Ok(Err(transport_exception)) => Err(transport_exception.into()), + Ok(Err(e)) => Err(e), Err(e) => Err(e), }; } @@ -463,7 +459,7 @@ impl HttpDownloader { /// @internal pub fn get_exception_hints(e: &anyhow::Error) -> Option<Vec<String>> { - let e_as_transport: Option<&TransportException> = e.downcast_ref::<TransportException>(); + let e_as_transport: Option<&TransportException> = e.catch::<TransportException>(); e_as_transport?; let e_as_transport = e_as_transport.unwrap(); @@ -600,11 +596,7 @@ impl HttpDownloader { options: &IndexMap<String, PhpMixed>, ) -> anyhow::Result<Response> { if file_url.is_empty() { - return Err(LogicException { - message: "url cannot be an empty string".to_string(), - code: 0, - } - .into()); + return Err(LogicException::new("url cannot be an empty string".to_string()).into()); } let mock = self diff --git a/crates/shirabe/src/util/no_proxy_pattern.rs b/crates/shirabe/src/util/no_proxy_pattern.rs index 582fdb68..83adb09f 100644 --- a/crates/shirabe/src/util/no_proxy_pattern.rs +++ b/crates/shirabe/src/util/no_proxy_pattern.rs @@ -153,27 +153,24 @@ impl NoProxyPattern { let mask = network.netmask.as_deref().unwrap_or_default(); let ip = target.ip.as_slice(); if net.is_empty() { - return Err(RuntimeException { - message: format!( - "Could not parse network IP {}", - String::from_utf8_lossy(net) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse network IP {}", + String::from_utf8_lossy(net) + )) .into()); } if mask.is_empty() { - return Err(RuntimeException { - message: format!("Could not parse netmask {}", String::from_utf8_lossy(mask)), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse netmask {}", + String::from_utf8_lossy(mask) + )) .into()); } if ip.is_empty() { - return Err(RuntimeException { - message: format!("Could not parse target IP {}", String::from_utf8_lossy(ip)), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse target IP {}", + String::from_utf8_lossy(ip) + )) .into()); } @@ -327,23 +324,17 @@ impl NoProxyPattern { // Get the network from the address and mask if netmask.is_empty() { - return Err(RuntimeException { - message: format!( - "Could not parse netmask {}", - String::from_utf8_lossy(&netmask) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse netmask {}", + String::from_utf8_lossy(&netmask) + )) .into()); } if range_ip.is_empty() { - return Err(RuntimeException { - message: format!( - "Could not parse range IP {}", - String::from_utf8_lossy(range_ip) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Could not parse range IP {}", + String::from_utf8_lossy(range_ip) + )) .into()); } diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs index ac455fc7..d98f1edf 100644 --- a/crates/shirabe/src/util/perforce.rs +++ b/crates/shirabe/src/util/perforce.rs @@ -359,17 +359,13 @@ impl Perforce { if index.is_none() { return Ok(false); } - return Err(Exception { - message: format!("p4 command not found in path: {}", error_output), - code: 0, - } + return Err(Exception::new(format!( + "p4 command not found in path: {}", + error_output + )) .into()); } - return Err(Exception { - message: format!("Invalid user name: {}", user), - code: 0, - } - .into()); + return Err(Exception::new(format!("Invalid user name: {}", user)).into()); } Ok(true) @@ -497,11 +493,7 @@ impl Perforce { let spec = match fopen(&client_spec, "w") { Ok(spec) => spec, Err(e) => { - return Err(Exception { - message: e.to_string(), - code: 0, - } - .into()); + return Err(Exception::new(e.to_string()).into()); } }; let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { @@ -509,11 +501,7 @@ impl Perforce { })); if let Err(e) = result { fclose(&spec); - return Err(Exception { - message: format!("{:?}", e), - code: 0, - } - .into()); + return Err(Exception::new(format!("{:?}", e)).into()); } fclose(&spec); Ok(()) @@ -565,13 +553,10 @@ impl Perforce { process.run(None, indexmap::IndexMap::new())?; if !process.is_successful() { - return Err(Exception { - message: format!( - "Error logging in:{}", - self.process.borrow().get_error_output() - ), - code: 0, - } + return Err(Exception::new(format!( + "Error logging in:{}", + self.process.borrow().get_error_output() + )) .into()); } } diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs index a71fea92..2bb5cf99 100644 --- a/crates/shirabe/src/util/platform.rs +++ b/crates/shirabe/src/util/platform.rs @@ -36,10 +36,9 @@ impl Platform { return Ok(String::new()); } - return Err(RuntimeException { - message: "Could not determine the current working directory".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "Could not determine the current working directory".to_string(), + ) .into()); } @@ -159,11 +158,7 @@ impl Platform { } } - Err(RuntimeException { - message: "Could not determine user directory".to_string(), - code: 0, - } - .into()) + Err(RuntimeException::new("Could not determine user directory".to_string()).into()) } /// @return bool Whether the host machine is running on the Windows Subsystem for Linux (WSL) 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())?; diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs index 2dafc0d3..e2901b2c 100644 --- a/crates/shirabe/src/util/remote_filesystem.rs +++ b/crates/shirabe/src/util/remote_filesystem.rs @@ -14,6 +14,7 @@ use crate::util::http::ProxyManager; use crate::util::http::Response; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PHP_URL_HOST, PHP_URL_PATH, PHP_URL_SCHEME, PHP_VERSION_ID, PhpMixed, RuntimeException, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_PROGRESS, @@ -216,12 +217,11 @@ impl RemoteFilesystem { let mut file_url = file_url.to_string(); if options.contains_key("prevent_ip_access_callable") { - return Err(anyhow::anyhow!(RuntimeException { - message: - "RemoteFilesystem doesn't support the 'prevent_ip_access_callable' config." - .to_string(), - code: 0, - })); + return Err(RuntimeException::new( + "RemoteFilesystem doesn't support the 'prevent_ip_access_callable' config." + .to_string(), + ) + .into()); } if let Some(token) = options.get("gitlab-token").cloned() { @@ -404,7 +404,7 @@ impl RemoteFilesystem { })(); let mut caught_e: Option<anyhow::Error> = None; if let Err(mut e) = inner_result { - if let Some(te) = e.downcast_mut::<TransportException>() { + if let Some(te) = e.catch_mut::<TransportException>() { if !http_response_header.is_empty() && !http_response_header[0].is_empty() { te.set_headers(http_response_header.clone()); te.set_status_code(Self::find_status_code(&http_response_header)); @@ -535,7 +535,7 @@ impl RemoteFilesystem { ); } - let mut e = TransportException::new_with_code( + let mut e = TransportException::new( format!( "The \"{}\" file could not be downloaded ({})", self.file_url, http_response_header[0] @@ -607,13 +607,14 @@ impl RemoteFilesystem { if result.is_some() && file_name.is_some() && !is_redirect { let result_str = result.as_deref().unwrap(); if result_str.is_empty() { - return Err(anyhow::anyhow!(TransportException::new( + return Err(TransportException::new( format!( "\"{}\" appears broken, and returned an empty 200 response", self.file_url ), 0, - ))); + ) + .into()); } // TODO(phase-c): PHP captures the file_put_contents warning here via set_error_handler @@ -623,7 +624,7 @@ impl RemoteFilesystem { let write_result = file_put_contents(file_name.as_deref().unwrap(), result_str.as_bytes()); if write_result.is_none() { - return Err(anyhow::anyhow!(TransportException::new( + return Err(TransportException::new( format!( "The \"{}\" file could not be written to {}: {}", self.file_url, @@ -631,7 +632,8 @@ impl RemoteFilesystem { put_error_message ), 0, - ))); + ) + .into()); } let _ = put_error_message; } @@ -659,7 +661,7 @@ impl RemoteFilesystem { } if result.is_none() { - let mut e = TransportException::new_with_code( + let mut e = TransportException::new( format!( "The \"{}\" file could not be downloaded: {}", self.file_url, error_message @@ -750,11 +752,12 @@ impl RemoteFilesystem { && let Some(max) = max_file_size && Platform::strlen(r) >= max { - return Err(anyhow::anyhow!(MaxFileSizeExceededException::new(format!( + return Err(MaxFileSizeExceededException::new(format!( "Maximum allowed download size reached. Downloaded {} of allowed {} bytes", Platform::strlen(r), max - )))); + )) + .into()); } if PHP_VERSION_ID >= 80400 { @@ -785,14 +788,15 @@ impl RemoteFilesystem { match notification_code { x if x == STREAM_NOTIFY_FAILURE => { if 400 == message_code { - return Err(anyhow::anyhow!(TransportException::new_with_code( + return Err(TransportException::new( format!( "The '{}' URL could not be accessed: {}", self.file_url, message.unwrap_or_default() ), message_code, - ))); + ) + .into()); } } x if x == STREAM_NOTIFY_FILE_SIZE_IS => { @@ -848,10 +852,7 @@ impl RemoteFilesystem { self.retry = result.retry; if self.retry { - return Err(anyhow::anyhow!(TransportException::new( - "RETRY".to_string(), - 0, - ))); + return Err(TransportException::new("RETRY".to_string(), 0).into()); } Ok(()) } @@ -1043,10 +1044,11 @@ impl RemoteFilesystem { // RemoteFilesystem as a String; from_utf8_lossy can corrupt binary payloads Some(d) => Some(String::from_utf8_lossy(&d).into_owned()), None => { - return Err(anyhow::anyhow!(TransportException::new( + return Err(TransportException::new( "Failed to decode zlib stream".to_string(), 0, - ))); + ) + .into()); } }; } diff --git a/crates/shirabe/src/util/stream_context_factory.rs b/crates/shirabe/src/util/stream_context_factory.rs index cae00c08..b407e897 100644 --- a/crates/shirabe/src/util/stream_context_factory.rs +++ b/crates/shirabe/src/util/stream_context_factory.rs @@ -22,7 +22,7 @@ impl StreamContextFactory { url: &str, default_options: IndexMap<String, PhpMixed>, default_params: IndexMap<String, PhpMixed>, - ) -> anyhow::Result<PhpMixed, TransportException> { + ) -> anyhow::Result<PhpMixed, Box<TransportException>> { let mut options: IndexMap<String, PhpMixed> = { let mut http = IndexMap::new(); // specify defaults again to try and work better with curlwrappers enabled @@ -66,7 +66,7 @@ impl StreamContextFactory { url: &str, mut options: IndexMap<String, PhpMixed>, for_curl: bool, - ) -> anyhow::Result<IndexMap<String, PhpMixed>, TransportException> { + ) -> anyhow::Result<IndexMap<String, PhpMixed>, Box<TransportException>> { // Make sure the headers are in an array form let has_header = options .get("http") @@ -105,23 +105,23 @@ impl StreamContextFactory { if proxy.is_secure() { if !extension_loaded("openssl") { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "You must enable the openssl extension to use a secure proxy." .to_string(), 0, - )); + ))); } if is_https_request { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "You must enable the curl extension to make https requests through a secure proxy.".to_string(), 0, - )); + ))); } } else if is_https_request && !extension_loaded("openssl") { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "You must enable the openssl extension to make https requests through a proxy.".to_string(), 0, - )); + ))); } // Header will be a Proxy-Authorization string or not set @@ -224,7 +224,7 @@ impl StreamContextFactory { // `logger` was a PSR LoggerInterface; CaBundle is slated for removal so // it is now an unused `()` placeholder. logger: (), - ) -> anyhow::Result<IndexMap<String, PhpMixed>, TransportException> { + ) -> anyhow::Result<IndexMap<String, PhpMixed>, Box<TransportException>> { let ciphers = [ "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256", @@ -336,10 +336,10 @@ impl StreamContextFactory { if let Some(ref cafile) = cafile && (!Filesystem::is_readable(cafile) || !CaBundle::validate_ca_file(cafile, logger)) { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "The configured cafile was not valid or could not be read.".to_string(), 0, - )); + ))); } let capath = defaults @@ -351,10 +351,10 @@ impl StreamContextFactory { if let Some(ref capath) = capath && (!shirabe_php_shim::is_dir(capath) || !Filesystem::is_readable(capath)) { - return Err(TransportException::new( + return Err(Box::new(TransportException::new( "The configured capath was not valid or could not be read.".to_string(), 0, - )); + ))); } // Disable TLS compression to prevent CRIME attacks where supported. diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs index a2974760..aaa0c6d1 100644 --- a/crates/shirabe/src/util/svn.rs +++ b/crates/shirabe/src/util/svn.rs @@ -177,11 +177,7 @@ impl Svn { && stripos(&full_output, "svn: E170001:").is_none() && stripos(&full_output, "svn: E215004:").is_none() { - return Err(RuntimeException { - message: full_output, - code: 0, - } - .into()); + return Err(RuntimeException::new(full_output).into()); } if !self.has_auth() { @@ -196,11 +192,7 @@ impl Svn { return self.execute_with_auth_retry(svn_command, cwd, url, path, verbose); } - Err(RuntimeException { - message: format!("wrong credentials provided ({})", full_output), - code: 0, - } - .into()) + Err(RuntimeException::new(format!("wrong credentials provided ({})", full_output)).into()) } pub fn set_cache_credentials(&mut self, cache_credentials: bool) { @@ -213,10 +205,9 @@ impl Svn { pub(crate) fn do_auth_dance(&mut self) -> anyhow::Result<&mut Self> { // cannot ask for credentials in non interactive mode if !self.io.is_interactive() { - return Err(RuntimeException { - message: "can not ask for authentication in non interactive mode".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "can not ask for authentication in non interactive mode".to_string(), + ) .into()); } @@ -310,11 +301,7 @@ impl Svn { /// @throws \LogicException pub(crate) fn get_password(&self) -> anyhow::Result<String> { if self.credentials.is_none() { - return Err(LogicException { - message: "No svn auth detected.".to_string(), - code: 0, - } - .into()); + return Err(LogicException::new("No svn auth detected.".to_string()).into()); } Ok(self.credentials.as_ref().unwrap().password.clone()) @@ -325,11 +312,7 @@ impl Svn { /// @throws \LogicException pub(crate) fn get_username(&self) -> anyhow::Result<String> { if self.credentials.is_none() { - return Err(LogicException { - message: "No svn auth detected.".to_string(), - code: 0, - } - .into()); + return Err(LogicException::new("No svn auth detected.".to_string()).into()); } Ok(self.credentials.as_ref().unwrap().username.clone()) diff --git a/crates/shirabe/src/util/tar.rs b/crates/shirabe/src/util/tar.rs index f5b0ebbe..94320edb 100644 --- a/crates/shirabe/src/util/tar.rs +++ b/crates/shirabe/src/util/tar.rs @@ -20,10 +20,8 @@ impl Tar { /// UTF-8 could never survive the JSON parsing that follows in PHP either. fn content_to_string(content: Vec<u8>) -> anyhow::Result<String> { String::from_utf8(content).map_err(|_| { - anyhow::anyhow!(RuntimeException { - message: "composer.json in the archive is not valid UTF-8".to_string(), - code: 0, - }) + RuntimeException::new("composer.json in the archive is not valid UTF-8".to_string()) + .into() }) } @@ -38,17 +36,14 @@ impl Tar { if folder_file.is_dir() { top_level_paths.insert(name, true); if top_level_paths.len() > 1 { - return Err(anyhow::anyhow!(RuntimeException { - message: format!( - "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", - top_level_paths - .keys() - .cloned() - .collect::<Vec<_>>() - .join(",") - ), - code: 0, - })); + return Err(RuntimeException::new(format!( + "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", + top_level_paths + .keys() + .cloned() + .collect::<Vec<_>>() + .join(",") + )).into()); } } } @@ -63,11 +58,10 @@ impl Tar { return Self::content_to_string(file.get_content()); } - Err(anyhow::anyhow!(RuntimeException { - message: - "No composer.json found either at the top level or within the topmost directory" - .to_string(), - code: 0, - })) + Err(RuntimeException::new( + "No composer.json found either at the top level or within the topmost directory" + .to_string(), + ) + .into()) } } diff --git a/crates/shirabe/src/util/zip.rs b/crates/shirabe/src/util/zip.rs index a671d63b..0ba177d0 100644 --- a/crates/shirabe/src/util/zip.rs +++ b/crates/shirabe/src/util/zip.rs @@ -10,10 +10,9 @@ pub struct Zip; impl Zip { pub fn get_composer_json(path_to_zip: &str) -> anyhow::Result<Option<String>> { if !extension_loaded("zip") { - return Err(RuntimeException { - message: "The Zip Util requires PHP's zip extension".to_string(), - code: 0, - } + return Err(RuntimeException::new( + "The Zip Util requires PHP's zip extension".to_string(), + ) .into()); } @@ -64,13 +63,10 @@ impl Zip { if dir_name == "." { top_level_paths.insert(name, true); if top_level_paths.len() > 1 { - return Err(RuntimeException { - message: format!( - "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", - implode(",", &top_level_paths.keys().cloned().collect::<Vec<_>>()) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", + implode(",", &top_level_paths.keys().cloned().collect::<Vec<_>>()) + )) .into()); } continue; @@ -80,13 +76,10 @@ impl Zip { if !dir_name.contains('\\') && !dir_name.contains('/') { top_level_paths.insert(format!("{}/", dir_name), true); if top_level_paths.len() > 1 { - return Err(RuntimeException { - message: format!( - "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", - implode(",", &top_level_paths.keys().cloned().collect::<Vec<_>>()) - ), - code: 0, - } + return Err(RuntimeException::new(format!( + "Archive has more than one top level directories, and no composer.json was found on the top level, so it's an invalid archive. Top level paths found were: {}", + implode(",", &top_level_paths.keys().cloned().collect::<Vec<_>>()) + )) .into()); } } @@ -101,12 +94,10 @@ impl Zip { } } - Err(RuntimeException { - message: - "No composer.json found either at the top level or within the topmost directory" - .to_string(), - code: 0, - } + Err(RuntimeException::new( + "No composer.json found either at the top level or within the topmost directory" + .to_string(), + ) .into()) } } diff --git a/crates/shirabe/tests/downloader/download_manager_test.rs b/crates/shirabe/tests/downloader/download_manager_test.rs index 9d12bad6..dd894565 100644 --- a/crates/shirabe/tests/downloader/download_manager_test.rs +++ b/crates/shirabe/tests/downloader/download_manager_test.rs @@ -253,13 +253,7 @@ fn test_full_package_download_failover() { .expect_download() .times(1) .withf(|_pkg, path, _prev, _output| path == "target_dir") - .returning(|_, _, _, _| { - Err(RuntimeException { - message: "Foo".to_string(), - code: 0, - } - .into()) - }); + .returning(|_, _, _, _| Err(RuntimeException::new("Foo".to_string()).into())); let mut downloader_success = downloader_mock("source"); downloader_success diff --git a/crates/shirabe/tests/downloader/file_downloader_test.rs b/crates/shirabe/tests/downloader/file_downloader_test.rs index 469bf473..eac4e45e 100644 --- a/crates/shirabe/tests/downloader/file_downloader_test.rs +++ b/crates/shirabe/tests/downloader/file_downloader_test.rs @@ -17,6 +17,7 @@ use shirabe::util::HttpDownloader; use shirabe::util::filesystem::{Filesystem, FilesystemMock}; use shirabe::util::http_downloader::HttpDownloaderMockHandler; use shirabe::util::r#loop::Loop; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ InvalidArgumentException, PhpMixed, RuntimeException, UnexpectedValueException, }; @@ -84,7 +85,7 @@ fn test_download_for_package_without_dist_reference() { let e = result.expect_err("expected InvalidArgumentException"); assert!( - e.downcast_ref::<InvalidArgumentException>().is_some(), + e.is_instanceof::<InvalidArgumentException>(), "expected InvalidArgumentException, got: {e}" ); } @@ -107,7 +108,7 @@ fn test_download_to_existing_file() { let e = result.expect_err("download to an existing file was expected to throw"); assert!( - e.downcast_ref::<RuntimeException>().is_some(), + e.is_instanceof::<RuntimeException>(), "expected RuntimeException, got: {e}" ); assert!( @@ -167,7 +168,7 @@ fn test_download_but_file_is_unsaved() { let e = result.expect_err("download was expected to throw"); assert!( - e.downcast_ref::<UnexpectedValueException>().is_some(), + e.is_instanceof::<UnexpectedValueException>(), "expected UnexpectedValueException, got: {e}" ); assert!( @@ -294,7 +295,7 @@ fn test_download_file_with_invalid_checksum() { let e = result.expect_err("download was expected to throw"); assert!( - e.downcast_ref::<UnexpectedValueException>().is_some(), + e.is_instanceof::<UnexpectedValueException>(), "expected UnexpectedValueException, got: {e}" ); assert!( diff --git a/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs b/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs index af8a33db..817a6954 100644 --- a/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs +++ b/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs @@ -27,6 +27,7 @@ use shirabe::util::platform::Platform; use shirabe::util::process_executor::{MockHandler, ProcessExecutor}; use shirabe_class_map_generator::class_map::ClassMap; use shirabe_external_packages::symfony::console::output::output_interface; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PHP_EOL, PhpMixed}; fn tear_down() { @@ -286,8 +287,7 @@ fn test_dispatcher_detect_infinite_recursion() { let result = dispatcher.dispatch(Some("root"), Some(&mut event)); let err = result.expect_err("infinite recursion must raise a RuntimeException"); assert!( - err.downcast_ref::<shirabe_php_shim::RuntimeException>() - .is_some(), + err.is_instanceof::<shirabe_php_shim::RuntimeException>(), "expected RuntimeException, got: {err:?}" ); } @@ -392,8 +392,7 @@ fn test_listener_exceptions_are_caught() { let e = result.expect_err("expected RuntimeException"); assert!( - e.downcast_ref::<shirabe_php_shim::RuntimeException>() - .is_some(), + e.is_instanceof::<shirabe_php_shim::RuntimeException>(), "got: {e:?}" ); } diff --git a/crates/shirabe/tests/json/composer_schema_test.rs b/crates/shirabe/tests/json/composer_schema_test.rs index 9cbea150..f2a5da44 100644 --- a/crates/shirabe/tests/json/composer_schema_test.rs +++ b/crates/shirabe/tests/json/composer_schema_test.rs @@ -9,6 +9,7 @@ //! which property) is identical to upstream. use shirabe::json::{JsonFile, JsonValidationException}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::json_decode; const NAME_PATTERN: &str = r#"^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]|-{1,2})?[a-z0-9]+)*$"#; @@ -22,7 +23,7 @@ fn check(json: &str) -> Vec<String> { match JsonFile::validate_json_schema("test", &data, JsonFile::LAX_SCHEMA, None) { Ok(_) => Vec::new(), Err(e) => e - .downcast_ref::<JsonValidationException>() + .catch::<JsonValidationException>() .unwrap() .get_errors() .clone(), diff --git a/crates/shirabe/tests/json/json_file_test.rs b/crates/shirabe/tests/json/json_file_test.rs index 49ac1605..a20e7234 100644 --- a/crates/shirabe/tests/json/json_file_test.rs +++ b/crates/shirabe/tests/json/json_file_test.rs @@ -3,6 +3,7 @@ use indexmap::IndexMap; use shirabe::json::{JsonEncodeOptions, JsonFile, JsonValidationException}; use shirabe_external_packages::seld::json_lint::ParsingException; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::PhpMixed; /// ref: JsonFileTest::expectParseException @@ -269,14 +270,14 @@ fn test_schema_validation_error() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); assert!(e.get_errors().contains(&expected_error)); let err = json .validate_schema(JsonFile::LAX_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); assert!(e.get_errors().contains(&expected_error)); } @@ -298,7 +299,7 @@ fn test_schema_validation_lax_additional_properties() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!( format!("\"{}\" does not match the expected JSON schema", file), e.get_message() @@ -327,7 +328,7 @@ fn test_schema_validation_lax_required() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); let errors = e.get_errors(); assert!(errors.contains(&"name : \"name\" is a required property".to_string())); @@ -338,7 +339,7 @@ fn test_schema_validation_lax_required() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); assert_eq!( &vec!["description : \"description\" is a required property".to_string()], @@ -350,7 +351,7 @@ fn test_schema_validation_lax_required() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); assert_eq!( &vec!["name : \"name\" is a required property".to_string()], @@ -362,7 +363,7 @@ fn test_schema_validation_lax_required() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); let errors = e.get_errors(); assert!(errors.contains(&"name : \"name\" is a required property".to_string())); @@ -373,7 +374,7 @@ fn test_schema_validation_lax_required() { let err = json .validate_schema(JsonFile::STRICT_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); let errors = e.get_errors(); assert!(errors.contains(&"name : \"name\" is a required property".to_string())); @@ -444,7 +445,7 @@ fn test_auth_schema_validation_with_custom_data_source() { let err = JsonFile::validate_json_schema("COMPOSER_AUTH", &json, JsonFile::AUTH_SCHEMA, None) .unwrap_err(); - let e = err.downcast_ref::<JsonValidationException>().unwrap(); + let e = err.catch::<JsonValidationException>().unwrap(); assert_eq!(expected_message, e.get_message()); assert_eq!(&vec![expected_error], e.get_errors()); } @@ -519,7 +520,7 @@ fn test_composer_lock_file_merge_conflict_complex() { std::fs::read_to_string(fixture_path("composer-lock-merge-conflict-complex.txt")).unwrap(); let err = JsonFile::parse_json(Some(&data), Some("/path/to/composer.lock")).unwrap_err(); - assert!(err.downcast_ref::<ParsingException>().is_some()); + assert!(err.is_instanceof::<ParsingException>()); } #[test] @@ -531,7 +532,7 @@ fn test_composer_lock_file_merge_conflict_complex_crlf() { .unwrap(); let err = JsonFile::parse_json(Some(&data), Some("/path/to/composer.lock")).unwrap_err(); - assert!(err.downcast_ref::<ParsingException>().is_some()); + assert!(err.is_instanceof::<ParsingException>()); } #[test] diff --git a/crates/shirabe/tests/package/archiver/archive_manager_test.rs b/crates/shirabe/tests/package/archiver/archive_manager_test.rs index a7235bf9..84feb5bc 100644 --- a/crates/shirabe/tests/package/archiver/archive_manager_test.rs +++ b/crates/shirabe/tests/package/archiver/archive_manager_test.rs @@ -13,6 +13,7 @@ use shirabe::util::ProcessExecutor; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe_external_packages::symfony::process::Process; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{ PhpMixed, file_exists, file_put_contents, realpath, sys_get_temp_dir, unlink, }; @@ -159,10 +160,7 @@ fn test_unknown_format() { ); let err = result.expect_err("expected RuntimeException for unknown format"); - assert!( - err.downcast_ref::<shirabe_php_shim::RuntimeException>() - .is_some() - ); + assert!(err.is_instanceof::<shirabe_php_shim::RuntimeException>()); } // ref: ArchiveManagerTest::testArchiveTar / testArchiveCustomFileName. diff --git a/crates/shirabe/tests/package/loader/validating_array_loader_test.rs b/crates/shirabe/tests/package/loader/validating_array_loader_test.rs index 07f3e4a6..1b1ed448 100644 --- a/crates/shirabe/tests/package/loader/validating_array_loader_test.rs +++ b/crates/shirabe/tests/package/loader/validating_array_loader_test.rs @@ -4,6 +4,7 @@ use crate::test_case; use indexmap::IndexMap; use shirabe::package::handle::PackageInterfaceHandle; use shirabe::package::loader::{InvalidPackageException, LoaderInterface, ValidatingArrayLoader}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::PhpMixed; fn s(v: &str) -> PhpMixed { @@ -792,7 +793,7 @@ fn test_load_failure_throws_exception() { Ok(_) => panic!("Expected exception to be thrown"), Err(e) => { let exception = e - .downcast_ref::<InvalidPackageException>() + .catch::<InvalidPackageException>() .expect("Expected InvalidPackageException"); let mut errors: Vec<String> = exception.get_errors().to_vec(); expected_errors.sort(); diff --git a/crates/shirabe/tests/package/locker_test.rs b/crates/shirabe/tests/package/locker_test.rs index f1747166..4732d679 100644 --- a/crates/shirabe/tests/package/locker_test.rs +++ b/crates/shirabe/tests/package/locker_test.rs @@ -10,6 +10,7 @@ use shirabe::package::handle::{CompletePackageHandle, PackageInterfaceHandle}; use shirabe::plugin::plugin_interface; use shirabe::repository::{FindPackageConstraint, RepositoryInterfaceHandle}; use shirabe::util::process_executor::ProcessExecutor; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{LogicException, PhpMixed, hash}; use tempfile::TempDir; @@ -83,7 +84,7 @@ fn test_get_not_locked_packages() { .get_locked_repository(false) .expect_err("getLockedRepository should fail when no lock file exists"); assert!( - err.downcast_ref::<LogicException>().is_some(), + err.is_instanceof::<LogicException>(), "expected LogicException, got: {err}" ); } @@ -219,7 +220,7 @@ fn test_lock_bad_packages() { ) .expect_err("setLockData should fail for a package with no version"); assert!( - err.downcast_ref::<LogicException>().is_some(), + err.is_instanceof::<LogicException>(), "expected LogicException, got: {err}" ); } diff --git a/crates/shirabe/tests/plugin/plugin_installer_test.rs b/crates/shirabe/tests/plugin/plugin_installer_test.rs index 06cd2ce9..0c82a11a 100644 --- a/crates/shirabe/tests/plugin/plugin_installer_test.rs +++ b/crates/shirabe/tests/plugin/plugin_installer_test.rs @@ -31,6 +31,7 @@ use shirabe::util::r#loop::Loop; use shirabe::util::process_executor::ProcessExecutor; use shirabe_external_packages::symfony::console::output::output_interface::VERBOSITY_NORMAL; use shirabe_external_packages::symfony::process::PhpExecutableFinder; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::PhpMixed; use shirabe_semver::VersionParser; use tempfile::TempDir; @@ -859,8 +860,7 @@ fn test_querying_with_invalid_capability_class_name_throws() { ), }; assert!( - err.downcast_ref::<shirabe_php_shim::UnexpectedValueException>() - .is_some(), + err.is_instanceof::<shirabe_php_shim::UnexpectedValueException>(), "expected UnexpectedValueException for {invalid_implementation_class_name:?}, got: {err}" ); // PHP: ->expects($this->once())->method('getCapabilities'). diff --git a/crates/shirabe/tests/repository/filesystem_repository_test.rs b/crates/shirabe/tests/repository/filesystem_repository_test.rs index 3279f29e..a9ac91a0 100644 --- a/crates/shirabe/tests/repository/filesystem_repository_test.rs +++ b/crates/shirabe/tests/repository/filesystem_repository_test.rs @@ -13,6 +13,7 @@ use shirabe::package::{Link, PackageInterfaceHandle, RootAliasPackageHandle, Roo use shirabe::repository::RepositoryInterface; use shirabe::repository::filesystem_repository::FilesystemRepository; use shirabe::util::filesystem::Filesystem; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::PhpMixed; use shirabe_semver::VersionParser; @@ -50,7 +51,6 @@ fn test_repository_read() { assert_eq!(packages[0].get_type(), "vendor"); } -#[ignore = "InvalidRepositoryException message building calls shirabe_php_shim::var::get_class_err(), which is still todo!()"] #[test] fn test_corrupted_repository_file() { // PHP mocks read() to return the scalar string 'foo'; a real file containing the JSON string @@ -63,7 +63,7 @@ fn test_corrupted_repository_file() { let result = repository.get_packages(); let err = result.unwrap_err(); assert!( - err.is::<shirabe::repository::InvalidRepositoryException>(), + err.is_instanceof::<shirabe::repository::InvalidRepositoryException>(), "expected InvalidRepositoryException, got: {err}" ); } diff --git a/crates/shirabe/tests/repository/vcs/git_bitbucket_driver_test.rs b/crates/shirabe/tests/repository/vcs/git_bitbucket_driver_test.rs index 4d84bcf2..67e9c8a9 100644 --- a/crates/shirabe/tests/repository/vcs/git_bitbucket_driver_test.rs +++ b/crates/shirabe/tests/repository/vcs/git_bitbucket_driver_test.rs @@ -10,6 +10,7 @@ use shirabe::repository::vcs::GitBitbucketDriver; use shirabe::util::filesystem::Filesystem; use shirabe::util::http_downloader::{HttpDownloader, HttpDownloaderMockHandler}; use shirabe::util::process_executor::ProcessExecutor; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{InvalidArgumentException, PhpMixed, RuntimeException}; use tempfile::TempDir; @@ -112,11 +113,11 @@ fn test_get_root_identifier_wrong_scm_type() { let err = driver.get_root_identifier().unwrap_err(); let runtime = err - .downcast_ref::<RuntimeException>() + .catch::<RuntimeException>() .expect("expected RuntimeException"); assert_eq!( "https://bitbucket.org/user/repo.git does not appear to be a git repository, use https://bitbucket.org/user/repo but remember that Bitbucket no longer supports the mercurial repositories. https://bitbucket.org/blog/sunsetting-mercurial-support-in-bitbucket", - runtime.message + runtime.get_message() ); } @@ -250,7 +251,7 @@ fn test_initialize_invalid_repository_url() { let result = get_driver("https://bitbucket.org/acme", io, config, http_downloader); let err = result.unwrap_err(); assert!( - err.downcast_ref::<InvalidArgumentException>().is_some(), + err.is_instanceof::<InvalidArgumentException>(), "expected InvalidArgumentException, got: {err:?}" ); } diff --git a/crates/shirabe/tests/repository/vcs/git_driver_test.rs b/crates/shirabe/tests/repository/vcs/git_driver_test.rs index 7a85d4cc..8494c1ca 100644 --- a/crates/shirabe/tests/repository/vcs/git_driver_test.rs +++ b/crates/shirabe/tests/repository/vcs/git_driver_test.rs @@ -12,6 +12,7 @@ use shirabe::util::filesystem::Filesystem; use shirabe::util::http_downloader::HttpDownloaderMockHandler; use shirabe::util::platform::Platform; use shirabe::util::process_executor::MockHandler; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, RuntimeException}; use tempfile::TempDir; @@ -293,7 +294,7 @@ fn test_file_get_content_invalid_identifier() { assert_eq!(None, driver.get_file_content("file.txt", "h").unwrap()); let err = driver.get_file_content("file.txt", "-h").unwrap_err(); - assert!(err.downcast_ref::<RuntimeException>().is_some()); + assert!(err.is_instanceof::<RuntimeException>()); } #[test] @@ -324,5 +325,5 @@ fn test_get_change_date_invalid_identifier() { let mut driver = GitDriver::new(repo_config, io, config, http_downloader, process); let err = driver.get_change_date("-n1 --format=%at HEAD").unwrap_err(); - assert!(err.downcast_ref::<RuntimeException>().is_some()); + assert!(err.is_instanceof::<RuntimeException>()); } diff --git a/crates/shirabe/tests/repository/vcs/hg_driver_test.rs b/crates/shirabe/tests/repository/vcs/hg_driver_test.rs index be36f412..4245e140 100644 --- a/crates/shirabe/tests/repository/vcs/hg_driver_test.rs +++ b/crates/shirabe/tests/repository/vcs/hg_driver_test.rs @@ -11,6 +11,7 @@ use shirabe::repository::vcs::HgDriver; use shirabe::util::filesystem::Filesystem; use shirabe::util::http_downloader::HttpDownloaderMockHandler; use shirabe::util::process_executor::MockHandler; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, RuntimeException}; use tempfile::TempDir; @@ -141,7 +142,7 @@ fn test_file_get_content_invalid_identifier() { assert_eq!(None, driver.get_file_content("file.txt", "h").unwrap()); let err = driver.get_file_content("file.txt", "-h").unwrap_err(); - assert!(err.downcast_ref::<RuntimeException>().is_some()); + assert!(err.is_instanceof::<RuntimeException>()); } #[test] @@ -168,5 +169,5 @@ fn test_get_change_date_invalid_identifier() { let driver = HgDriver::new(repo_config, io, config, http_downloader, process); let err = driver.get_change_date("-r foo").unwrap_err(); - assert!(err.downcast_ref::<RuntimeException>().is_some()); + assert!(err.is_instanceof::<RuntimeException>()); } diff --git a/crates/shirabe/tests/repository/vcs/svn_driver_test.rs b/crates/shirabe/tests/repository/vcs/svn_driver_test.rs index 6a74063d..bc45b082 100644 --- a/crates/shirabe/tests/repository/vcs/svn_driver_test.rs +++ b/crates/shirabe/tests/repository/vcs/svn_driver_test.rs @@ -11,6 +11,7 @@ use shirabe::repository::vcs::SvnDriver; use shirabe::util::filesystem::Filesystem; use shirabe::util::http_downloader::HttpDownloaderMockHandler; use shirabe::util::process_executor::MockHandler; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, RuntimeException}; use tempfile::TempDir; @@ -130,10 +131,10 @@ fn test_wrong_credentials_in_url() { let mut svn = SvnDriver::new(repo_config, console, config, http_downloader, process); let err = svn.initialize().unwrap_err(); let runtime = err - .downcast_ref::<RuntimeException>() + .catch::<RuntimeException>() .expect("expected RuntimeException"); assert_eq!( "Repository https://till:secret@corp.svn.local/repo could not be processed, wrong credentials provided (svn: OPTIONS of 'https://corp.svn.local/repo': authorization failed: Could not authenticate to server: rejected Basic challenge (https://corp.svn.local/))", - runtime.message + runtime.get_message() ); } diff --git a/crates/shirabe/tests/util/auth_helper_test.rs b/crates/shirabe/tests/util/auth_helper_test.rs index ef5daaf7..6b376f97 100644 --- a/crates/shirabe/tests/util/auth_helper_test.rs +++ b/crates/shirabe/tests/util/auth_helper_test.rs @@ -7,6 +7,7 @@ use shirabe::config::ConfigSourceInterface; use shirabe::io::IOInterface; use shirabe::io::io_interface; use shirabe::util::{AuthHelper, Bitbucket, StoreAuth}; +use shirabe_php_shim::Catch as _; use shirabe_php_shim::{PhpMixed, base64_encode, json_encode}; // Mirrors AuthHelperTest::setUp: a DEBUG-verbosity IOMock plus a real Config, both @@ -560,7 +561,7 @@ fn test_store_auth_with_prompt_invalid_answer() { let err = auth_helper .store_auth(origin, StoreAuth::Prompt) .expect_err("expected a RuntimeException"); - assert!(err.downcast_ref::<RuntimeException>().is_some()); + assert!(err.is_instanceof::<RuntimeException>()); // Mirrors PHP's `->with('Do you want to store credentials for '.$origin.' in '. // $configSourceName.' ? [Yn] ', $this->anything(), null, 'y')` verification on askAndValidate. @@ -639,7 +640,7 @@ fn test_prompt_auth_if_needed_git_lab_no_auth_change() { ); let err = result.expect_err("expected a TransportException"); - assert!(err.downcast_ref::<TransportException>().is_some()); + assert!(err.is_instanceof::<TransportException>()); assert_eq!( vec