- Allow downcasting error wrapped into `Error::WithContext`

- Overwrite error context when called multiple times
This commit is contained in:
Alex Orlenko 2023-05-08 23:39:53 +01:00
parent bbd2fe06e1
commit 1ac98e7d16
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
2 changed files with 41 additions and 14 deletions

View File

@ -335,6 +335,10 @@ impl StdError for Error {
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(ref err) => err.source(),
Error::WithContext { ref cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.source(),
_ => None,
},
_ => None,
}
}
@ -353,6 +357,10 @@ impl Error {
{
match self {
Error::ExternalError(err) => err.downcast_ref(),
Error::WithContext { cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.downcast_ref(),
_ => None,
},
_ => None,
}
}
@ -414,33 +422,35 @@ pub trait ErrorContext: Sealed {
impl ErrorContext for Error {
fn context<C: fmt::Display>(self, context: C) -> Self {
Error::WithContext {
context: context.to_string(),
cause: Arc::new(self),
let context = context.to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
Error::WithContext {
context: f(&self).to_string(),
cause: Arc::new(self),
let context = f(&self).to_string();
match self {
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
_ => Error::WithContext {
context,
cause: Arc::new(self),
},
}
}
}
impl<T> ErrorContext for StdResult<T, Error> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| Error::WithContext {
context: context.to_string(),
cause: Arc::new(err),
})
self.map_err(|err| err.context(context))
}
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
self.map_err(|err| Error::WithContext {
context: f(&err).to_string(),
cause: Arc::new(err),
})
self.map_err(|err| err.with_context(f))
}
}

View File

@ -1,3 +1,5 @@
use std::io;
use mlua::{Error, ErrorContext, Lua, Result};
#[test]
@ -29,5 +31,20 @@ fn test_error_context() -> Result<()> {
println!("{msg2}");
assert!(msg2.contains("error converting Lua nil to String"));
// Rewrite context message and test `downcast_ref`
let func3 = lua.create_function(|_, ()| {
Err::<(), _>(Error::external(io::Error::new(
io::ErrorKind::Other,
"other",
)))
.context("some context")
.context("some new context")
})?;
let res = func3.call::<_, ()>(()).err().unwrap();
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
assert!(!res.to_string().contains("some context"));
assert!(res.to_string().contains("some new context"));
assert!(cause.downcast_ref::<io::Error>().is_some());
Ok(())
}