diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-08 22:14:12 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-08 22:14:12 +0900 |
| commit | f4cad2123b2af0de72bda4ce039e16e74f163f4e (patch) | |
| tree | 21803308c5ff41e23c9d3b117433eea16b4ff663 /crates/shirabe/src/util | |
| parent | 0209f63210e5b547b5c6b73367bb80ea86c255ec (diff) | |
| download | php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.tar.gz php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.tar.zst php-shirabe-f4cad2123b2af0de72bda4ce039e16e74f163f4e.zip | |
feat(php-shim): give ported exceptions PHP's class hierarchy
Ported exceptions were flat structs reached with `downcast_ref`, so
Composer's `catch (\RuntimeException $e)` only matched the exact leaf
type and `get_class($e)` had nothing to report. Each exception now
embeds an instance of the class it extends and travels inside an
`AnyThrowable`; `Catch::catch`/`catch_mut` walk that chain, and
`PhpClass::php_class_name` yields the PHP FQCN.
Dropping the `std::error::Error` impls from the exception types leaves
`AnyThrowable` as the only route into an `anyhow::Error`, so the walk
cannot be bypassed. A `no_exception_downcast` linter catches the
`downcast::<X>()` calls that would now silently answer `None`.
Three sites change behavior as a result: the `TransportException`
exit-code override reaches `MaxFileSizeExceededException`, the
`catch (\LogicException)` in findSimilar() reaches its subclasses, and
rendered exception titles carry the real class name rather than a
guess. `get_class_err()` is no longer a `todo!()`, which re-enables
FilesystemRepositoryTest::testCorruptedRepositoryFile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/util')
24 files changed, 289 insertions, 424 deletions
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()) } } |
