mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Accept any error in Function::wrap/wrap_mut/wrap_async
Previously wrapped functions were required to return `mlua::Result`. Now it's possible to wrap functions returning any errors as long as they implement `std::error::Error`. Existing code remains compatible with `mlua::Result` as this type is not converted to an external error.
This commit is contained in:
+5
-1
@@ -345,7 +345,11 @@ impl Error {
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
let boxed = err.into();
|
||||
match boxed.downcast::<Self>() {
|
||||
Ok(err) => *err,
|
||||
Err(boxed) => Error::ExternalError(boxed.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to downcast the external error object to a concrete type by reference.
|
||||
|
||||
+20
-16
@@ -81,9 +81,10 @@
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
@@ -635,30 +636,32 @@ impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_mut<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFnMut<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -671,6 +674,7 @@ impl Function {
|
||||
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -687,6 +691,7 @@ impl Function {
|
||||
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
@@ -701,11 +706,12 @@ impl Function {
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_async<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeAsyncFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
@@ -714,7 +720,7 @@ impl Function {
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
Box::pin(async move { fut.await.into_lua_err()?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -728,6 +734,7 @@ impl Function {
|
||||
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
@@ -789,14 +796,14 @@ impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
type Output;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
@@ -804,7 +811,7 @@ pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
@@ -815,7 +822,6 @@ macro_rules! impl_lua_native_fn {
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
@@ -830,7 +836,6 @@ macro_rules! impl_lua_native_fn {
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
@@ -847,7 +852,6 @@ macro_rules! impl_lua_native_fn {
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
|
||||
let f = Function::wrap_async(|s: String| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Ok(s)
|
||||
Ok::<_, Error>(s)
|
||||
});
|
||||
lua.globals().set("f", f)?;
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
|
||||
@@ -77,6 +77,19 @@ fn test_error_chain() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_external_error() {
|
||||
// `Error::external` should preserve `mlua::Error`
|
||||
let runtime_err = Error::runtime("test error");
|
||||
let converted = Error::external(runtime_err);
|
||||
assert!(matches!(converted, Error::RuntimeError(ref msg) if msg == "test error"));
|
||||
|
||||
// Other errors should become `ExternalError`
|
||||
let converted = Error::external(io::Error::other("other error"));
|
||||
assert!(matches!(converted, Error::ExternalError(_)));
|
||||
assert!(converted.downcast_ref::<io::Error>().is_some());
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
#[test]
|
||||
fn test_error_anyhow() -> Result<()> {
|
||||
|
||||
+35
-3
@@ -1,3 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, Function, Lua, LuaString, Result, Table, Variadic};
|
||||
|
||||
#[test]
|
||||
@@ -343,7 +346,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap(|s: LuaString, n| Ok(s.to_str().unwrap().repeat(n)));
|
||||
let f = Function::wrap(|s: LuaString, n| Ok::<_, Error>(s.to_str().unwrap().repeat(n)));
|
||||
lua.globals().set("f", f)?;
|
||||
lua.load(r#"assert(f("hello", 2) == "hellohello")"#)
|
||||
.exec()
|
||||
@@ -361,11 +364,40 @@ fn test_function_wrap() -> Result<()> {
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Return external error
|
||||
#[derive(Debug)]
|
||||
struct MyError(String);
|
||||
impl fmt::Display for MyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MyError: {}", self.0)
|
||||
}
|
||||
}
|
||||
impl std::error::Error for MyError {}
|
||||
|
||||
let fext = Function::wrap(|s: String| -> StdResult<String, MyError> {
|
||||
if s == "bad" {
|
||||
return Err(MyError("bad input".into()));
|
||||
}
|
||||
Ok(format!("ok: {s}"))
|
||||
});
|
||||
lua.globals().set("fext", fext)?;
|
||||
lua.load(r#"assert(fext("hello") == "ok: hello")"#)
|
||||
.exec()
|
||||
.unwrap();
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(fext, "bad")
|
||||
assert(not ok and tostring(err):find("MyError: bad input"))
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Mutable callback
|
||||
let mut i = 0;
|
||||
let fmut = Function::wrap_mut(move || {
|
||||
i += 1;
|
||||
Ok(i)
|
||||
Ok::<_, Error>(i)
|
||||
});
|
||||
lua.globals().set("fmut", fmut)?;
|
||||
lua.load(r#"fmut(); fmut(); assert(fmut() == 3)"#).exec().unwrap();
|
||||
@@ -385,7 +417,7 @@ fn test_function_wrap() -> Result<()> {
|
||||
// Check recursive mut callback error
|
||||
let fmut = Function::wrap_mut(|f: Function| match f.call::<()>(&f) {
|
||||
Err(Error::CallbackError { cause, .. }) => match cause.as_ref() {
|
||||
Error::RecursiveMutCallback { .. } => Ok(()),
|
||||
Error::RecursiveMutCallback { .. } => Ok::<_, Error>(()),
|
||||
other => panic!("incorrect result: {other:?}"),
|
||||
},
|
||||
other => panic!("incorrect result: {other:?}"),
|
||||
|
||||
+10
-7
@@ -1,6 +1,6 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use mlua::{Function, LightUserData, Lua, LuaString, Number, Result, Thread};
|
||||
use mlua::{Error, Function, LightUserData, Lua, LuaString, Number, Result, Thread};
|
||||
|
||||
#[test]
|
||||
fn test_lightuserdata() -> Result<()> {
|
||||
@@ -30,7 +30,7 @@ fn test_boolean_type_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__add", Function::wrap(|a, b| Ok(a || b)))?;
|
||||
mt.set("__add", Function::wrap(|a, b| Ok::<_, mlua::Error>(a || b)))?;
|
||||
assert_eq!(lua.type_metatable::<bool>(), None);
|
||||
lua.set_type_metatable::<bool>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<bool>().unwrap(), mt);
|
||||
@@ -51,7 +51,7 @@ fn test_lightuserdata_type_metatable() -> Result<()> {
|
||||
mt.set(
|
||||
"__add",
|
||||
Function::wrap(|a: LightUserData, b: LightUserData| {
|
||||
Ok(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
|
||||
Ok::<_, Error>(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
|
||||
}),
|
||||
)?;
|
||||
lua.set_type_metatable::<LightUserData>(Some(mt.clone()));
|
||||
@@ -79,7 +79,10 @@ fn test_number_type_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__call", Function::wrap(|n1: f64, n2: f64| Ok(n1 * n2)))?;
|
||||
mt.set(
|
||||
"__call",
|
||||
Function::wrap(|n1: f64, n2: f64| Ok::<_, Error>(n1 * n2)),
|
||||
)?;
|
||||
lua.set_type_metatable::<Number>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Number>().unwrap(), mt);
|
||||
|
||||
@@ -96,7 +99,7 @@ fn test_string_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__add",
|
||||
Function::wrap(|a: String, b: String| Ok(format!("{a}{b}"))),
|
||||
Function::wrap(|a: String, b: String| Ok::<_, Error>(format!("{a}{b}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<LuaString>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<LuaString>().unwrap(), mt);
|
||||
@@ -113,7 +116,7 @@ fn test_function_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__index",
|
||||
Function::wrap(|_: Function, key: String| Ok(format!("function.{key}"))),
|
||||
Function::wrap(|_: Function, key: String| Ok::<_, Error>(format!("function.{key}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<Function>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Function>(), Some(mt));
|
||||
@@ -132,7 +135,7 @@ fn test_thread_type_metatable() -> Result<()> {
|
||||
let mt = lua.create_table()?;
|
||||
mt.set(
|
||||
"__index",
|
||||
Function::wrap(|_: Thread, key: String| Ok(format!("thread.{key}"))),
|
||||
Function::wrap(|_: Thread, key: String| Ok::<_, Error>(format!("thread.{key}"))),
|
||||
)?;
|
||||
lua.set_type_metatable::<Thread>(Some(mt.clone()));
|
||||
assert_eq!(lua.type_metatable::<Thread>(), Some(mt));
|
||||
|
||||
Reference in New Issue
Block a user