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::hash::Hash;
|
||||
use error_chain::ChainedError;
|
||||
|
||||
use error::*;
|
||||
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>;
|
||||
}
|
||||
|
||||
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<
|
||||
for<'lua> FnMut(&'lua Lua, 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 err = lua.create_function(|lua, _| {
|
||||
lua.pack(Result::Err::<String, String>(
|
||||
"only through failure can we succeed".to_string(),
|
||||
lua.pack(Result::Err::<String, LuaError>(
|
||||
"only through failure can we succeed".into(),
|
||||
))
|
||||
}).unwrap();
|
||||
let ok = lua.create_function(|lua, _| {
|
||||
lua.pack(Result::Ok::<String, String>("!".to_string()))
|
||||
lua.pack(Result::Ok::<String, LuaError>("!".to_string()))
|
||||
}).unwrap();
|
||||
|
||||
globals.set("err", err).unwrap();
|
||||
|
@ -713,13 +713,14 @@ fn test_result_conversions() {
|
|||
|
||||
lua.exec::<()>(
|
||||
r#"
|
||||
local err, msg = err()
|
||||
assert(err == nil)
|
||||
assert(msg == "only through failure can we succeed")
|
||||
local r, e = err()
|
||||
assert(r == nil)
|
||||
assert(tostring(e) == "only through failure can we succeed")
|
||||
assert(type(e:backtrace()) == "string")
|
||||
|
||||
local ok, extra = ok()
|
||||
assert(ok == "!")
|
||||
assert(extra == nil)
|
||||
local r, e = ok()
|
||||
assert(r == "!")
|
||||
assert(e == nil)
|
||||
"#,
|
||||
None,
|
||||
).unwrap();
|
||||
|
|
105
src/util.rs
105
src/util.rs
|
@ -4,6 +4,7 @@ use std::ffi::CStr;
|
|||
use std::any::Any;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
||||
use error_chain::ChainedError;
|
||||
|
||||
use ffi;
|
||||
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,
|
||||
Err(p) => {
|
||||
push_error(state, WrappedError::Panic(p));
|
||||
push_panic(state, p);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
|
@ -154,16 +155,26 @@ where
|
|||
match catch_unwind(f) {
|
||||
Ok(Ok(r)) => r,
|
||||
Ok(Err(err)) => {
|
||||
push_error(state, WrappedError::Error(err));
|
||||
push_error(state, err);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
push_error(state, WrappedError::Panic(p));
|
||||
push_panic(state, p);
|
||||
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
|
||||
// it is a WrappedError::Panic, clears the current stack and panics.
|
||||
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 err = (*(userdata as *mut Option<WrappedError>))
|
||||
.take()
|
||||
.unwrap_or_else(|| WrappedError::Error("expired error".into()));
|
||||
.unwrap_or_else(|| WrappedError::Error("consumed error".into()));
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err {
|
||||
|
@ -226,10 +237,7 @@ pub unsafe fn pcall_with_traceback(
|
|||
.to_owned();
|
||||
push_error(
|
||||
state,
|
||||
WrappedError::Error(LuaError::with_chain(
|
||||
error,
|
||||
LuaErrorKind::CallbackError(traceback),
|
||||
)),
|
||||
LuaError::with_chain(error, LuaErrorKind::CallbackError(traceback)),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -268,10 +276,7 @@ pub unsafe fn resume_with_traceback(
|
|||
.to_owned();
|
||||
push_error(
|
||||
from,
|
||||
WrappedError::Error(LuaError::with_chain(
|
||||
error,
|
||||
LuaErrorKind::CallbackError(traceback),
|
||||
)),
|
||||
LuaError::with_chain(error, LuaErrorKind::CallbackError(traceback)),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -339,8 +344,67 @@ enum WrappedError {
|
|||
}
|
||||
|
||||
// Pushes the given error or panic as a wrapped error onto the stack
|
||||
unsafe fn push_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||
ffi::luaL_checkstack(state, 6, ptr::null());
|
||||
unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||
// 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
|
||||
*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 {
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
ffi::luaL_checkstack(state, 7, ptr::null());
|
||||
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_pushlightuserdata(
|
||||
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_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");
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_settable(state, -3);
|
||||
|
|
Loading…
Reference in New Issue