diff --git a/src/error.rs b/src/error.rs index de22dc7..7e74d69 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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), + /// An error with additional context. + WithContext { + context: StdString, + cause: Arc, + }, } /// 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`. +pub trait ErrorContext: Sealed { + fn context(self, context: C) -> Self; + fn with_context(self, f: impl FnOnce(&Error) -> C) -> Self; +} + +impl ErrorContext for Error { + fn context(self, context: C) -> Self { + Error::WithContext { + context: context.to_string(), + cause: Arc::new(self), + } + } + + fn with_context(self, f: impl FnOnce(&Error) -> C) -> Self { + Error::WithContext { + context: f(&self).to_string(), + cause: Arc::new(self), + } + } +} + +impl ErrorContext for StdResult { + fn context(self, context: C) -> Self { + self.map_err(|err| Error::WithContext { + context: context.to_string(), + cause: Arc::new(err), + }) + } + + fn with_context(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 for Error { fn from(err: AddrParseError) -> Self { Error::external(err) diff --git a/src/lib.rs b/src/lib.rs index 85aa5cd..f3a12e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 Sealed for std::result::Result {} +} diff --git a/src/prelude.rs b/src/prelude.rs index 20ca6b8..32eb440 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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,