aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--crates/shirabe-external-packages/src/symfony/filesystem/exception/io_exception.rs2
-rw-r--r--crates/shirabe-php-shim/src/exception.rs70
-rw-r--r--crates/shirabe/src/command/exec_command.rs9
-rw-r--r--crates/shirabe/src/command/global_command.rs8
-rw-r--r--crates/shirabe/src/command/require_command.rs12
-rw-r--r--crates/shirabe/src/config/json_config_source.rs9
-rw-r--r--crates/shirabe/src/downloader/zip_downloader.rs6
-rw-r--r--crates/shirabe/src/json/json_file.rs8
-rw-r--r--crates/shirabe/src/package/archiver/phar_archiver.rs6
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs27
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs14
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs18
-rw-r--r--crates/shirabe/src/repository/path_repository.rs9
-rw-r--r--crates/shirabe/src/repository/vcs/github_driver.rs18
-rw-r--r--crates/shirabe/src/util/git.rs17
15 files changed, 170 insertions, 63 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/filesystem/exception/io_exception.rs b/crates/shirabe-external-packages/src/symfony/filesystem/exception/io_exception.rs
index ffe80ea1..07a189c3 100644
--- a/crates/shirabe-external-packages/src/symfony/filesystem/exception/io_exception.rs
+++ b/crates/shirabe-external-packages/src/symfony/filesystem/exception/io_exception.rs
@@ -12,7 +12,7 @@ impl IOException {
pub fn new(
message: String,
code: i64,
- previous: Option<std::sync::Arc<shirabe_php_shim::AnyThrowable>>,
+ previous: Option<std::sync::Arc<anyhow::Error>>,
path: Option<String>,
) -> Self {
Self {
diff --git a/crates/shirabe-php-shim/src/exception.rs b/crates/shirabe-php-shim/src/exception.rs
index 803d5b64..fd5e4a53 100644
--- a/crates/shirabe-php-shim/src/exception.rs
+++ b/crates/shirabe-php-shim/src/exception.rs
@@ -1,12 +1,16 @@
use crate::PhpClass;
-/// The fields a PHP `\Throwable` carries: its message and code, and the exception it wraps. Ported
+/// The fields a PHP `\Throwable` carries: its message and code, and the error it wraps. Ported
/// exception types embed this, either directly or through the parent exception they extend.
+///
+/// `previous` is any error rather than an [`AnyThrowable`], because the port reaches a `catch`
+/// carrying errors PHP would have raised as exception objects and the port raises as itself. One
+/// that does carry an exception is still reachable as such, through [`Catch`].
#[derive(Debug, Clone)]
pub struct ThrowableFields {
message: String,
code: i64,
- previous: Option<std::sync::Arc<AnyThrowable>>,
+ previous: Option<std::sync::Arc<anyhow::Error>>,
}
impl ThrowableFields {
@@ -23,7 +27,7 @@ impl ThrowableFields {
self.code = code;
}
- pub fn get_previous(&self) -> Option<&AnyThrowable> {
+ pub fn get_previous(&self) -> Option<&anyhow::Error> {
self.previous.as_deref()
}
}
@@ -97,6 +101,15 @@ impl AnyThrowable {
error.downcast_ref::<Self>()
}
+ /// PHP has no `setPrevious`: an exception gets its `previous` from its constructor. The one
+ /// exception is a `finally` throwing over an exception already on its way out — the one the
+ /// `finally` threw propagates, and the engine makes the one it displaced its `previous`.
+ pub fn set_previous(&mut self, previous: std::sync::Arc<anyhow::Error>) {
+ self.downcast_mut::<ThrowableFields>()
+ .expect("every exception bottoms out at the ThrowableFields")
+ .previous = Some(previous);
+ }
+
/// PHP's `catch (T $e)`: the exception seen as an instance of `T`, or `None` if it is not one.
/// A subclass answers through the instance of `T` it embeds, so `T`'s own state is reachable
/// the way PHP reaches an inherited property.
@@ -135,7 +148,7 @@ impl AnyThrowable {
self.0.fields().get_code()
}
- pub fn get_previous(&self) -> Option<&AnyThrowable> {
+ pub fn get_previous(&self) -> Option<&anyhow::Error> {
self.0.fields().get_previous()
}
}
@@ -149,7 +162,7 @@ impl std::fmt::Display for AnyThrowable {
impl std::error::Error for AnyThrowable {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.get_previous()
- .map(|previous| previous as &(dyn std::error::Error + 'static))
+ .map(|previous| &**previous as &(dyn std::error::Error + 'static))
}
}
@@ -277,7 +290,7 @@ macro_rules! impl_php_exception {
self.$field.set_code(code);
}
- pub fn get_previous(&self) -> Option<&$crate::AnyThrowable> {
+ pub fn get_previous(&self) -> Option<&::anyhow::Error> {
self.$field.get_previous()
}
}
@@ -306,7 +319,7 @@ macro_rules! define_php_exception {
pub fn with_code_and_previous(
message: String,
code: i64,
- previous: Option<std::sync::Arc<AnyThrowable>>,
+ previous: Option<std::sync::Arc<anyhow::Error>>,
) -> Self {
Self {
inner: ThrowableFields {
@@ -325,7 +338,7 @@ macro_rules! define_php_exception {
pub fn with_code_and_previous(
message: String,
code: i64,
- previous: Option<std::sync::Arc<AnyThrowable>>,
+ previous: Option<std::sync::Arc<anyhow::Error>>,
) -> Self {
Self {
inner: <$parent>::with_code_and_previous(message, code, previous),
@@ -399,7 +412,7 @@ impl ErrorException {
severity: i64,
filename: String,
lineno: i64,
- previous: Option<std::sync::Arc<AnyThrowable>>,
+ previous: Option<std::sync::Arc<anyhow::Error>>,
) -> Self {
Self {
inner: Exception::with_code_and_previous(message, code, previous),
@@ -518,8 +531,45 @@ mod tests {
}
#[test]
+ fn the_previous_is_any_error_a_catch_can_reach() {
+ let previous = std::sync::Arc::new(anyhow::Error::new(std::io::Error::other("io")));
+ let error: anyhow::Error =
+ Exception::with_code_and_previous("boom".to_string(), 0, Some(previous)).into();
+
+ assert_eq!(
+ error
+ .catch::<Exception>()
+ .and_then(|e| e.get_previous())
+ .map(ToString::to_string),
+ Some("io".to_string())
+ );
+ }
+
+ #[test]
+ fn set_previous_reaches_the_superclass_holding_it() {
+ let pending = std::sync::Arc::new(anyhow::Error::from(RuntimeException::new(
+ "first".to_string(),
+ )));
+ let mut error: anyhow::Error = Subclass::new(7).into();
+
+ error
+ .downcast_mut::<AnyThrowable>()
+ .unwrap()
+ .set_previous(pending);
+
+ assert_eq!(
+ error
+ .catch::<Subclass>()
+ .and_then(|e| e.get_previous())
+ .and_then(|previous| previous.catch::<RuntimeException>())
+ .map(|previous| previous.get_message().to_string()),
+ Some("first".to_string())
+ );
+ }
+
+ #[test]
fn the_previous_exception_is_the_error_source() {
- let previous = std::sync::Arc::new(AnyThrowable::new(RuntimeException::new(
+ let previous = std::sync::Arc::new(anyhow::Error::from(RuntimeException::new(
"cause".to_string(),
)));
let error: anyhow::Error =
diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs
index a67c234e..61391117 100644
--- a/crates/shirabe/src/command/exec_command.rs
+++ b/crates/shirabe/src/command/exec_command.rs
@@ -220,10 +220,11 @@ impl Command for ExecCommand {
&& getcwd().as_deref() != Some(iwd.as_str())
{
chdir(iwd).map_err(|e| {
- RuntimeException::new(format!(
- "Could not switch back to working directory \"{}\"",
- iwd
- ))
+ RuntimeException::with_code_and_previous(
+ format!("Could not switch back to working directory \"{}\"", iwd),
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
})?;
}
diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs
index 78b6f3e6..df9798a3 100644
--- a/crates/shirabe/src/command/global_command.rs
+++ b/crates/shirabe/src/command/global_command.rs
@@ -90,8 +90,12 @@ impl GlobalCommand {
}
}
- chdir(&home).map_err(|_e| {
- RuntimeException::new(format!("Could not switch to home directory \"{}\"", home))
+ chdir(&home).map_err(|e| {
+ RuntimeException::with_code_and_previous(
+ format!("Could not switch to home directory \"{}\"", home),
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
})?;
if !quiet {
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 84b4a6e4..88916175 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -977,10 +977,14 @@ impl Command for RequireCommand {
if self.newly_created.get() {
self.revert_composer_file();
- return Err(RuntimeException::new(format!(
- "No composer.json present in the current directory ({}), this may be the cause of the following exception.",
- self.file.borrow()
- ))
+ return Err(RuntimeException::with_code_and_previous(
+ format!(
+ "No composer.json present in the current directory ({}), this may be the cause of the following exception.",
+ self.file.borrow()
+ ),
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
.into());
}
diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs
index 0257ee31..59146630 100644
--- a/crates/shirabe/src/config/json_config_source.rs
+++ b/crates/shirabe/src/config/json_config_source.rs
@@ -148,11 +148,16 @@ impl JsonConfigSource {
};
// restore contents to the original state
file_put_contents(self.file.borrow().get_path(), contents.as_bytes());
- return Err(RuntimeException::new(format!(
+ let message = format!(
"Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json). {}{}",
PHP_EOL,
implode(PHP_EOL, jve.get_errors()),
- ))
+ );
+ return Err(RuntimeException::with_code_and_previous(
+ message,
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
.into());
}
}
diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs
index 84ed4c9e..c481f13f 100644
--- a/crates/shirabe/src/downloader/zip_downloader.rs
+++ b/crates/shirabe/src/downloader/zip_downloader.rs
@@ -361,11 +361,13 @@ impl ZipDownloader {
result.map_err(|e| {
if let Some(err) = e.catch::<ErrorException>() {
- RuntimeException::new(format!(
+ let message = format!(
"The archive for \"{}\" may contain identical file names with different capitalization (which fails on case insensitive filesystems): {}",
package.get_name(),
err.get_message(),
- )).into()
+ );
+ RuntimeException::with_code_and_previous(message, 0, Some(std::sync::Arc::new(e)))
+ .into()
} else {
e
}
diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs
index 2d061ced..da6370e4 100644
--- a/crates/shirabe/src/json/json_file.rs
+++ b/crates/shirabe/src/json/json_file.rs
@@ -174,7 +174,13 @@ impl JsonFile {
// TransportException keeps its message verbatim; any other exception is wrapped
// with the "Could not read" prefix.
if let Some(te) = e.catch::<TransportException>() {
- return Err(RuntimeException::new(te.get_message().to_string()).into());
+ let message = te.get_message().to_string();
+ return Err(RuntimeException::with_code_and_previous(
+ message,
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
+ .into());
}
return Err(RuntimeException::new(format!(
"Could not read {}\n\n{}",
diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs
index 0968f2a4..4a41d5a4 100644
--- a/crates/shirabe/src/package/archiver/phar_archiver.rs
+++ b/crates/shirabe/src/package/archiver/phar_archiver.rs
@@ -5,7 +5,7 @@ use crate::package::archiver::ArchivableFilesFinder;
use crate::package::archiver::ArchiverInterface;
use indexmap::IndexMap;
use shirabe_php_shim::{
- FilesystemIterator, Phar, PharData, RuntimeException, bzcompress, file_exists,
+ AnyThrowable, FilesystemIterator, Phar, PharData, RuntimeException, bzcompress, file_exists,
file_put_contents, function_exists, gzcompress, str_repeat, strrpos, unlink,
};
@@ -147,7 +147,9 @@ impl ArchiverInterface for PharArchiver {
"Could not create archive '{}' from '{}': {}",
target_outer, sources, e
);
- RuntimeException::new(message).into()
+ let code = AnyThrowable::of(e.as_ref()).map_or(0, AnyThrowable::get_code);
+ RuntimeException::with_code_and_previous(message, code, Some(std::sync::Arc::new(e)))
+ .into()
})
}
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 2c6d3971..106f094e 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -19,8 +19,9 @@ use chrono::Utc;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::Preg;
use shirabe_php_shim::{
- E_USER_DEPRECATED, PhpMixed, UnexpectedValueException, is_scalar, is_string, json_encode,
- ltrim, php_regex, stripos, strpos, strtolower, strval, substr, trigger_error, trim,
+ AnyThrowable, E_USER_DEPRECATED, PhpMixed, UnexpectedValueException, is_scalar, is_string,
+ json_encode, ltrim, php_regex, stripos, strpos, strtolower, strval, substr, trigger_error,
+ trim,
};
#[derive(Debug)]
@@ -112,11 +113,17 @@ impl ArrayLoader {
{
Ok(v) => version = v,
Err(e) => {
- return Err(UnexpectedValueException::new(format!(
+ let message = format!(
"Failed to normalize version for package \"{}\": {}",
config.get("name").and_then(|v| v.as_string()).unwrap_or(""),
e
- ))
+ );
+ let code = AnyThrowable::of(e.as_ref()).map_or(0, AnyThrowable::get_code);
+ return Err(UnexpectedValueException::with_code_and_previous(
+ message,
+ code,
+ Some(std::sync::Arc::new(e)),
+ )
.into());
}
}
@@ -654,11 +661,17 @@ impl ArrayLoader {
let parsed_constraint = match self.version_parser.parse_constraints(&constraint) {
Ok(c) => c,
- Err(_e) => {
- return Err(UnexpectedValueException::new(format!(
+ Err(e) => {
+ let message = format!(
"Link constraint in {} {} > {} should be a valid version constraint, got \"{}\"",
source, description, target, constraint
- ))
+ );
+ let code = AnyThrowable::of(e.as_ref()).map_or(0, AnyThrowable::get_code);
+ return Err(UnexpectedValueException::with_code_and_previous(
+ message,
+ code,
+ Some(std::sync::Arc::new(e)),
+ )
.into());
}
};
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index 2d16f137..75ad134b 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -224,11 +224,15 @@ impl ArtifactRepository {
.unwrap_or_default();
match self.loader.load(cfg, None) {
Ok(package) => Ok(Some(package)),
- Err(exception) => Err(UnexpectedValueException::new(format!(
- "Failed loading package in {}: {}",
- pathname, exception
- ))
- .into()),
+ Err(exception) => {
+ let message = format!("Failed loading package in {}: {}", pathname, exception);
+ Err(UnexpectedValueException::with_code_and_previous(
+ message,
+ 0,
+ Some(std::sync::Arc::new(exception)),
+ )
+ .into())
+ }
}
}
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 5371c2c3..b53374aa 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -40,9 +40,9 @@ 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,
- php_regex, realpath, strtolower, strtr, urlencode, var_export,
+ AnyThrowable, CmpOp, InvalidArgumentException, LogicException, PHP_EOL, PhpMixed,
+ RuntimeException, UnexpectedValueException, extension_loaded, hash, http_build_query,
+ json_decode, parse_url_all, php_regex, realpath, strtolower, strtr, urlencode, var_export,
};
use shirabe_semver::CompilingMatcher;
use shirabe_semver::constraint::AnyConstraint;
@@ -2695,17 +2695,21 @@ impl ComposerRepository {
})();
result.map_err(|e| {
- RuntimeException::new(format!(
+ let message = format!(
"Could not load packages in {}{}: [{}] {}",
self.get_repo_name(),
source
.as_ref()
.map(|s| format!(" from {}", s))
.unwrap_or_default(),
- "Exception",
+ AnyThrowable::of(e.as_ref()).map_or_else(
+ || "Exception".to_string(),
+ shirabe_php_shim::PhpClass::php_class_name,
+ ),
e
- ))
- .into()
+ );
+ RuntimeException::with_code_and_previous(message, 0, Some(std::sync::Arc::new(e)))
+ .into()
})
}
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index 313fc75e..46b7f95f 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -350,10 +350,11 @@ impl PathRepository {
self.inner
.add_package(self.loader.load(package.clone(), None).map_err(|e| {
- RuntimeException::new(format!(
- "Failed loading the package in {}",
- composer_file_path
- ))
+ RuntimeException::with_code_and_previous(
+ format!("Failed loading the package in {}", composer_file_path),
+ 0,
+ Some(std::sync::Arc::new(e)),
+ )
})?);
}
diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs
index 72bcc8ae..ffdf04ec 100644
--- a/crates/shirabe/src/repository/vcs/github_driver.rs
+++ b/crates/shirabe/src/repository/vcs/github_driver.rs
@@ -1038,7 +1038,7 @@ impl GitHubDriver {
}
if !self.inner.io.is_interactive() {
- self.attempt_clone_fallback(Some(&e))
+ self.attempt_clone_fallback(Some(std::sync::Arc::new((*e).into())))
.map_err(|err| TransportException::new(err.to_string(), 0))?;
return Ok(Response::new(
@@ -1090,7 +1090,7 @@ impl GitHubDriver {
}
if !self.inner.io.is_interactive() && fetching_repo_data {
- self.attempt_clone_fallback(Some(&e))
+ self.attempt_clone_fallback(Some(std::sync::Arc::new((*e).into())))
.map_err(|err| TransportException::new(err.to_string(), 0))?;
return Ok(Response::new(
@@ -1177,7 +1177,7 @@ impl GitHubDriver {
}
Err(e) => {
if e.get_code() == 499 {
- self.attempt_clone_fallback(Some(&e))?;
+ self.attempt_clone_fallback(Some(std::sync::Arc::new((*e).into())))?;
} else {
return Err((*e).into());
}
@@ -1227,14 +1227,14 @@ impl GitHubDriver {
/// @throws \RuntimeException
pub(crate) fn attempt_clone_fallback(
&mut self,
- e: Option<&TransportException>,
+ e: Option<std::sync::Arc<anyhow::Error>>,
) -> anyhow::Result<bool> {
if !self.allow_git_fallback {
- return Err(RuntimeException::new(format!(
- "Fallback to git driver disabled{}",
- e.map(|e| format!(": {}", e.get_message()))
- .unwrap_or_default()
- ))
+ return Err(RuntimeException::with_code_and_previous(
+ "Fallback to git driver disabled".to_string(),
+ 0,
+ e,
+ )
.into());
}
diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs
index 3eec0947..d7bd3685 100644
--- a/crates/shirabe/src/util/git.rs
+++ b/crates/shirabe/src/util/git.rs
@@ -16,7 +16,7 @@ use crate::util::{AuthHelper, StoreAuth};
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_php_shim::{
- CmpOp, InvalidArgumentException, PHP_EOL, PhpMixed, RuntimeException, array_map,
+ AnyThrowable, CmpOp, InvalidArgumentException, PHP_EOL, PhpMixed, RuntimeException, array_map,
clearstatcache, explode, implode, in_array_loose, in_array_strict, is_dir, php_regex,
preg_quote, rawurldecode, rawurlencode, str_contains, str_ends_with, str_replace_array, strlen,
strpos, substr, trim, version_compare,
@@ -839,7 +839,7 @@ impl Git {
Ok(())
})();
// finally
- let _ = self.run_commands(
+ let finally_result = self.run_commands(
vec![vec![
"git".to_string(),
"remote".to_string(),
@@ -853,8 +853,19 @@ impl Git {
false,
(),
);
+ let outcome = match finally_result {
+ Ok(()) => try_result,
+ Err(mut thrown) => {
+ if let Some(pending) = try_result.err().map(std::sync::Arc::new)
+ && let Some(exception) = thrown.downcast_mut::<AnyThrowable>()
+ {
+ exception.set_previous(pending);
+ }
+ Err(thrown)
+ }
+ };
- if let Err(e) = try_result {
+ if let Err(e) = outcome {
self.io.write_error3(
&format!("<error>Sync mirror failed: {}</error>", e),
true,