mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Add Error::chain method to return iterator over nested errors
This commit is contained in:
@@ -365,6 +365,14 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the chain of nested errors wrapped by this Error.
|
||||
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
|
||||
Chain {
|
||||
root: self,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
|
||||
Error::BadArgument {
|
||||
to: Some(to.to_string()),
|
||||
@@ -487,3 +495,44 @@ impl serde::de::Error for Error {
|
||||
Self::DeserializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
struct Chain<'a> {
|
||||
root: &'a Error,
|
||||
current: Option<&'a (dyn StdError + 'static)>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chain<'a> {
|
||||
type Item = &'a (dyn StdError + 'static);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let error: Option<&dyn StdError> = match self.current {
|
||||
None => {
|
||||
self.current = Some(self.root);
|
||||
self.current
|
||||
}
|
||||
Some(current) => match current.downcast_ref::<Error>()? {
|
||||
Error::BadArgument { cause, .. }
|
||||
| Error::CallbackError { cause, .. }
|
||||
| Error::WithContext { cause, .. } => {
|
||||
self.current = Some(&**cause);
|
||||
self.current
|
||||
}
|
||||
Error::ExternalError(err) => {
|
||||
self.current = Some(&**err);
|
||||
self.current
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
// Skip `ExternalError` as it only wraps the underlying error
|
||||
// without meaningful context
|
||||
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return self.current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,3 +46,29 @@ fn test_error_context() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_chain() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Check that `Error::ExternalError` creates a chain with a single element
|
||||
let io_err = io::Error::new(io::ErrorKind::Other, "other");
|
||||
assert_eq!(Error::external(io_err).chain().count(), 1);
|
||||
|
||||
let func = lua.create_function(|_, ()| {
|
||||
let err = Error::external(io::Error::new(io::ErrorKind::Other, "other")).context("io error");
|
||||
Err::<(), _>(err)
|
||||
})?;
|
||||
let err = func.call::<()>(()).err().unwrap();
|
||||
assert_eq!(err.chain().count(), 3);
|
||||
for (i, err) in err.chain().enumerate() {
|
||||
match i {
|
||||
0 => assert!(matches!(err.downcast_ref(), Some(Error::CallbackError { .. }))),
|
||||
1 => assert!(matches!(err.downcast_ref(), Some(Error::WithContext { .. }))),
|
||||
2 => assert!(matches!(err.downcast_ref(), Some(io::Error { .. }))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user