Add `ErrorContext` extension trait to attach additional context to `Error`

This commit is contained in:
Alex Orlenko 2023-03-17 01:20:57 +00:00
parent a0d37fd182
commit 8d80321738
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
3 changed files with 63 additions and 5 deletions

View File

@ -7,6 +7,8 @@ use std::str::Utf8Error;
use std::string::String as StdString;
use std::sync::Arc;
use crate::private::Sealed;
/// Error type returned by `mlua` methods.
#[derive(Debug, Clone)]
#[non_exhaustive]
@ -190,6 +192,11 @@ pub enum Error {
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
/// from which the original error (and a stack traceback) can be recovered.
ExternalError(Arc<dyn StdError + Send + Sync>),
/// An error with additional context.
WithContext {
context: StdString,
cause: Arc<Error>,
},
}
/// A specialized `Result` type used by `mlua`'s API.
@ -306,6 +313,10 @@ impl fmt::Display for Error {
write!(fmt, "deserialize error: {err}")
},
Error::ExternalError(ref err) => write!(fmt, "{err}"),
Error::WithContext { ref context, ref cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
}
}
}
@ -374,6 +385,44 @@ where
}
}
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
pub trait ErrorContext: Sealed {
fn context<C: fmt::Display>(self, context: C) -> Self;
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
}
impl ErrorContext for Error {
fn context<C: fmt::Display>(self, context: C) -> Self {
Error::WithContext {
context: context.to_string(),
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),
}
}
}
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),
})
}
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),
})
}
}
impl std::convert::From<AddrParseError> for Error {
fn from(err: AddrParseError) -> Self {
Error::external(err)

View File

@ -108,7 +108,7 @@ pub mod prelude;
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{GCMode, Lua, LuaOptions};
@ -232,3 +232,12 @@ pub use mlua_derive::chunk;
#[cfg(any(feature = "module", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
pub use mlua_derive::lua_module;
pub(crate) mod private {
use super::*;
pub trait Sealed {}
impl Sealed for Error {}
impl<T> Sealed for std::result::Result<T, Error> {}
}

View File

@ -3,10 +3,10 @@
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk,
Error as LuaError, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo,
GCMode as LuaGCMode, Integer as LuaInteger, IntoLua, IntoLuaMulti,
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
Error as LuaError, ErrorContext as LuaErrorContext, ExternalError as LuaExternalError,
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction,
FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua,
IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,