Skip to content

Commit 27f91df

Browse files
committed
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.
1 parent 75ff11f commit 27f91df

6 files changed

Lines changed: 84 additions & 28 deletions

File tree

src/error.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,11 @@ impl Error {
345345
/// Wraps an external error object.
346346
#[inline]
347347
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
348-
Error::ExternalError(err.into().into())
348+
let boxed = err.into();
349+
match boxed.downcast::<Self>() {
350+
Ok(err) => *err,
351+
Err(boxed) => Error::ExternalError(boxed.into()),
352+
}
349353
}
350354

351355
/// Attempts to downcast the external error object to a concrete type by reference.

src/function.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@
8181
8282
use std::cell::RefCell;
8383
use std::os::raw::{c_int, c_void};
84+
use std::result::Result as StdResult;
8485
use std::{mem, ptr, slice};
8586

86-
use crate::error::{Error, Result};
87+
use crate::error::{Error, ExternalError, ExternalResult, Result};
8788
use crate::state::Lua;
8889
use crate::table::Table;
8990
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
@@ -635,30 +636,32 @@ impl Function {
635636
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
636637
/// trait.
637638
#[inline]
638-
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
639+
pub fn wrap<F, A, R, E>(func: F) -> impl IntoLua
639640
where
640-
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
641+
F: LuaNativeFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
641642
A: FromLuaMulti,
642643
R: IntoLuaMulti,
644+
E: ExternalError,
643645
{
644646
WrappedFunction(Box::new(move |lua, nargs| unsafe {
645647
let args = A::from_stack_args(nargs, 1, None, lua)?;
646-
func.call(args)?.push_into_stack_multi(lua)
648+
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
647649
}))
648650
}
649651

650652
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
651-
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
653+
pub fn wrap_mut<F, A, R, E>(func: F) -> impl IntoLua
652654
where
653-
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
655+
F: LuaNativeFnMut<A, Output = StdResult<R, E>> + MaybeSend + 'static,
654656
A: FromLuaMulti,
655657
R: IntoLuaMulti,
658+
E: ExternalError,
656659
{
657660
let func = RefCell::new(func);
658661
WrappedFunction(Box::new(move |lua, nargs| unsafe {
659662
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
660663
let args = A::from_stack_args(nargs, 1, None, lua)?;
661-
func.call(args)?.push_into_stack_multi(lua)
664+
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
662665
}))
663666
}
664667

@@ -671,6 +674,7 @@ impl Function {
671674
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
672675
where
673676
F: LuaNativeFn<A> + MaybeSend + 'static,
677+
F::Output: IntoLuaMulti,
674678
A: FromLuaMulti,
675679
{
676680
WrappedFunction(Box::new(move |lua, nargs| unsafe {
@@ -687,6 +691,7 @@ impl Function {
687691
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
688692
where
689693
F: LuaNativeFnMut<A> + MaybeSend + 'static,
694+
F::Output: IntoLuaMulti,
690695
A: FromLuaMulti,
691696
{
692697
let func = RefCell::new(func);
@@ -701,11 +706,12 @@ impl Function {
701706
/// trait.
702707
#[cfg(feature = "async")]
703708
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
704-
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
709+
pub fn wrap_async<F, A, R, E>(func: F) -> impl IntoLua
705710
where
706-
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
711+
F: LuaNativeAsyncFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
707712
A: FromLuaMulti,
708713
R: IntoLuaMulti,
714+
E: ExternalError,
709715
{
710716
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
711717
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
@@ -714,7 +720,7 @@ impl Function {
714720
};
715721
let lua = rawlua.lua();
716722
let fut = func.call(args);
717-
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
723+
Box::pin(async move { fut.await.into_lua_err()?.push_into_stack_multi(lua.raw_lua()) })
718724
}))
719725
}
720726

@@ -728,6 +734,7 @@ impl Function {
728734
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
729735
where
730736
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
737+
F::Output: IntoLuaMulti,
731738
A: FromLuaMulti,
732739
{
733740
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
@@ -789,22 +796,22 @@ impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
789796

790797
/// A trait for types that can be used as Lua functions.
791798
pub trait LuaNativeFn<A: FromLuaMulti> {
792-
type Output: IntoLuaMulti;
799+
type Output;
793800

794801
fn call(&self, args: A) -> Self::Output;
795802
}
796803

797804
/// A trait for types with mutable state that can be used as Lua functions.
798805
pub trait LuaNativeFnMut<A: FromLuaMulti> {
799-
type Output: IntoLuaMulti;
806+
type Output;
800807

801808
fn call(&mut self, args: A) -> Self::Output;
802809
}
803810

804811
/// A trait for types that returns a future and can be used as Lua functions.
805812
#[cfg(feature = "async")]
806813
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
807-
type Output: IntoLuaMulti;
814+
type Output;
808815

809816
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
810817
}
@@ -815,7 +822,6 @@ macro_rules! impl_lua_native_fn {
815822
where
816823
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
817824
($($A,)*): FromLuaMulti,
818-
R: IntoLuaMulti,
819825
{
820826
type Output = R;
821827

@@ -830,7 +836,6 @@ macro_rules! impl_lua_native_fn {
830836
where
831837
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
832838
($($A,)*): FromLuaMulti,
833-
R: IntoLuaMulti,
834839
{
835840
type Output = R;
836841

@@ -847,7 +852,6 @@ macro_rules! impl_lua_native_fn {
847852
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
848853
($($A,)*): FromLuaMulti,
849854
Fut: Future<Output = R> + MaybeSend + 'static,
850-
R: IntoLuaMulti,
851855
{
852856
type Output = R;
853857

tests/async.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ async fn test_async_function_wrap() -> Result<()> {
4141

4242
let f = Function::wrap_async(|s: String| async move {
4343
tokio::task::yield_now().await;
44-
Ok(s)
44+
Ok::<_, Error>(s)
4545
});
4646
lua.globals().set("f", f)?;
4747
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;

tests/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ fn test_error_chain() -> Result<()> {
7777
Ok(())
7878
}
7979

80+
#[test]
81+
fn test_external_error() {
82+
// `Error::external` should preserve `mlua::Error`
83+
let runtime_err = Error::runtime("test error");
84+
let converted = Error::external(runtime_err);
85+
assert!(matches!(converted, Error::RuntimeError(ref msg) if msg == "test error"));
86+
87+
// Other errors should become `ExternalError`
88+
let converted = Error::external(io::Error::other("other error"));
89+
assert!(matches!(converted, Error::ExternalError(_)));
90+
assert!(converted.downcast_ref::<io::Error>().is_some());
91+
}
92+
8093
#[cfg(feature = "anyhow")]
8194
#[test]
8295
fn test_error_anyhow() -> Result<()> {

tests/function.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::fmt;
2+
use std::result::Result as StdResult;
3+
14
use mlua::{Error, Function, Lua, LuaString, Result, Table, Variadic};
25

36
#[test]
@@ -343,7 +346,7 @@ fn test_function_deep_clone() -> Result<()> {
343346
fn test_function_wrap() -> Result<()> {
344347
let lua = Lua::new();
345348

346-
let f = Function::wrap(|s: LuaString, n| Ok(s.to_str().unwrap().repeat(n)));
349+
let f = Function::wrap(|s: LuaString, n| Ok::<_, Error>(s.to_str().unwrap().repeat(n)));
347350
lua.globals().set("f", f)?;
348351
lua.load(r#"assert(f("hello", 2) == "hellohello")"#)
349352
.exec()
@@ -361,11 +364,40 @@ fn test_function_wrap() -> Result<()> {
361364
.exec()
362365
.unwrap();
363366

367+
// Return external error
368+
#[derive(Debug)]
369+
struct MyError(String);
370+
impl fmt::Display for MyError {
371+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372+
write!(f, "MyError: {}", self.0)
373+
}
374+
}
375+
impl std::error::Error for MyError {}
376+
377+
let fext = Function::wrap(|s: String| -> StdResult<String, MyError> {
378+
if s == "bad" {
379+
return Err(MyError("bad input".into()));
380+
}
381+
Ok(format!("ok: {s}"))
382+
});
383+
lua.globals().set("fext", fext)?;
384+
lua.load(r#"assert(fext("hello") == "ok: hello")"#)
385+
.exec()
386+
.unwrap();
387+
lua.load(
388+
r#"
389+
local ok, err = pcall(fext, "bad")
390+
assert(not ok and tostring(err):find("MyError: bad input"))
391+
"#,
392+
)
393+
.exec()
394+
.unwrap();
395+
364396
// Mutable callback
365397
let mut i = 0;
366398
let fmut = Function::wrap_mut(move || {
367399
i += 1;
368-
Ok(i)
400+
Ok::<_, Error>(i)
369401
});
370402
lua.globals().set("fmut", fmut)?;
371403
lua.load(r#"fmut(); fmut(); assert(fmut() == 3)"#).exec().unwrap();
@@ -385,7 +417,7 @@ fn test_function_wrap() -> Result<()> {
385417
// Check recursive mut callback error
386418
let fmut = Function::wrap_mut(|f: Function| match f.call::<()>(&f) {
387419
Err(Error::CallbackError { cause, .. }) => match cause.as_ref() {
388-
Error::RecursiveMutCallback { .. } => Ok(()),
420+
Error::RecursiveMutCallback { .. } => Ok::<_, Error>(()),
389421
other => panic!("incorrect result: {other:?}"),
390422
},
391423
other => panic!("incorrect result: {other:?}"),

tests/types.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::os::raw::c_void;
22

3-
use mlua::{Function, LightUserData, Lua, LuaString, Number, Result, Thread};
3+
use mlua::{Error, Function, LightUserData, Lua, LuaString, Number, Result, Thread};
44

55
#[test]
66
fn test_lightuserdata() -> Result<()> {
@@ -30,7 +30,7 @@ fn test_boolean_type_metatable() -> Result<()> {
3030
let lua = Lua::new();
3131

3232
let mt = lua.create_table()?;
33-
mt.set("__add", Function::wrap(|a, b| Ok(a || b)))?;
33+
mt.set("__add", Function::wrap(|a, b| Ok::<_, mlua::Error>(a || b)))?;
3434
assert_eq!(lua.type_metatable::<bool>(), None);
3535
lua.set_type_metatable::<bool>(Some(mt.clone()));
3636
assert_eq!(lua.type_metatable::<bool>().unwrap(), mt);
@@ -51,7 +51,7 @@ fn test_lightuserdata_type_metatable() -> Result<()> {
5151
mt.set(
5252
"__add",
5353
Function::wrap(|a: LightUserData, b: LightUserData| {
54-
Ok(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
54+
Ok::<_, Error>(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
5555
}),
5656
)?;
5757
lua.set_type_metatable::<LightUserData>(Some(mt.clone()));
@@ -79,7 +79,10 @@ fn test_number_type_metatable() -> Result<()> {
7979
let lua = Lua::new();
8080

8181
let mt = lua.create_table()?;
82-
mt.set("__call", Function::wrap(|n1: f64, n2: f64| Ok(n1 * n2)))?;
82+
mt.set(
83+
"__call",
84+
Function::wrap(|n1: f64, n2: f64| Ok::<_, Error>(n1 * n2)),
85+
)?;
8386
lua.set_type_metatable::<Number>(Some(mt.clone()));
8487
assert_eq!(lua.type_metatable::<Number>().unwrap(), mt);
8588

@@ -96,7 +99,7 @@ fn test_string_type_metatable() -> Result<()> {
9699
let mt = lua.create_table()?;
97100
mt.set(
98101
"__add",
99-
Function::wrap(|a: String, b: String| Ok(format!("{a}{b}"))),
102+
Function::wrap(|a: String, b: String| Ok::<_, Error>(format!("{a}{b}"))),
100103
)?;
101104
lua.set_type_metatable::<LuaString>(Some(mt.clone()));
102105
assert_eq!(lua.type_metatable::<LuaString>().unwrap(), mt);
@@ -113,7 +116,7 @@ fn test_function_type_metatable() -> Result<()> {
113116
let mt = lua.create_table()?;
114117
mt.set(
115118
"__index",
116-
Function::wrap(|_: Function, key: String| Ok(format!("function.{key}"))),
119+
Function::wrap(|_: Function, key: String| Ok::<_, Error>(format!("function.{key}"))),
117120
)?;
118121
lua.set_type_metatable::<Function>(Some(mt.clone()));
119122
assert_eq!(lua.type_metatable::<Function>(), Some(mt));
@@ -132,7 +135,7 @@ fn test_thread_type_metatable() -> Result<()> {
132135
let mt = lua.create_table()?;
133136
mt.set(
134137
"__index",
135-
Function::wrap(|_: Thread, key: String| Ok(format!("thread.{key}"))),
138+
Function::wrap(|_: Thread, key: String| Ok::<_, Error>(format!("thread.{key}"))),
136139
)?;
137140
lua.set_type_metatable::<Thread>(Some(mt.clone()));
138141
assert_eq!(lua.type_metatable::<Thread>(), Some(mt));

0 commit comments

Comments
 (0)