From f4cad2123b2af0de72bda4ce039e16e74f163f4e Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 22:14:12 +0900 Subject: feat(php-shim): give ported exceptions PHP's class hierarchy Ported exceptions were flat structs reached with `downcast_ref`, so Composer's `catch (\RuntimeException $e)` only matched the exact leaf type and `get_class($e)` had nothing to report. Each exception now embeds an instance of the class it extends and travels inside an `AnyThrowable`; `Catch::catch`/`catch_mut` walk that chain, and `PhpClass::php_class_name` yields the PHP FQCN. Dropping the `std::error::Error` impls from the exception types leaves `AnyThrowable` as the only route into an `anyhow::Error`, so the walk cannot be bypassed. A `no_exception_downcast` linter catches the `downcast::()` calls that would now silently answer `None`. Three sites change behavior as a result: the `TransportException` exit-code override reaches `MaxFileSizeExceededException`, the `catch (\LogicException)` in findSimilar() reaches its subclasses, and rendered exception titles carry the real class name rather than a guess. `get_class_err()` is no longer a `todo!()`, which re-enables FilesystemRepositoryTest::testCorruptedRepositoryFile. Co-Authored-By: Claude Opus 5 (1M context) --- .../shirabe/src/repository/artifact_repository.rs | 26 ++- .../shirabe/src/repository/composer_repository.rs | 208 ++++++++------------- .../src/repository/filesystem_repository.rs | 47 +++-- crates/shirabe/src/repository/filter_repository.rs | 44 ++--- .../src/repository/invalid_repository_exception.rs | 14 +- .../shirabe/src/repository/package_repository.rs | 25 +-- crates/shirabe/src/repository/path_repository.rs | 38 ++-- .../shirabe/src/repository/platform_repository.rs | 52 +++--- .../shirabe/src/repository/repository_factory.rs | 62 +++--- .../shirabe/src/repository/repository_manager.rs | 16 +- .../repository/repository_security_exception.rs | 12 +- crates/shirabe/src/repository/repository_set.rs | 35 ++-- .../shirabe/src/repository/vcs/forgejo_driver.rs | 62 +++--- crates/shirabe/src/repository/vcs/fossil_driver.rs | 63 +++---- .../src/repository/vcs/git_bitbucket_driver.rs | 38 ++-- crates/shirabe/src/repository/vcs/git_driver.rs | 78 +++----- crates/shirabe/src/repository/vcs/github_driver.rs | 60 +++--- crates/shirabe/src/repository/vcs/gitlab_driver.rs | 56 +++--- crates/shirabe/src/repository/vcs/hg_driver.rs | 41 ++-- .../shirabe/src/repository/vcs/perforce_driver.rs | 14 +- crates/shirabe/src/repository/vcs/svn_driver.rs | 39 ++-- crates/shirabe/src/repository/vcs/vcs_driver.rs | 21 ++- crates/shirabe/src/repository/vcs_repository.rs | 19 +- 23 files changed, 439 insertions(+), 631 deletions(-) (limited to 'crates/shirabe/src/repository') 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>, ) -> anyhow::Result { 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> { 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::() + if let Some(te) = e.catch::() && 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::() { + if let Some(te) = e.catch::() { 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 = 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 { 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> = 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 { 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> { 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::().is_some() { continue; } - if e.downcast_ref::().is_some() { + if e.is_instanceof::() { return Err(e); } - if let Some(te) = e.downcast_ref::() + if let Some(te) = e.catch::() && te.get_status_code() == Some(404) { return Err(e); } - if e.downcast_ref::().is_some() { + if e.is_instanceof::() { 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 { 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::().is_some() { + if e.is_instanceof::() { return Err(e); } - if let Some(te) = e.downcast_ref::() + if let Some(te) = e.catch::() && te.get_status_code() == Some(404) { return Err(e); @@ -3109,10 +3069,9 @@ impl ComposerRepository { last_modified_time: Option<&str>, ) -> anyhow::Result { 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 { - if let Some(te) = e.downcast_ref::() + if let Some(te) = e.catch::() && 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::() + if let Some(te) = e.catch::() && 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 { 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>>, ) -> anyhow::Result { 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 = 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 { 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::().is_none() { + if !e.is_instanceof::() { return Err(e); } if !ignore_unreachable { return Err(e); } let message = e - .downcast_ref::() + .catch::() .unwrap() - .message - .clone(); + .get_message() + .to_string(); unreachable_repos.push(message); } } @@ -482,11 +480,9 @@ impl RepositorySet { || repo_ref.as_any().is::() }; 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::() }; 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 = 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 = 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 { + ) -> anyhow::Result> { 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::().is_some() { + if e.is_instanceof::() { 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> { 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::().is_some() { + if e.is_instanceof::() { 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::().is_some() { + if e.is_instanceof::() { 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::().is_some() { + if e.is_instanceof::() { 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> { 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>> { 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::().is_some() { + if e.is_instanceof::() { 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::().is_some() { + if e.is_instanceof::() { 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 { + ) -> anyhow::Result> { 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 { 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 = 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::().is_some() { + if e.is_instanceof::() { 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 = 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 { + ) -> anyhow::Result> { 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!( "Failed to download {}/{}:{}", - 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::().is_some() { + if e.is_instanceof::() { 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> { 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>> { 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::().is_some() { + if e.is_instanceof::() { 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 { - 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::().is_none() { + if !e.is_instanceof::() { return Err(e); } let message = e - .downcast_ref::() + .catch::() .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::() { - return Err(TransportException::new(e.message.clone(), 0).into()); + if let Some(e) = e.catch::() { + 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::().is_some() { + if e.is_instanceof::() { 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 { + pub fn get_contents(&self, url: &str) -> anyhow::Result> { 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::() { - Ok(te) => te, - Err(other) => TransportException::new(other.to_string(), 0), + .map_err(|e| { + Box::new(match e.catch::() { + 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 { + fn get_contents(&self, url: &str) -> anyhow::Result> { 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::() { - Ok(te) => te, - Err(other) => TransportException::new(other.to_string(), 0), + .map_err(|e| { + Box::new(match e.catch::() { + 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::() + if let Some(te) = e.catch::() && self.should_rethrow_transport_exception(te) { return Err(e); @@ -367,7 +368,7 @@ impl VcsRepository { } } Err(e) => { - if let Some(te) = e.downcast_ref::() + if let Some(te) = e.catch::() && 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::() { + if let Some(te) = e.catch::() { 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::() { + let detail = if let Some(te) = e.catch::() { 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::() { + if let Some(te) = e.catch::() { self.version_transport_exceptions .borrow_mut() .entry("branches".to_string()) -- cgit v1.3.1-4-g156e