Not sure I like everything about this approach yet
This commit is contained in:
parent
a609f709ee
commit
2c439f8097
|
@ -63,24 +63,6 @@ impl<'lua> FromLua<'lua> for LuaFunction<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for LuaUserData<'lua> {
|
|
||||||
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
|
||||||
Ok(LuaValue::UserData(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for LuaUserData<'lua> {
|
|
||||||
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaUserData<'lua>> {
|
|
||||||
match value {
|
|
||||||
LuaValue::UserData(ud) => Ok(ud),
|
|
||||||
_ => Err(
|
|
||||||
LuaConversionError::FromLua("cannot convert lua value to userdata".to_owned())
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for LuaThread<'lua> {
|
impl<'lua> ToLua<'lua> for LuaThread<'lua> {
|
||||||
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
Ok(LuaValue::Thread(self))
|
Ok(LuaValue::Thread(self))
|
||||||
|
@ -99,6 +81,24 @@ impl<'lua> FromLua<'lua> for LuaThread<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for LuaUserData<'lua> {
|
||||||
|
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
|
Ok(LuaValue::UserData(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for LuaUserData<'lua> {
|
||||||
|
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaUserData<'lua>> {
|
||||||
|
match value {
|
||||||
|
LuaValue::UserData(ud) => Ok(ud),
|
||||||
|
_ => Err(
|
||||||
|
LuaConversionError::FromLua("cannot convert lua value to userdata".to_owned())
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua, T: LuaUserDataType> ToLua<'lua> for T {
|
impl<'lua, T: LuaUserDataType> ToLua<'lua> for T {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
lua.create_userdata(self).map(LuaValue::UserData)
|
lua.create_userdata(self).map(LuaValue::UserData)
|
||||||
|
@ -117,6 +117,29 @@ impl<'lua, T: LuaUserDataType + Copy> FromLua<'lua> for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for LuaErrorUserData<'lua> {
|
||||||
|
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
|
Ok(LuaValue::Error(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for LuaErrorUserData<'lua> {
|
||||||
|
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaErrorUserData<'lua>> {
|
||||||
|
match value {
|
||||||
|
LuaValue::Error(err) => Ok(err),
|
||||||
|
_ => Err(
|
||||||
|
LuaConversionError::FromLua("cannot convert lua value to error".to_owned()).into(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for LuaError {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
|
Ok(LuaValue::Error(lua.create_error(self)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for bool {
|
impl<'lua> ToLua<'lua> for bool {
|
||||||
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
|
||||||
Ok(LuaValue::Boolean(self))
|
Ok(LuaValue::Boolean(self))
|
||||||
|
|
83
src/lua.rs
83
src/lua.rs
|
@ -38,10 +38,14 @@ pub enum LuaValue<'lua> {
|
||||||
Table(LuaTable<'lua>),
|
Table(LuaTable<'lua>),
|
||||||
/// Reference to a Lua function (or closure).
|
/// Reference to a Lua function (or closure).
|
||||||
Function(LuaFunction<'lua>),
|
Function(LuaFunction<'lua>),
|
||||||
/// Reference to a "full" userdata object.
|
|
||||||
UserData(LuaUserData<'lua>),
|
|
||||||
/// Reference to a Lua thread (or coroutine).
|
/// Reference to a Lua thread (or coroutine).
|
||||||
Thread(LuaThread<'lua>),
|
Thread(LuaThread<'lua>),
|
||||||
|
/// Reference to a userdata object that holds a custom type which implements
|
||||||
|
/// `LuaUserDataType`. Special builtin userdata types will be represented as
|
||||||
|
/// other `LuaValue` variants.
|
||||||
|
UserData(LuaUserData<'lua>),
|
||||||
|
/// `LuaError` is a special builtin userdata type.
|
||||||
|
Error(LuaErrorUserData<'lua>),
|
||||||
}
|
}
|
||||||
pub use self::LuaValue::Nil as LuaNil;
|
pub use self::LuaValue::Nil as LuaNil;
|
||||||
|
|
||||||
|
@ -122,15 +126,6 @@ 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_wrapped_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>>,
|
||||||
|
@ -889,16 +884,20 @@ pub trait LuaUserDataType: 'static + Sized {
|
||||||
fn add_methods(_methods: &mut LuaUserDataMethods<Self>) {}
|
fn add_methods(_methods: &mut LuaUserDataMethods<Self>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle to an internal instance of custom userdata. All userdata in this API
|
/// Handle to an internal Lua userdata for a type that implements
|
||||||
/// is based around `RefCell`, to best match the mutable semantics of the Lua
|
/// LuaUserDataType. Internally, instances are stored in a `RefCell`, to best
|
||||||
/// language.
|
/// match the mutable semantics of the Lua language.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct LuaUserData<'lua>(LuaRef<'lua>);
|
pub struct LuaUserData<'lua>(LuaRef<'lua>);
|
||||||
|
|
||||||
impl<'lua> LuaUserData<'lua> {
|
impl<'lua> LuaUserData<'lua> {
|
||||||
/// Checks whether `T` is the type of this userdata.
|
/// Checks whether `T` is the type of this userdata.
|
||||||
pub fn is<T: LuaUserDataType>(&self) -> bool {
|
pub fn is<T: LuaUserDataType>(&self) -> LuaResult<bool> {
|
||||||
self.inspect(|_: &RefCell<T>| Ok(())).is_ok()
|
match self.inspect(|_: &RefCell<T>| Ok(())) {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(LuaError::UserDataError(LuaUserDataError::TypeMismatch)) => Ok(false),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow this userdata out of the internal RefCell that is held in lua.
|
/// Borrow this userdata out of the internal RefCell that is held in lua.
|
||||||
|
@ -910,7 +909,8 @@ impl<'lua> LuaUserData<'lua> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow mutably this userdata out of the internal RefCell that is held in lua.
|
/// Borrow mutably this userdata out of the internal RefCell that is held in
|
||||||
|
/// lua.
|
||||||
pub fn borrow_mut<T: LuaUserDataType>(&self) -> LuaResult<RefMut<T>> {
|
pub fn borrow_mut<T: LuaUserDataType>(&self) -> LuaResult<RefMut<T>> {
|
||||||
self.inspect(|cell| {
|
self.inspect(|cell| {
|
||||||
Ok(cell.try_borrow_mut().map_err(
|
Ok(cell.try_borrow_mut().map_err(
|
||||||
|
@ -955,6 +955,27 @@ impl<'lua> LuaUserData<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle to a `LuaError` that is held internally in Lua
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LuaErrorUserData<'lua>(LuaRef<'lua>);
|
||||||
|
|
||||||
|
impl<'lua> LuaErrorUserData<'lua> {
|
||||||
|
/// Gets a reference to the internally held `LuaError`.
|
||||||
|
pub fn get<T: LuaUserDataType>(&self) -> LuaResult<&LuaError> {
|
||||||
|
unsafe {
|
||||||
|
let lua = self.0.lua;
|
||||||
|
stack_guard(lua.state, 0, move || {
|
||||||
|
check_stack(lua.state, 1)?;
|
||||||
|
lua.push_ref(lua.state, &self.0);
|
||||||
|
|
||||||
|
let userdata = ffi::lua_touserdata(lua.state, -1);
|
||||||
|
let err = &*(userdata as *const WrappedError);
|
||||||
|
Ok(&err.0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Top level Lua struct which holds the Lua state itself.
|
/// Top level Lua struct which holds the Lua state itself.
|
||||||
pub struct Lua {
|
pub struct Lua {
|
||||||
state: *mut ffi::lua_State,
|
state: *mut ffi::lua_State,
|
||||||
|
@ -1236,6 +1257,14 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a userdata object from a LuaError
|
||||||
|
pub fn create_error(&self, err: LuaError) -> LuaResult<LuaErrorUserData> {
|
||||||
|
unsafe {
|
||||||
|
push_wrapped_error(self.state, err);
|
||||||
|
Ok(LuaErrorUserData(self.pop_ref(self.state)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to the global environment.
|
/// Returns a handle to the global environment.
|
||||||
pub fn globals(&self) -> LuaResult<LuaTable> {
|
pub fn globals(&self) -> LuaResult<LuaTable> {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1436,12 +1465,16 @@ impl Lua {
|
||||||
self.push_ref(state, &f.0);
|
self.push_ref(state, &f.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LuaValue::Thread(t) => {
|
||||||
|
self.push_ref(state, &t.0);
|
||||||
|
}
|
||||||
|
|
||||||
LuaValue::UserData(ud) => {
|
LuaValue::UserData(ud) => {
|
||||||
self.push_ref(state, &ud.0);
|
self.push_ref(state, &ud.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaValue::Thread(t) => {
|
LuaValue::Error(e) => {
|
||||||
self.push_ref(state, &t.0);
|
self.push_ref(state, &e.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1483,7 +1516,17 @@ impl Lua {
|
||||||
|
|
||||||
ffi::LUA_TFUNCTION => LuaValue::Function(LuaFunction(self.pop_ref(state))),
|
ffi::LUA_TFUNCTION => LuaValue::Function(LuaFunction(self.pop_ref(state))),
|
||||||
|
|
||||||
ffi::LUA_TUSERDATA => LuaValue::UserData(LuaUserData(self.pop_ref(state))),
|
ffi::LUA_TUSERDATA => {
|
||||||
|
// It should not be possible to interact with userdata types
|
||||||
|
// other than custom LuaUserDataType types OR a WrappedError.
|
||||||
|
// WrappedPanic should never be able to be caught in lua, so it
|
||||||
|
// should never be here.
|
||||||
|
if is_wrapped_error(state, -1) {
|
||||||
|
LuaValue::Error(LuaErrorUserData(self.pop_ref(state)))
|
||||||
|
} else {
|
||||||
|
LuaValue::UserData(LuaUserData(self.pop_ref(state)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ffi::LUA_TTHREAD => LuaValue::Thread(LuaThread(self.pop_ref(state))),
|
ffi::LUA_TTHREAD => LuaValue::Thread(LuaThread(self.pop_ref(state))),
|
||||||
|
|
||||||
|
|
|
@ -244,10 +244,10 @@ fn test_user_data() {
|
||||||
let userdata1 = lua.create_userdata(UserData1(1)).unwrap();
|
let userdata1 = lua.create_userdata(UserData1(1)).unwrap();
|
||||||
let userdata2 = lua.create_userdata(UserData2(Box::new(2))).unwrap();
|
let userdata2 = lua.create_userdata(UserData2(Box::new(2))).unwrap();
|
||||||
|
|
||||||
assert!(userdata1.is::<UserData1>());
|
assert!(userdata1.is::<UserData1>().unwrap());
|
||||||
assert!(!userdata1.is::<UserData2>());
|
assert!(!userdata1.is::<UserData2>().unwrap());
|
||||||
assert!(userdata2.is::<UserData2>());
|
assert!(userdata2.is::<UserData2>().unwrap());
|
||||||
assert!(!userdata2.is::<UserData1>());
|
assert!(!userdata2.is::<UserData1>().unwrap());
|
||||||
|
|
||||||
assert_eq!(userdata1.borrow::<UserData1>().unwrap().0, 1);
|
assert_eq!(userdata1.borrow::<UserData1>().unwrap().0, 1);
|
||||||
assert_eq!(*userdata2.borrow::<UserData2>().unwrap().0, 2);
|
assert_eq!(*userdata2.borrow::<UserData2>().unwrap().0, 2);
|
||||||
|
|
12
src/util.rs
12
src/util.rs
|
@ -121,8 +121,8 @@ pub unsafe fn handle_error(state: *mut ffi::lua_State, err: c_int) -> LuaResult<
|
||||||
if err == ffi::LUA_OK || err == ffi::LUA_YIELD {
|
if err == ffi::LUA_OK || err == ffi::LUA_YIELD {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
if is_wrapped_error(state, -1) {
|
if let Some(err) = pop_wrapped_error(state) {
|
||||||
Err(pop_wrapped_error(state).unwrap())
|
Err(err)
|
||||||
|
|
||||||
} else if is_wrapped_panic(state, -1) {
|
} else if is_wrapped_panic(state, -1) {
|
||||||
let userdata = ffi::lua_touserdata(state, -1);
|
let userdata = ffi::lua_touserdata(state, -1);
|
||||||
|
@ -200,7 +200,7 @@ pub unsafe extern "C" fn destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
||||||
// In the context of a lua callback, this will call the given function and if the given function
|
// In the context of a lua callback, this will call the given function and if the given function
|
||||||
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
||||||
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
// longjmp). The error or panic is wrapped in such a way that when calling handle_error back on
|
||||||
// the rust side, it will resume the panic.
|
// the rust side, it will resume the panic.
|
||||||
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
||||||
where
|
where
|
||||||
|
@ -228,8 +228,7 @@ pub unsafe fn pcall_with_traceback(
|
||||||
nresults: c_int,
|
nresults: c_int,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
unsafe extern "C" fn message_handler(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn message_handler(state: *mut ffi::lua_State) -> c_int {
|
||||||
if is_wrapped_error(state, 1) {
|
if let Some(error) = pop_wrapped_error(state) {
|
||||||
let error = pop_wrapped_error(state).unwrap();
|
|
||||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut()))
|
let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut()))
|
||||||
.to_str()
|
.to_str()
|
||||||
|
@ -262,8 +261,7 @@ pub unsafe fn resume_with_traceback(
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
let res = ffi::lua_resume(state, from, nargs);
|
let res = ffi::lua_resume(state, from, nargs);
|
||||||
if res != ffi::LUA_OK && res != ffi::LUA_YIELD {
|
if res != ffi::LUA_OK && res != ffi::LUA_YIELD {
|
||||||
if is_wrapped_error(state, 1) {
|
if let Some(error) = pop_wrapped_error(state) {
|
||||||
let error = pop_wrapped_error(state).unwrap();
|
|
||||||
ffi::luaL_traceback(from, state, ptr::null(), 0);
|
ffi::luaL_traceback(from, state, ptr::null(), 0);
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tolstring(from, -1, ptr::null_mut()))
|
let traceback = CStr::from_ptr(ffi::lua_tolstring(from, -1, ptr::null_mut()))
|
||||||
.to_str()
|
.to_str()
|
||||||
|
|
Loading…
Reference in New Issue