Change LuaError ToLua approach
Lua should be consistent independent of the way they are produced. pcall in lua should produce the same sort of error that would be returned by a LuaError conversion. The situation is not currently great, because LuaError is not Clone, so passing a LuaError back into rust will result in the error being "consumed".
This commit is contained in:
parent
8e3a9f0e84
commit
802d5467c1
|
@ -1,6 +1,5 @@
|
||||||
use std::collections::{HashMap, BTreeMap};
|
use std::collections::{HashMap, BTreeMap};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use error_chain::ChainedError;
|
|
||||||
|
|
||||||
use error::*;
|
use error::*;
|
||||||
use lua::*;
|
use lua::*;
|
||||||
|
@ -264,15 +263,3 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaUserDataType for LuaError {
|
|
||||||
fn add_methods(methods: &mut LuaUserDataMethods<Self>) {
|
|
||||||
methods.add_method("backtrace", |lua, err, _| {
|
|
||||||
lua.pack(format!("{}", err.display()))
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_meta_method(LuaMetaMethod::ToString, |lua, err, _| {
|
|
||||||
lua.pack(err.to_string())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -118,6 +118,15 @@ pub trait FromLuaMulti<'a>: Sized {
|
||||||
fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
|
fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for LuaError {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
|
unsafe {
|
||||||
|
push_error(lua.state, self);
|
||||||
|
Ok(lua.pop_value(lua.state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type LuaCallback = Box<
|
type LuaCallback = Box<
|
||||||
for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
|
for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
|
||||||
-> LuaResult<LuaMultiValue<'lua>>,
|
-> LuaResult<LuaMultiValue<'lua>>,
|
||||||
|
|
19
src/tests.rs
19
src/tests.rs
|
@ -700,12 +700,12 @@ fn test_result_conversions() {
|
||||||
let globals = lua.globals().unwrap();
|
let globals = lua.globals().unwrap();
|
||||||
|
|
||||||
let err = lua.create_function(|lua, _| {
|
let err = lua.create_function(|lua, _| {
|
||||||
lua.pack(Result::Err::<String, String>(
|
lua.pack(Result::Err::<String, LuaError>(
|
||||||
"only through failure can we succeed".to_string(),
|
"only through failure can we succeed".into(),
|
||||||
))
|
))
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
let ok = lua.create_function(|lua, _| {
|
let ok = lua.create_function(|lua, _| {
|
||||||
lua.pack(Result::Ok::<String, String>("!".to_string()))
|
lua.pack(Result::Ok::<String, LuaError>("!".to_string()))
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
globals.set("err", err).unwrap();
|
globals.set("err", err).unwrap();
|
||||||
|
@ -713,13 +713,14 @@ fn test_result_conversions() {
|
||||||
|
|
||||||
lua.exec::<()>(
|
lua.exec::<()>(
|
||||||
r#"
|
r#"
|
||||||
local err, msg = err()
|
local r, e = err()
|
||||||
assert(err == nil)
|
assert(r == nil)
|
||||||
assert(msg == "only through failure can we succeed")
|
assert(tostring(e) == "only through failure can we succeed")
|
||||||
|
assert(type(e:backtrace()) == "string")
|
||||||
|
|
||||||
local ok, extra = ok()
|
local r, e = ok()
|
||||||
assert(ok == "!")
|
assert(r == "!")
|
||||||
assert(extra == nil)
|
assert(e == nil)
|
||||||
"#,
|
"#,
|
||||||
None,
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
105
src/util.rs
105
src/util.rs
|
@ -4,6 +4,7 @@ use std::ffi::CStr;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
||||||
|
use error_chain::ChainedError;
|
||||||
|
|
||||||
use ffi;
|
use ffi;
|
||||||
use error::{LuaResult, LuaError, LuaErrorKind};
|
use error::{LuaResult, LuaError, LuaErrorKind};
|
||||||
|
@ -137,7 +138,7 @@ pub unsafe extern "C" fn destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||||
}) {
|
}) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
push_error(state, WrappedError::Panic(p));
|
push_panic(state, p);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,16 +155,26 @@ where
|
||||||
match catch_unwind(f) {
|
match catch_unwind(f) {
|
||||||
Ok(Ok(r)) => r,
|
Ok(Ok(r)) => r,
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
push_error(state, WrappedError::Error(err));
|
push_error(state, err);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
push_error(state, WrappedError::Panic(p));
|
push_panic(state, p);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pushes a WrappedError::Error to the top of the stack
|
||||||
|
pub unsafe fn push_error(state: *mut ffi::lua_State, err: LuaError) {
|
||||||
|
push_wrapped_error(state, WrappedError::Error(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes a WrappedError::Panic to the top of the stack
|
||||||
|
pub unsafe fn push_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
||||||
|
push_wrapped_error(state, WrappedError::Panic(panic));
|
||||||
|
}
|
||||||
|
|
||||||
// Pops a WrappedError off of the top of the stack, if it is a WrappedError::Error, returns it, if
|
// Pops a WrappedError off of the top of the stack, if it is a WrappedError::Error, returns it, if
|
||||||
// it is a WrappedError::Panic, clears the current stack and panics.
|
// it is a WrappedError::Panic, clears the current stack and panics.
|
||||||
pub unsafe fn pop_error(state: *mut ffi::lua_State) -> LuaError {
|
pub unsafe fn pop_error(state: *mut ffi::lua_State) -> LuaError {
|
||||||
|
@ -179,7 +190,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State) -> LuaError {
|
||||||
let userdata = ffi::lua_touserdata(state, -1);
|
let userdata = ffi::lua_touserdata(state, -1);
|
||||||
let err = (*(userdata as *mut Option<WrappedError>))
|
let err = (*(userdata as *mut Option<WrappedError>))
|
||||||
.take()
|
.take()
|
||||||
.unwrap_or_else(|| WrappedError::Error("expired error".into()));
|
.unwrap_or_else(|| WrappedError::Error("consumed error".into()));
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
match err {
|
match err {
|
||||||
|
@ -226,10 +237,7 @@ pub unsafe fn pcall_with_traceback(
|
||||||
.to_owned();
|
.to_owned();
|
||||||
push_error(
|
push_error(
|
||||||
state,
|
state,
|
||||||
WrappedError::Error(LuaError::with_chain(
|
LuaError::with_chain(error, LuaErrorKind::CallbackError(traceback)),
|
||||||
error,
|
|
||||||
LuaErrorKind::CallbackError(traceback),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -268,10 +276,7 @@ pub unsafe fn resume_with_traceback(
|
||||||
.to_owned();
|
.to_owned();
|
||||||
push_error(
|
push_error(
|
||||||
from,
|
from,
|
||||||
WrappedError::Error(LuaError::with_chain(
|
LuaError::with_chain(error, LuaErrorKind::CallbackError(traceback)),
|
||||||
error,
|
|
||||||
LuaErrorKind::CallbackError(traceback),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,8 +344,67 @@ enum WrappedError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes the given error or panic as a wrapped error onto the stack
|
// Pushes the given error or panic as a wrapped error onto the stack
|
||||||
unsafe fn push_error(state: *mut ffi::lua_State, err: WrappedError) {
|
unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||||
ffi::luaL_checkstack(state, 6, ptr::null());
|
// Wrapped errors have a __tostring metamethod and a 'backtrace' normal
|
||||||
|
// method.
|
||||||
|
|
||||||
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
callback_error(state, || {
|
||||||
|
if !is_wrapped_error(state, -1) {
|
||||||
|
return Err("not wrapped error in error method".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let userdata = ffi::lua_touserdata(state, -1);
|
||||||
|
match (*(userdata as *mut Option<WrappedError>)).as_ref() {
|
||||||
|
Some(&WrappedError::Error(ref error)) => {
|
||||||
|
push_string(state, &error.to_string());
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
Some(&WrappedError::Panic(_)) => {
|
||||||
|
// This should be impossible, there should be no way for lua
|
||||||
|
// to catch a panic error.
|
||||||
|
push_string(state, "panic error");
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
push_string(state, "consumed error");
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn error_backtrace(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
callback_error(state, || {
|
||||||
|
if !is_wrapped_error(state, -1) {
|
||||||
|
return Err("not wrapped error in error method".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let userdata = ffi::lua_touserdata(state, -1);
|
||||||
|
match (*(userdata as *mut Option<WrappedError>)).as_ref() {
|
||||||
|
Some(&WrappedError::Error(ref error)) => {
|
||||||
|
push_string(state, &error.display().to_string());
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
Some(&WrappedError::Panic(_)) => {
|
||||||
|
// This should be impossible, there should be no way for lua
|
||||||
|
// to catch a panic error.
|
||||||
|
push_string(state, "panic error");
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
push_string(state, "consumed error");
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
let err_userdata = ffi::lua_newuserdata(state, mem::size_of::<Option<WrappedError>>()) as
|
let err_userdata = ffi::lua_newuserdata(state, mem::size_of::<Option<WrappedError>>()) as
|
||||||
*mut Option<WrappedError>;
|
*mut Option<WrappedError>;
|
||||||
|
@ -351,6 +415,8 @@ unsafe fn push_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||||
if ffi::lua_isnil(state, -1) != 0 {
|
if ffi::lua_isnil(state, -1) != 0 {
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
|
ffi::luaL_checkstack(state, 7, ptr::null());
|
||||||
|
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
|
@ -362,6 +428,17 @@ unsafe fn push_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||||
ffi::lua_pushcfunction(state, destructor::<Option<WrappedError>>);
|
ffi::lua_pushcfunction(state, destructor::<Option<WrappedError>>);
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
|
push_string(state, "__tostring");
|
||||||
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
|
push_string(state, "__index");
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
push_string(state, "backtrace");
|
||||||
|
ffi::lua_pushcfunction(state, error_backtrace);
|
||||||
|
ffi::lua_settable(state, -3);
|
||||||
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
push_string(state, "__metatable");
|
push_string(state, "__metatable");
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
Loading…
Reference in New Issue